diff options
Diffstat (limited to 'vendor/gipfl/zfdb/src')
41 files changed, 12383 insertions, 0 deletions
diff --git a/vendor/gipfl/zfdb/src/Adapter/Adapter.php b/vendor/gipfl/zfdb/src/Adapter/Adapter.php new file mode 100644 index 0000000..a3cad5d --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Adapter.php @@ -0,0 +1,1267 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter; + +use gipfl\ZfDb\Adapter\Exception\AdapterException; +use gipfl\ZfDb\Db; +use gipfl\ZfDb\Expr; +use gipfl\ZfDb\Profiler; +use gipfl\ZfDb\Profiler\ProfilerException; +use gipfl\ZfDb\Select; +use gipfl\ZfDb\Statement; +use gipfl\ZfDb\Statement\StatementInterface; + +/** + * @see Zend_Db + */ + +/** + * @see Select + */ + +/** + * Class for connecting to SQL databases and performing common operations. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +abstract class Adapter +{ + + /** + * User-provided configuration + * + * @var array + */ + protected $_config = array(); + + /** + * Fetch mode + * + * @var integer + */ + protected $_fetchMode = Db::FETCH_ASSOC; + + /** + * Query profiler object, of type Zend_Db_Profiler + * or a subclass of that. + * + * @var Profiler + */ + protected $_profiler; + + /** + * Default class name for a DB statement. + * + * @var string + */ + protected $_defaultStmtClass = Statement::class; + + /** + * Default class name for the profiler object. + * + * @var string + */ + protected $_defaultProfilerClass = Profiler::class; + + /** + * Database connection + * + * @var object|resource|null + */ + protected $_connection = null; + + /** + * Specifies the case of column names retrieved in queries + * Options + * Zend_Db::CASE_NATURAL (default) + * Zend_Db::CASE_LOWER + * Zend_Db::CASE_UPPER + * + * @var integer + */ + protected $_caseFolding = Db::CASE_NATURAL; + + /** + * Specifies whether the adapter automatically quotes identifiers. + * If true, most SQL generated by Zend_Db classes applies + * identifier quoting automatically. + * If false, developer must quote identifiers themselves + * by calling quoteIdentifier(). + * + * @var bool + */ + protected $_autoQuoteIdentifiers = true; + + /** + * Keys are UPPERCASE SQL datatypes or the constants + * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE. + * + * Values are: + * 0 = 32-bit integer + * 1 = 64-bit integer + * 2 = float or decimal + * + * @var array Associative array of datatypes to values 0, 1, or 2. + */ + protected $_numericDataTypes = array( + Db::INT_TYPE => Db::INT_TYPE, + Db::BIGINT_TYPE => Db::BIGINT_TYPE, + Db::FLOAT_TYPE => Db::FLOAT_TYPE + ); + + /** Weither or not that object can get serialized + * + * @var bool + */ + protected $_allowSerialization = true; + + /** + * Weither or not the database should be reconnected + * to that adapter when waking up + * + * @var bool + */ + protected $_autoReconnectOnUnserialize = false; + + /** + * Constructor. + * + * $config is an array of key/value pairs or an instance of Zend_Config + * containing configuration options. These options are common to most adapters: + * + * dbname => (string) The name of the database to user + * username => (string) Connect to the database as this username. + * password => (string) Password associated with the username. + * host => (string) What host to connect to, defaults to localhost + * + * Some options are used on a case-by-case basis by adapters: + * + * port => (string) The port of the database + * persistent => (boolean) Whether to use a persistent connection or not, defaults to false + * protocol => (string) The network protocol, defaults to TCPIP + * caseFolding => (int) style of case-alteration used for identifiers + * socket => (string) The socket or named pipe that should be used + * + * @param array|Zend_Config $config An array or instance of Zend_Config having configuration data + * @throws AdapterException + */ + public function __construct($config) + { + /* + * Verify that adapter parameters are in an array. + */ + if (!is_array($config)) { + /* + * Convert Zend_Config argument to a plain array. + */ + if ($config instanceof Zend_Config) { + $config = $config->toArray(); + } else { + /** + * @see AdapterException + */ + throw new AdapterException('Adapter parameters must be in an array or a Zend_Config object'); + } + } + + $this->_checkRequiredOptions($config); + + $options = array( + Db::CASE_FOLDING => $this->_caseFolding, + Db::AUTO_QUOTE_IDENTIFIERS => $this->_autoQuoteIdentifiers, + Db::FETCH_MODE => $this->_fetchMode, + ); + $driverOptions = array(); + + /* + * normalize the config and merge it with the defaults + */ + if (array_key_exists('options', $config)) { + // can't use array_merge() because keys might be integers + foreach ((array) $config['options'] as $key => $value) { + $options[$key] = $value; + } + } + if (array_key_exists('driver_options', $config)) { + if (!empty($config['driver_options'])) { + // can't use array_merge() because keys might be integers + foreach ((array) $config['driver_options'] as $key => $value) { + $driverOptions[$key] = $value; + } + } + } + + if (!isset($config['charset'])) { + $config['charset'] = null; + } + + if (!isset($config['persistent'])) { + $config['persistent'] = false; + } + + $this->_config = array_merge($this->_config, $config); + $this->_config['options'] = $options; + $this->_config['driver_options'] = $driverOptions; + + + // obtain the case setting, if there is one + if (array_key_exists(Db::CASE_FOLDING, $options)) { + $case = (int) $options[Db::CASE_FOLDING]; + switch ($case) { + case Db::CASE_LOWER: + case Db::CASE_UPPER: + case Db::CASE_NATURAL: + $this->_caseFolding = $case; + break; + default: + /** @see AdapterException */ + throw new AdapterException('Case must be one of the following constants: ' + . 'Zend_Db::CASE_NATURAL, Zend_Db::CASE_LOWER, Zend_Db::CASE_UPPER'); + } + } + + if (array_key_exists(Db::FETCH_MODE, $options)) { + if (is_string($options[Db::FETCH_MODE])) { + $constant = 'Db::FETCH_' . strtoupper($options[Db::FETCH_MODE]); + if (defined($constant)) { + $options[Db::FETCH_MODE] = constant($constant); + } + } + $this->setFetchMode((int) $options[Db::FETCH_MODE]); + } + + // obtain quoting property if there is one + if (array_key_exists(Db::AUTO_QUOTE_IDENTIFIERS, $options)) { + $this->_autoQuoteIdentifiers = (bool) $options[Db::AUTO_QUOTE_IDENTIFIERS]; + } + + // obtain allow serialization property if there is one + if (array_key_exists(Db::ALLOW_SERIALIZATION, $options)) { + $this->_allowSerialization = (bool) $options[Db::ALLOW_SERIALIZATION]; + } + + // obtain auto reconnect on unserialize property if there is one + if (array_key_exists(Db::AUTO_RECONNECT_ON_UNSERIALIZE, $options)) { + $this->_autoReconnectOnUnserialize = (bool) $options[Db::AUTO_RECONNECT_ON_UNSERIALIZE]; + } + + // create a profiler object + $profiler = false; + if (array_key_exists(Db::PROFILER, $this->_config)) { + $profiler = $this->_config[Db::PROFILER]; + unset($this->_config[Db::PROFILER]); + } + $this->setProfiler($profiler); + } + + /** + * Check for config options that are mandatory. + * Throw exceptions if any are missing. + * + * @param array $config + * @throws AdapterException + */ + protected function _checkRequiredOptions(array $config) + { + // we need at least a dbname + if (! array_key_exists('dbname', $config)) { + /** @see AdapterException */ + throw new AdapterException( + "Configuration array must have a key for 'dbname' that names the database instance" + ); + } + + if (! array_key_exists('password', $config)) { + /** + * @see AdapterException + */ + throw new AdapterException("Configuration array must have a key for 'password' for login credentials"); + } + + if (! array_key_exists('username', $config)) { + /** + * @see AdapterException + */ + throw new AdapterException("Configuration array must have a key for 'username' for login credentials"); + } + } + + /** + * Returns the underlying database connection object or resource. + * If not presently connected, this initiates the connection. + * + * @return object|resource|null + */ + public function getConnection() + { + $this->_connect(); + return $this->_connection; + } + + /** + * Returns the configuration variables in this adapter. + * + * @return array + */ + public function getConfig() + { + return $this->_config; + } + + /** + * Set the adapter's profiler object. + * + * The argument may be a boolean, an associative array, an instance of + * Zend_Db_Profiler, or an instance of Zend_Config. + * + * A boolean argument sets the profiler to enabled if true, or disabled if + * false. The profiler class is the adapter's default profiler class, + * Zend_Db_Profiler. + * + * An instance of Zend_Db_Profiler sets the adapter's instance to that + * object. The profiler is enabled and disabled separately. + * + * An associative array argument may contain any of the keys 'enabled', + * 'class', and 'instance'. The 'enabled' and 'instance' keys correspond to the + * boolean and object types documented above. The 'class' key is used to name a + * class to use for a custom profiler. The class must be Zend_Db_Profiler or a + * subclass. The class is instantiated with no constructor arguments. The 'class' + * option is ignored when the 'instance' option is supplied. + * + * An object of type Zend_Config may contain the properties 'enabled', 'class', and + * 'instance', just as if an associative array had been passed instead. + * + * @param Profiler|Zend_Config|array|boolean $profiler + * @return Adapter Provides a fluent interface + * @throws ProfilerException if the object instance or class specified + * is not Zend_Db_Profiler or an extension of that class. + */ + public function setProfiler($profiler) + { + $enabled = null; + $profilerClass = $this->_defaultProfilerClass; + $profilerInstance = null; + + if ($profilerIsObject = is_object($profiler)) { + if ($profiler instanceof Profiler) { + $profilerInstance = $profiler; + } elseif ($profiler instanceof Zend_Config) { + $profiler = $profiler->toArray(); + } else { + throw new ProfilerException('Profiler argument must be an instance of either Profiler' + . ' or Zend_Config when provided as an object'); + } + } + + if (is_array($profiler)) { + if (isset($profiler['enabled'])) { + $enabled = (bool) $profiler['enabled']; + } + if (isset($profiler['class'])) { + $profilerClass = $profiler['class']; + } + if (isset($profiler['instance'])) { + $profilerInstance = $profiler['instance']; + } + } elseif (!$profilerIsObject) { + $enabled = (bool) $profiler; + } + + if ($profilerInstance === null) { + $profilerInstance = new $profilerClass(); + } + + if (!$profilerInstance instanceof Profiler) { + throw new ProfilerException('Class ' . get_class($profilerInstance) . ' does not extend ' + . 'Zend_Db_Profiler'); + } + + if (null !== $enabled) { + $profilerInstance->setEnabled($enabled); + } + + $this->_profiler = $profilerInstance; + + return $this; + } + + + /** + * Returns the profiler for this adapter. + * + * @return Profiler + */ + public function getProfiler() + { + return $this->_profiler; + } + + /** + * Get the default statement class. + * + * @return string + */ + public function getStatementClass() + { + return $this->_defaultStmtClass; + } + + /** + * Set the default statement class. + * + * @return Adapter Fluent interface + */ + public function setStatementClass($class) + { + $this->_defaultStmtClass = $class; + return $this; + } + + /** + * Prepares and executes an SQL statement with bound data. + * + * @param mixed $sql The SQL statement with placeholders. + * May be a string or Zend_Db_Select. + * @param mixed $bind An array of data to bind to the placeholders. + * @return StatementInterface + */ + public function query($sql, $bind = array()) + { + // connect to the database if needed + $this->_connect(); + + // is the $sql a Zend_Db_Select object? + if ($sql instanceof Select) { + if (empty($bind)) { + $bind = $sql->getBind(); + } + + $sql = $sql->assemble(); + } + + // make sure $bind to an array; + // don't use (array) typecasting because + // because $bind may be a Zend_Db_Expr object + if (!is_array($bind)) { + $bind = array($bind); + } + + // prepare and execute the statement with profiling + $stmt = $this->prepare($sql); + $stmt->execute($bind); + + // return the results embedded in the prepared statement object + $stmt->setFetchMode($this->_fetchMode); + return $stmt; + } + + /** + * Leave autocommit mode and begin a transaction. + * + * @return Adapter + */ + public function beginTransaction() + { + $this->_connect(); + $q = $this->_profiler->queryStart('begin', Profiler::TRANSACTION); + $this->_beginTransaction(); + $this->_profiler->queryEnd($q); + return $this; + } + + /** + * Commit a transaction and return to autocommit mode. + * + * @return Adapter + */ + public function commit() + { + $this->_connect(); + $q = $this->_profiler->queryStart('commit', Profiler::TRANSACTION); + $this->_commit(); + $this->_profiler->queryEnd($q); + return $this; + } + + /** + * Roll back a transaction and return to autocommit mode. + * + * @return Adapter + */ + public function rollBack() + { + $this->_connect(); + $q = $this->_profiler->queryStart('rollback', Profiler::TRANSACTION); + $this->_rollBack(); + $this->_profiler->queryEnd($q); + return $this; + } + + /** + * Inserts a table row with specified data. + * + * @param mixed $table The table to insert data into. + * @param array $bind Column-value pairs. + * @return int The number of affected rows. + * @throws AdapterException|\gipfl\ZfDb\Statement\Exception\StatementException + */ + public function insert($table, array $bind) + { + // extract and quote col names from the array keys + $cols = array(); + $vals = array(); + $i = 0; + foreach ($bind as $col => $val) { + $cols[] = $this->quoteIdentifier($col, true); + if ($val instanceof Expr) { + $vals[] = $val->__toString(); + unset($bind[$col]); + } else { + if ($this->supportsParameters('positional')) { + $vals[] = '?'; + } else { + if ($this->supportsParameters('named')) { + unset($bind[$col]); + $bind[':col'.$i] = $val; + $vals[] = ':col'.$i; + $i++; + } else { + throw new AdapterException(get_class($this) ." doesn't support positional or named binding"); + } + } + } + } + + // build the statement + $sql = "INSERT INTO " + . $this->quoteIdentifier($table, true) + . ' (' . implode(', ', $cols) . ') ' + . 'VALUES (' . implode(', ', $vals) . ')'; + + // execute the statement and return the number of affected rows + if ($this->supportsParameters('positional')) { + $bind = array_values($bind); + } + $stmt = $this->query($sql, $bind); + $result = $stmt->rowCount(); + return $result; + } + + /** + * Updates table rows with specified data based on a WHERE clause. + * + * @param mixed $table The table to update. + * @param array $bind Column-value pairs. + * @param mixed $where UPDATE WHERE clause(s). + * @return int The number of affected rows. + * @throws AdapterException + */ + public function update($table, array $bind, $where = '') + { + /** + * Build "col = ?" pairs for the statement, + * except for Zend_Db_Expr which is treated literally. + */ + $set = array(); + $i = 0; + foreach ($bind as $col => $val) { + if ($val instanceof Expr) { + $val = $val->__toString(); + unset($bind[$col]); + } else { + if ($this->supportsParameters('positional')) { + $val = '?'; + } else { + if ($this->supportsParameters('named')) { + unset($bind[$col]); + $bind[':col'.$i] = $val; + $val = ':col'.$i; + $i++; + } else { + /** @see AdapterException */ + throw new AdapterException(get_class($this) ." doesn't support positional or named binding"); + } + } + } + $set[] = $this->quoteIdentifier($col, true) . ' = ' . $val; + } + + $where = $this->_whereExpr($where); + + /** + * Build the UPDATE statement + */ + $sql = "UPDATE " + . $this->quoteIdentifier($table, true) + . ' SET ' . implode(', ', $set) + . (($where) ? " WHERE $where" : ''); + + /** + * Execute the statement and return the number of affected rows + */ + if ($this->supportsParameters('positional')) { + $stmt = $this->query($sql, array_values($bind)); + } else { + $stmt = $this->query($sql, $bind); + } + $result = $stmt->rowCount(); + return $result; + } + + /** + * Deletes table rows based on a WHERE clause. + * + * @param mixed $table The table to update. + * @param mixed $where DELETE WHERE clause(s). + * @return int The number of affected rows. + */ + public function delete($table, $where = '') + { + $where = $this->_whereExpr($where); + + /** + * Build the DELETE statement + */ + $sql = "DELETE FROM " + . $this->quoteIdentifier($table, true) + . (($where) ? " WHERE $where" : ''); + + /** + * Execute the statement and return the number of affected rows + */ + $stmt = $this->query($sql); + $result = $stmt->rowCount(); + return $result; + } + + /** + * Convert an array, string, or Zend_Db_Expr object + * into a string to put in a WHERE clause. + * + * @param mixed $where + * @return string + */ + protected function _whereExpr($where) + { + if (empty($where)) { + return $where; + } + if (!is_array($where)) { + $where = array($where); + } + foreach ($where as $cond => &$term) { + // is $cond an int? (i.e. Not a condition) + if (is_int($cond)) { + // $term is the full condition + if ($term instanceof Expr) { + $term = $term->__toString(); + } + } else { + // $cond is the condition with placeholder, + // and $term is quoted into the condition + $term = $this->quoteInto($cond, $term); + } + $term = '(' . $term . ')'; + } + + $where = implode(' AND ', $where); + return $where; + } + + /** + * Creates and returns a new Zend_Db_Select object for this adapter. + * + * @return Select + */ + public function select() + { + return new Select($this); + } + + /** + * Get the fetch mode. + * + * @return int + */ + public function getFetchMode() + { + return $this->_fetchMode; + } + + /** + * Fetches all SQL result rows as a sequential array. + * Uses the current fetchMode for the adapter. + * + * @param string|Select $sql An SQL SELECT statement. + * @param mixed $bind Data to bind into SELECT placeholders. + * @param mixed $fetchMode Override current fetch mode. + * @return array + */ + public function fetchAll($sql, $bind = array(), $fetchMode = null) + { + if ($fetchMode === null) { + $fetchMode = $this->_fetchMode; + } + $stmt = $this->query($sql, $bind); + $result = $stmt->fetchAll($fetchMode); + return $result; + } + + /** + * Fetches the first row of the SQL result. + * Uses the current fetchMode for the adapter. + * + * @param string|Select $sql An SQL SELECT statement. + * @param mixed $bind Data to bind into SELECT placeholders. + * @param mixed $fetchMode Override current fetch mode. + * @return mixed Array, object, or scalar depending on fetch mode. + */ + public function fetchRow($sql, $bind = array(), $fetchMode = null) + { + if ($fetchMode === null) { + $fetchMode = $this->_fetchMode; + } + $stmt = $this->query($sql, $bind); + $result = $stmt->fetch($fetchMode); + return $result; + } + + /** + * Fetches all SQL result rows as an associative array. + * + * The first column is the key, the entire row array is the + * value. You should construct the query to be sure that + * the first column contains unique values, or else + * rows with duplicate values in the first column will + * overwrite previous data. + * + * @param string|Select $sql An SQL SELECT statement. + * @param mixed $bind Data to bind into SELECT placeholders. + * @return array + */ + public function fetchAssoc($sql, $bind = array()) + { + $stmt = $this->query($sql, $bind); + $data = array(); + while ($row = $stmt->fetch(Db::FETCH_ASSOC)) { + $tmp = array_values(array_slice($row, 0, 1)); + $data[$tmp[0]] = $row; + } + return $data; + } + + /** + * Fetches the first column of all SQL result rows as an array. + * + * @param string|Select $sql An SQL SELECT statement. + * @param mixed $bind Data to bind into SELECT placeholders. + * @return array + */ + public function fetchCol($sql, $bind = array()) + { + $stmt = $this->query($sql, $bind); + $result = $stmt->fetchAll(Db::FETCH_COLUMN, 0); + return $result; + } + + /** + * Fetches all SQL result rows as an array of key-value pairs. + * + * The first column is the key, the second column is the + * value. + * + * @param string|Select $sql An SQL SELECT statement. + * @param mixed $bind Data to bind into SELECT placeholders. + * @return array + */ + public function fetchPairs($sql, $bind = array()) + { + $stmt = $this->query($sql, $bind); + $data = array(); + while ($row = $stmt->fetch(Db::FETCH_NUM)) { + $data[$row[0]] = $row[1]; + } + return $data; + } + + /** + * Fetches the first column of the first row of the SQL result. + * + * @param string|Select $sql An SQL SELECT statement. + * @param mixed $bind Data to bind into SELECT placeholders. + * @return string + */ + public function fetchOne($sql, $bind = array()) + { + $stmt = $this->query($sql, $bind); + $result = $stmt->fetchColumn(0); + return $result; + } + + /** + * Quote a raw string. + * + * @param string $value Raw string + * @return string Quoted string + */ + protected function _quote($value) + { + if (is_int($value)) { + return $value; + } elseif (is_float($value)) { + return sprintf('%F', $value); + } + return "'" . addcslashes($value, "\000\n\r\\'\"\032") . "'"; + } + + /** + * Safely quotes a value for an SQL statement. + * + * If an array is passed as the value, the array values are quoted + * and then returned as a comma-separated string. + * + * @param mixed $value The value to quote. + * @param mixed $type OPTIONAL the SQL datatype name, or constant, or null. + * @return mixed An SQL-safe quoted value (or string of separated values). + */ + public function quote($value, $type = null) + { + $this->_connect(); + + if ($value instanceof Select) { + return '(' . $value->assemble() . ')'; + } + + if ($value instanceof Expr) { + return $value->__toString(); + } + + if (is_array($value)) { + foreach ($value as &$val) { + $val = $this->quote($val, $type); + } + return implode(', ', $value); + } + + if ($type !== null && array_key_exists($type = strtoupper($type), $this->_numericDataTypes)) { + $quotedValue = '0'; + switch ($this->_numericDataTypes[$type]) { + case Db::INT_TYPE: // 32-bit integer + $quotedValue = (string) intval($value); + break; + case Db::BIGINT_TYPE: // 64-bit integer + // ANSI SQL-style hex literals (e.g. x'[\dA-F]+') + // are not supported here, because these are string + // literals, not numeric literals. + if (preg_match( + '/^( + [+-]? # optional sign + (?: + 0[Xx][\da-fA-F]+ # ODBC-style hexadecimal + |\d+ # decimal or octal, or MySQL ZEROFILL decimal + (?:[eE][+-]?\d+)? # optional exponent on decimals or octals + ) + )/x', + (string) $value, + $matches + )) { + $quotedValue = $matches[1]; + } + break; + case Db::FLOAT_TYPE: // float or decimal + $quotedValue = sprintf('%F', $value); + } + return $quotedValue; + } + + return $this->_quote($value); + } + + /** + * Quotes a value and places into a piece of text at a placeholder. + * + * The placeholder is a question-mark; all placeholders will be replaced + * with the quoted value. For example: + * + * <code> + * $text = "WHERE date < ?"; + * $date = "2005-01-02"; + * $safe = $sql->quoteInto($text, $date); + * // $safe = "WHERE date < '2005-01-02'" + * </code> + * + * @param string $text The text with a placeholder. + * @param mixed $value The value to quote. + * @param string $type OPTIONAL SQL datatype + * @param integer $count OPTIONAL count of placeholders to replace + * @return string An SQL-safe quoted value placed into the original text. + */ + public function quoteInto($text, $value, $type = null, $count = null) + { + if ($count === null) { + return str_replace('?', $this->quote($value, $type), $text); + } else { + return implode($this->quote($value, $type), explode('?', $text, $count + 1)); + } + } + + /** + * Quotes an identifier. + * + * Accepts a string representing a qualified indentifier. For Example: + * <code> + * $adapter->quoteIdentifier('myschema.mytable') + * </code> + * Returns: "myschema"."mytable" + * + * Or, an array of one or more identifiers that may form a qualified identifier: + * <code> + * $adapter->quoteIdentifier(array('myschema','my.table')) + * </code> + * Returns: "myschema"."my.table" + * + * The actual quote character surrounding the identifiers may vary depending on + * the adapter. + * + * @param string|array|Expr $ident The identifier. + * @param boolean $auto If true, heed the AUTO_QUOTE_IDENTIFIERS config option. + * @return string The quoted identifier. + */ + public function quoteIdentifier($ident, $auto = false) + { + return $this->_quoteIdentifierAs($ident, null, $auto); + } + + /** + * Quote a column identifier and alias. + * + * @param string|array|Expr $ident The identifier or expression. + * @param string $alias An alias for the column. + * @param boolean $auto If true, heed the AUTO_QUOTE_IDENTIFIERS config option. + * @return string The quoted identifier and alias. + */ + public function quoteColumnAs($ident, $alias, $auto = false) + { + return $this->_quoteIdentifierAs($ident, $alias, $auto); + } + + /** + * Quote a table identifier and alias. + * + * @param string|array|Expr $ident The identifier or expression. + * @param string $alias An alias for the table. + * @param boolean $auto If true, heed the AUTO_QUOTE_IDENTIFIERS config option. + * @return string The quoted identifier and alias. + */ + public function quoteTableAs($ident, $alias = null, $auto = false) + { + return $this->_quoteIdentifierAs($ident, $alias, $auto); + } + + /** + * Quote an identifier and an optional alias. + * + * @param string|array|Expr $ident The identifier or expression. + * @param string $alias An optional alias. + * @param boolean $auto If true, heed the AUTO_QUOTE_IDENTIFIERS config option. + * @param string $as The string to add between the identifier/expression and the alias. + * @return string The quoted identifier and alias. + */ + protected function _quoteIdentifierAs($ident, $alias = null, $auto = false, $as = ' AS ') + { + if ($ident instanceof Expr) { + $quoted = $ident->__toString(); + } elseif ($ident instanceof Select) { + $quoted = '(' . $ident->assemble() . ')'; + } else { + if (is_string($ident)) { + $ident = explode('.', $ident); + } + if (is_array($ident)) { + $segments = array(); + foreach ($ident as $segment) { + if ($segment instanceof Expr) { + $segments[] = $segment->__toString(); + } else { + $segments[] = $this->_quoteIdentifier($segment, $auto); + } + } + if ($alias !== null && end($ident) == $alias) { + $alias = null; + } + $quoted = implode('.', $segments); + } else { + $quoted = $this->_quoteIdentifier($ident, $auto); + } + } + if ($alias !== null) { + $quoted .= $as . $this->_quoteIdentifier($alias, $auto); + } + return $quoted; + } + + /** + * Quote an identifier. + * + * @param string $value The identifier or expression. + * @param boolean $auto If true, heed the AUTO_QUOTE_IDENTIFIERS config option. + * @return string The quoted identifier and alias. + */ + protected function _quoteIdentifier($value, $auto = false) + { + if ($auto === false || $this->_autoQuoteIdentifiers === true) { + $q = $this->getQuoteIdentifierSymbol(); + return ($q . str_replace("$q", "$q$q", $value) . $q); + } + return $value; + } + + /** + * Returns the symbol the adapter uses for delimited identifiers. + * + * @return string + */ + public function getQuoteIdentifierSymbol() + { + return '"'; + } + + /** + * Return the most recent value from the specified sequence in the database. + * This is supported only on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null. + * + * @param string $sequenceName + * @return string + */ + public function lastSequenceId($sequenceName) + { + return null; + } + + /** + * Generate a new value from the specified sequence in the database, and return it. + * This is supported only on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null. + * + * @param string $sequenceName + * @return string + */ + public function nextSequenceId($sequenceName) + { + return null; + } + + /** + * Helper method to change the case of the strings used + * when returning result sets in FETCH_ASSOC and FETCH_BOTH + * modes. + * + * This is not intended to be used by application code, + * but the method must be public so the Statement class + * can invoke it. + * + * @param string $key + * @return string + */ + public function foldCase($key) + { + switch ($this->_caseFolding) { + case Db::CASE_LOWER: + $value = strtolower((string) $key); + break; + case Db::CASE_UPPER: + $value = strtoupper((string) $key); + break; + case Db::CASE_NATURAL: + default: + $value = (string) $key; + } + return $value; + } + + /** + * called when object is getting serialized + * This disconnects the DB object that cant be serialized + * + * @return array + * @throws AdapterException + */ + public function __sleep() + { + if ($this->_allowSerialization == false) { + /** @see AdapterException */ + throw new AdapterException( + get_class($this) . ' is not allowed to be serialized' + ); + } + $this->_connection = null; + + return array_keys( + array_diff_key(get_object_vars($this), array('_connection' => null)) + ); + } + + /** + * called when object is getting unserialized + * + * @return void + */ + public function __wakeup() + { + if ($this->_autoReconnectOnUnserialize == true) { + $this->getConnection(); + } + } + + /** + * Abstract Methods + */ + + /** + * Returns a list of the tables in the database. + * + * @return array + */ + abstract public function listTables(); + + /** + * Returns the column descriptions for a table. + * + * The return value is an associative array keyed by the column name, + * as returned by the RDBMS. + * + * The value of each array element is an associative array + * with the following keys: + * + * SCHEMA_NAME => string; name of database or schema + * TABLE_NAME => string; + * COLUMN_NAME => string; column name + * COLUMN_POSITION => number; ordinal position of column in table + * DATA_TYPE => string; SQL datatype name of column + * DEFAULT => string; default expression of column, null if none + * NULLABLE => boolean; true if column can have nulls + * LENGTH => number; length of CHAR/VARCHAR + * SCALE => number; scale of NUMERIC/DECIMAL + * PRECISION => number; precision of NUMERIC/DECIMAL + * UNSIGNED => boolean; unsigned property of an integer type + * PRIMARY => boolean; true if column is part of the primary key + * PRIMARY_POSITION => integer; position of column in primary key + * + * @param string $tableName + * @param string $schemaName OPTIONAL + * @return array + */ + abstract public function describeTable($tableName, $schemaName = null); + + /** + * Creates a connection to the database. + * + * @return void + */ + abstract protected function _connect(); + + /** + * Test if a connection is active + * + * @return boolean + */ + abstract public function isConnected(); + + /** + * Force the connection to close. + * + * @return void + */ + abstract public function closeConnection(); + + /** + * Prepare a statement and return a PDOStatement-like object. + * + * @param string|Select $sql SQL query + * @return Statement|\PDOStatement + */ + abstract public function prepare($sql); + + /** + * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column. + * + * As a convention, on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence + * from the arguments and returns the last id generated by that sequence. + * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method + * returns the last value generated for such a column, and the table name + * argument is disregarded. + * + * @param string $tableName OPTIONAL Name of table. + * @param string $primaryKey OPTIONAL Name of primary key column. + * @return string + */ + abstract public function lastInsertId($tableName = null, $primaryKey = null); + + /** + * Begin a transaction. + */ + abstract protected function _beginTransaction(); + + /** + * Commit a transaction. + */ + abstract protected function _commit(); + + /** + * Roll-back a transaction. + */ + abstract protected function _rollBack(); + + /** + * Set the fetch mode. + * + * @param integer $mode + * @return void + * @throws AdapterException + */ + abstract public function setFetchMode($mode); + + /** + * Adds an adapter-specific LIMIT clause to the SELECT statement. + * + * @param mixed $sql + * @param integer $count + * @param integer $offset + * @return string + */ + abstract public function limit($sql, $count, $offset = 0); + + /** + * Check if the adapter supports real SQL parameters. + * + * @param string $type 'positional' or 'named' + * @return bool + */ + abstract public function supportsParameters($type); + + /** + * Retrieve server version in PHP style + * + * @return string + */ + abstract public function getServerVersion(); +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Db2.php b/vendor/gipfl/zfdb/src/Adapter/Db2.php new file mode 100644 index 0000000..55dce72 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Db2.php @@ -0,0 +1,792 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + * + */ +namespace gipfl\ZfDb\Adapter; + +use gipfl\ZfDb\Adapter\Exception\AdapterExceptionDb2; +use gipfl\ZfDb\Db; +use gipfl\ZfDb\Statement\Db2Statement; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ + +class Db2 extends Adapter +{ + /** + * User-provided configuration. + * + * Basic keys are: + * + * username => (string) Connect to the database as this username. + * password => (string) Password associated with the username. + * host => (string) What host to connect to (default 127.0.0.1) + * dbname => (string) The name of the database to user + * protocol => (string) Protocol to use, defaults to "TCPIP" + * port => (integer) Port number to use for TCP/IP if protocol is "TCPIP" + * persistent => (boolean) Set TRUE to use a persistent connection (db2_pconnect) + * os => (string) This should be set to 'i5' if the db is on an os400/i5 + * schema => (string) The default schema the connection should use + * + * @var array + */ + protected $_config = array( + 'dbname' => null, + 'username' => null, + 'password' => null, + 'host' => 'localhost', + 'port' => '50000', + 'protocol' => 'TCPIP', + 'persistent' => false, + 'os' => null, + 'schema' => null + ); + + /** + * Execution mode + * + * @var int execution flag (DB2_AUTOCOMMIT_ON or DB2_AUTOCOMMIT_OFF) + */ + protected $_execute_mode = DB2_AUTOCOMMIT_ON; + + /** + * Default class name for a DB statement. + * + * @var string + */ + protected $_defaultStmtClass = Db2Statement::class; + protected $_isI5 = false; + + /** + * Keys are UPPERCASE SQL datatypes or the constants + * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE. + * + * Values are: + * 0 = 32-bit integer + * 1 = 64-bit integer + * 2 = float or decimal + * + * @var array Associative array of datatypes to values 0, 1, or 2. + */ + protected $_numericDataTypes = array( + Db::INT_TYPE => Db::INT_TYPE, + Db::BIGINT_TYPE => Db::BIGINT_TYPE, + Db::FLOAT_TYPE => Db::FLOAT_TYPE, + 'INTEGER' => Db::INT_TYPE, + 'SMALLINT' => Db::INT_TYPE, + 'BIGINT' => Db::BIGINT_TYPE, + 'DECIMAL' => Db::FLOAT_TYPE, + 'NUMERIC' => Db::FLOAT_TYPE + ); + + /** + * Creates a connection resource. + * + * @return void + */ + protected function _connect() + { + if (is_resource($this->_connection)) { + // connection already exists + return; + } + + if (!extension_loaded('ibm_db2')) { + throw new AdapterExceptionDb2( + 'The IBM DB2 extension is required for this adapter but the extension is not loaded' + ); + } + + $this->_determineI5(); + if ($this->_config['persistent']) { + // use persistent connection + $conn_func_name = 'db2_pconnect'; + } else { + // use "normal" connection + $conn_func_name = 'db2_connect'; + } + + if (!isset($this->_config['driver_options']['autocommit'])) { + // set execution mode + $this->_config['driver_options']['autocommit'] = &$this->_execute_mode; + } + + if (isset($this->_config['options'][Db::CASE_FOLDING])) { + $caseAttrMap = array( + Db::CASE_NATURAL => DB2_CASE_NATURAL, + Db::CASE_UPPER => DB2_CASE_UPPER, + Db::CASE_LOWER => DB2_CASE_LOWER + ); + $this->_config['driver_options']['DB2_ATTR_CASE'] + = $caseAttrMap[$this->_config['options'][Db::CASE_FOLDING]]; + } + + if ($this->_isI5 && isset($this->_config['driver_options']['i5_naming'])) { + if ($this->_config['driver_options']['i5_naming']) { + $this->_config['driver_options']['i5_naming'] = DB2_I5_NAMING_ON; + } else { + $this->_config['driver_options']['i5_naming'] = DB2_I5_NAMING_OFF; + } + } + + if ($this->_config['host'] !== 'localhost' && !$this->_isI5) { + // if the host isn't localhost, use extended connection params + $dbname = 'DRIVER={IBM DB2 ODBC DRIVER}' . + ';DATABASE=' . $this->_config['dbname'] . + ';HOSTNAME=' . $this->_config['host'] . + ';PORT=' . $this->_config['port'] . + ';PROTOCOL=' . $this->_config['protocol'] . + ';UID=' . $this->_config['username'] . + ';PWD=' . $this->_config['password'] .';'; + $this->_connection = $conn_func_name( + $dbname, + null, + null, + $this->_config['driver_options'] + ); + } else { + // host is localhost, so use standard connection params + $this->_connection = $conn_func_name( + $this->_config['dbname'], + $this->_config['username'], + $this->_config['password'], + $this->_config['driver_options'] + ); + } + + // check the connection + if (!$this->_connection) { + throw new AdapterExceptionDb2(db2_conn_errormsg(), db2_conn_error()); + } + } + + /** + * Test if a connection is active + * + * @return boolean + */ + public function isConnected() + { + return ((bool) (is_resource($this->_connection) + && get_resource_type($this->_connection) == 'DB2 Connection')); + } + + /** + * Force the connection to close. + * + * @return void + */ + public function closeConnection() + { + if ($this->isConnected()) { + db2_close($this->_connection); + } + $this->_connection = null; + } + + /** + * Returns an SQL statement for preparation. + * + * @param string $sql The SQL statement with placeholders. + * @return Db2Statement + */ + public function prepare($sql) + { + $this->_connect(); + $stmtClass = $this->_defaultStmtClass; + $stmt = new $stmtClass($this, $sql); + $stmt->setFetchMode($this->_fetchMode); + return $stmt; + } + + /** + * Gets the execution mode + * + * @return int the execution mode (DB2_AUTOCOMMIT_ON or DB2_AUTOCOMMIT_OFF) + */ + public function _getExecuteMode() + { + return $this->_execute_mode; + } + + /** + * @param integer $mode + * @return void + */ + public function _setExecuteMode($mode) + { + switch ($mode) { + case DB2_AUTOCOMMIT_OFF: + case DB2_AUTOCOMMIT_ON: + $this->_execute_mode = $mode; + db2_autocommit($this->_connection, $mode); + break; + default: + /** + * @see AdapterExceptionDb2 + */ + throw new AdapterExceptionDb2("execution mode not supported"); + break; + } + } + + /** + * Quote a raw string. + * + * @param string $value Raw string + * @return string Quoted string + */ + protected function _quote($value) + { + if (is_int($value) || is_float($value)) { + return $value; + } + /** + * Use db2_escape_string() if it is present in the IBM DB2 extension. + * But some supported versions of PHP do not include this function, + * so fall back to default quoting in the parent class. + */ + if (function_exists('db2_escape_string')) { + return "'" . db2_escape_string($value) . "'"; + } + return parent::_quote($value); + } + + /** + * @return string + */ + public function getQuoteIdentifierSymbol() + { + $this->_connect(); + $info = db2_server_info($this->_connection); + if ($info) { + $identQuote = $info->IDENTIFIER_QUOTE_CHAR; + } else { + // db2_server_info() does not return result on some i5 OS version + if ($this->_isI5) { + $identQuote ="'"; + } + } + return $identQuote; + } + + /** + * Returns a list of the tables in the database. + * @param string $schema OPTIONAL + * @return array + */ + public function listTables($schema = null) + { + $this->_connect(); + + if ($schema === null && $this->_config['schema'] != null) { + $schema = $this->_config['schema']; + } + + $tables = array(); + + if (!$this->_isI5) { + if ($schema) { + $stmt = db2_tables($this->_connection, null, $schema); + } else { + $stmt = db2_tables($this->_connection); + } + while ($row = db2_fetch_assoc($stmt)) { + $tables[] = $row['TABLE_NAME']; + } + } else { + $tables = $this->_i5listTables($schema); + } + + return $tables; + } + + + /** + * Returns the column descriptions for a table. + * + * The return value is an associative array keyed by the column name, + * as returned by the RDBMS. + * + * The value of each array element is an associative array + * with the following keys: + * + * SCHEMA_NAME => string; name of database or schema + * TABLE_NAME => string; + * COLUMN_NAME => string; column name + * COLUMN_POSITION => number; ordinal position of column in table + * DATA_TYPE => string; SQL datatype name of column + * DEFAULT => string; default expression of column, null if none + * NULLABLE => boolean; true if column can have nulls + * LENGTH => number; length of CHAR/VARCHAR + * SCALE => number; scale of NUMERIC/DECIMAL + * PRECISION => number; precision of NUMERIC/DECIMAL + * UNSIGNED => boolean; unsigned property of an integer type + * DB2 not supports UNSIGNED integer. + * PRIMARY => boolean; true if column is part of the primary key + * PRIMARY_POSITION => integer; position of column in primary key + * IDENTITY => integer; true if column is auto-generated with unique values + * + * @param string $tableName + * @param string $schemaName OPTIONAL + * @return array + */ + public function describeTable($tableName, $schemaName = null) + { + // Ensure the connection is made so that _isI5 is set + $this->_connect(); + + if ($schemaName === null && $this->_config['schema'] != null) { + $schemaName = $this->_config['schema']; + } + + if (!$this->_isI5) { + $sql = "SELECT DISTINCT c.tabschema, c.tabname, c.colname, c.colno, + c.typename, c.default, c.nulls, c.length, c.scale, + c.identity, tc.type AS tabconsttype, k.colseq + FROM syscat.columns c + LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc + ON (k.tabschema = tc.tabschema + AND k.tabname = tc.tabname + AND tc.type = 'P')) + ON (c.tabschema = k.tabschema + AND c.tabname = k.tabname + AND c.colname = k.colname) + WHERE " + . $this->quoteInto('UPPER(c.tabname) = UPPER(?)', $tableName); + + if ($schemaName) { + $sql .= $this->quoteInto(' AND UPPER(c.tabschema) = UPPER(?)', $schemaName); + } + + $sql .= " ORDER BY c.colno"; + } else { + // DB2 On I5 specific query + $sql = "SELECT DISTINCT C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.ORDINAL_POSITION, + C.DATA_TYPE, C.COLUMN_DEFAULT, C.NULLS ,C.LENGTH, C.SCALE, LEFT(C.IDENTITY,1), + LEFT(tc.TYPE, 1) AS tabconsttype, k.COLSEQ + FROM QSYS2.SYSCOLUMNS C + LEFT JOIN (QSYS2.syskeycst k JOIN QSYS2.SYSCST tc + ON (k.TABLE_SCHEMA = tc.TABLE_SCHEMA + AND k.TABLE_NAME = tc.TABLE_NAME + AND LEFT(tc.type,1) = 'P')) + ON (C.TABLE_SCHEMA = k.TABLE_SCHEMA + AND C.TABLE_NAME = k.TABLE_NAME + AND C.COLUMN_NAME = k.COLUMN_NAME) + WHERE " + . $this->quoteInto('UPPER(C.TABLE_NAME) = UPPER(?)', $tableName); + + if ($schemaName) { + $sql .= $this->quoteInto(' AND UPPER(C.TABLE_SCHEMA) = UPPER(?)', $schemaName); + } + + $sql .= " ORDER BY C.ORDINAL_POSITION FOR FETCH ONLY"; + } + + $desc = array(); + $stmt = $this->query($sql); + + /** + * To avoid case issues, fetch using FETCH_NUM + */ + $result = $stmt->fetchAll(Db::FETCH_NUM); + + /** + * The ordering of columns is defined by the query so we can map + * to variables to improve readability + */ + $tabschema = 0; + $tabname = 1; + $colname = 2; + $colno = 3; + $typename = 4; + $default = 5; + $nulls = 6; + $length = 7; + $scale = 8; + $identityCol = 9; + $tabconstType = 10; + $colseq = 11; + + foreach ($result as $key => $row) { + list ($primary, $primaryPosition, $identity) = array(false, null, false); + if ($row[$tabconstType] == 'P') { + $primary = true; + $primaryPosition = $row[$colseq]; + } + /** + * In IBM DB2, an column can be IDENTITY + * even if it is not part of the PRIMARY KEY. + */ + if ($row[$identityCol] == 'Y') { + $identity = true; + } + + // only colname needs to be case adjusted + $desc[$this->foldCase($row[$colname])] = array( + 'SCHEMA_NAME' => $this->foldCase($row[$tabschema]), + 'TABLE_NAME' => $this->foldCase($row[$tabname]), + 'COLUMN_NAME' => $this->foldCase($row[$colname]), + 'COLUMN_POSITION' => (!$this->_isI5) ? $row[$colno]+1 : $row[$colno], + 'DATA_TYPE' => $row[$typename], + 'DEFAULT' => $row[$default], + 'NULLABLE' => (bool) ($row[$nulls] == 'Y'), + 'LENGTH' => $row[$length], + 'SCALE' => $row[$scale], + 'PRECISION' => ($row[$typename] == 'DECIMAL' ? $row[$length] : 0), + 'UNSIGNED' => false, + 'PRIMARY' => $primary, + 'PRIMARY_POSITION' => $primaryPosition, + 'IDENTITY' => $identity + ); + } + + return $desc; + } + + /** + * Return the most recent value from the specified sequence in the database. + * This is supported only on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null. + * + * @param string $sequenceName + * @return string + */ + public function lastSequenceId($sequenceName) + { + $this->_connect(); + + if (!$this->_isI5) { + $quotedSequenceName = $this->quoteIdentifier($sequenceName, true); + $sql = 'SELECT PREVVAL FOR ' . $quotedSequenceName . ' AS VAL FROM SYSIBM.SYSDUMMY1'; + } else { + $quotedSequenceName = $sequenceName; + $sql = 'SELECT PREVVAL FOR ' . $this->quoteIdentifier($sequenceName, true) . ' AS VAL FROM QSYS2.QSQPTABL'; + } + + $value = $this->fetchOne($sql); + return (string) $value; + } + + /** + * Generate a new value from the specified sequence in the database, and return it. + * This is supported only on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null. + * + * @param string $sequenceName + * @return string + */ + public function nextSequenceId($sequenceName) + { + $this->_connect(); + $sql = 'SELECT NEXTVAL FOR '.$this->quoteIdentifier($sequenceName, true).' AS VAL FROM SYSIBM.SYSDUMMY1'; + $value = $this->fetchOne($sql); + return (string) $value; + } + + /** + * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column. + * + * As a convention, on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence + * from the arguments and returns the last id generated by that sequence. + * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method + * returns the last value generated for such a column, and the table name + * argument is disregarded. + * + * The IDENTITY_VAL_LOCAL() function gives the last generated identity value + * in the current process, even if it was for a GENERATED column. + * + * @param string $tableName OPTIONAL + * @param string $primaryKey OPTIONAL + * @param string $idType OPTIONAL used for i5 platform to define sequence/idenity unique value + * @return string + */ + + public function lastInsertId($tableName = null, $primaryKey = null, $idType = null) + { + $this->_connect(); + + if ($this->_isI5) { + return (string) $this->_i5LastInsertId($tableName, $idType); + } + + if ($tableName !== null) { + $sequenceName = $tableName; + if ($primaryKey) { + $sequenceName .= "_$primaryKey"; + } + $sequenceName .= '_seq'; + return $this->lastSequenceId($sequenceName); + } + + $sql = 'SELECT IDENTITY_VAL_LOCAL() AS VAL FROM SYSIBM.SYSDUMMY1'; + $value = $this->fetchOne($sql); + return (string) $value; + } + + /** + * Begin a transaction. + * + * @return void + */ + protected function _beginTransaction() + { + $this->_setExecuteMode(DB2_AUTOCOMMIT_OFF); + } + + /** + * Commit a transaction. + * + * @return void + */ + protected function _commit() + { + if (!db2_commit($this->_connection)) { + /** + * @see AdapterExceptionDb2 + */ + throw new AdapterExceptionDb2( + db2_conn_errormsg($this->_connection), + db2_conn_error($this->_connection) + ); + } + + $this->_setExecuteMode(DB2_AUTOCOMMIT_ON); + } + + /** + * Rollback a transaction. + * + * @return void + */ + protected function _rollBack() + { + if (!db2_rollback($this->_connection)) { + /** + * @see AdapterExceptionDb2 + */ + throw new AdapterExceptionDb2( + db2_conn_errormsg($this->_connection), + db2_conn_error($this->_connection) + ); + } + $this->_setExecuteMode(DB2_AUTOCOMMIT_ON); + } + + /** + * Set the fetch mode. + * + * @param integer $mode + * @return void + * @throws AdapterExceptionDb2 + */ + public function setFetchMode($mode) + { + switch ($mode) { + case Db::FETCH_NUM: // seq array + case Db::FETCH_ASSOC: // assoc array + case Db::FETCH_BOTH: // seq+assoc array + case Db::FETCH_OBJ: // object + $this->_fetchMode = $mode; + break; + case Db::FETCH_BOUND: // bound to PHP variable + throw new AdapterExceptionDb2('FETCH_BOUND is not supported yet'); + default: + throw new AdapterExceptionDb2("Invalid fetch mode '$mode' specified"); + } + } + + /** + * Adds an adapter-specific LIMIT clause to the SELECT statement. + * + * @param string $sql + * @param integer $count + * @param integer $offset OPTIONAL + * @return string + */ + public function limit($sql, $count, $offset = 0) + { + $count = intval($count); + if ($count <= 0) { + throw new AdapterExceptionDb2("LIMIT argument count=$count is not valid"); + } + + $offset = intval($offset); + if ($offset < 0) { + throw new AdapterExceptionDb2("LIMIT argument offset=$offset is not valid"); + } + + if ($offset == 0) { + $limit_sql = $sql . " FETCH FIRST $count ROWS ONLY"; + return $limit_sql; + } + + /** + * DB2 does not implement the LIMIT clause as some RDBMS do. + * We have to simulate it with subqueries and ROWNUM. + * Unfortunately because we use the column wildcard "*", + * this puts an extra column into the query result set. + */ + $limit_sql = "SELECT z2.* + FROM ( + SELECT ROW_NUMBER() OVER() AS \"ZEND_DB_ROWNUM\", z1.* + FROM ( + " . $sql . " + ) z1 + ) z2 + WHERE z2.zend_db_rownum BETWEEN " . ($offset+1) . " AND " . ($offset+$count); + return $limit_sql; + } + + /** + * Check if the adapter supports real SQL parameters. + * + * @param string $type 'positional' or 'named' + * @return bool + */ + public function supportsParameters($type) + { + if ($type == 'positional') { + return true; + } + + // if its 'named' or anything else + return false; + } + + /** + * Retrieve server version in PHP style + * + * @return string + */ + public function getServerVersion() + { + $this->_connect(); + $server_info = db2_server_info($this->_connection); + if ($server_info !== false) { + $version = $server_info->DBMS_VER; + if ($this->_isI5) { + $version = (int) substr($version, 0, 2) + . '.' . (int) substr($version, 2, 2) + . '.' . (int) substr($version, 4); + } + return $version; + } else { + return null; + } + } + + /** + * Return whether or not this is running on i5 + * + * @return bool + */ + public function isI5() + { + if ($this->_isI5 === null) { + $this->_determineI5(); + } + + return (bool) $this->_isI5; + } + + /** + * Check the connection parameters according to verify + * type of used OS + * + * @return void + */ + protected function _determineI5() + { + // first us the compiled flag. + $this->_isI5 = (php_uname('s') == 'OS400') ? true : false; + + // if this is set, then us it + if (isset($this->_config['os'])) { + if (strtolower($this->_config['os']) === 'i5') { + $this->_isI5 = true; + } else { + // any other value passed in, its null + $this->_isI5 = false; + } + } + } + + /** + * Db2 On I5 specific method + * + * Returns a list of the tables in the database . + * Used only for DB2/400. + * + * @return array + */ + protected function _i5listTables($schema = null) + { + //list of i5 libraries. + $tables = array(); + if ($schema) { + $tablesStatement = db2_tables($this->_connection, null, $schema); + while ($rowTables = db2_fetch_assoc($tablesStatement)) { + if ($rowTables['TABLE_NAME'] !== null) { + $tables[] = $rowTables['TABLE_NAME']; + } + } + } else { + $schemaStatement = db2_tables($this->_connection); + while ($schema = db2_fetch_assoc($schemaStatement)) { + if ($schema['TABLE_SCHEM'] !== null) { + // list of the tables which belongs to the selected library + $tablesStatement = db2_tables($this->_connection, null, $schema['TABLE_SCHEM']); + if (is_resource($tablesStatement)) { + while ($rowTables = db2_fetch_assoc($tablesStatement)) { + if ($rowTables['TABLE_NAME'] !== null) { + $tables[] = $rowTables['TABLE_NAME']; + } + } + } + } + } + } + + return $tables; + } + + protected function _i5LastInsertId($objectName = null, $idType = null) + { + + if ($objectName === null) { + $sql = 'SELECT IDENTITY_VAL_LOCAL() AS VAL FROM QSYS2.QSQPTABL'; + $value = $this->fetchOne($sql); + return $value; + } + + if (strtoupper($idType) === 'S') { + //check i5_lib option + $sequenceName = $objectName; + return $this->lastSequenceId($sequenceName); + } + + //returns last identity value for the specified table + //if (strtoupper($idType) === 'I') { + $tableName = $objectName; + return $this->fetchOne('SELECT IDENTITY_VAL_LOCAL() from ' . $this->quoteIdentifier($tableName)); + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Exception/AdapterException.php b/vendor/gipfl/zfdb/src/Adapter/Exception/AdapterException.php new file mode 100644 index 0000000..67e000d --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Exception/AdapterException.php @@ -0,0 +1,50 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter\Exception; + +use gipfl\ZfDb\Exception\DbException; +use Exception; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class AdapterException extends DbException +{ + protected $_chainedException = null; + + public function __construct($message = '', $code = 0, Exception $e = null) + { + if ($e && (0 === $code)) { + $code = $e->getCode(); + $code = ctype_digit($code) ? (int) $code : 0; + } + parent::__construct($message, $code, $e); + } + + public function hasChainedException() + { + return ($this->getPrevious() !== null); + } + + public function getChainedException() + { + return $this->getPrevious(); + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionDb2.php b/vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionDb2.php new file mode 100644 index 0000000..c43b36f --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionDb2.php @@ -0,0 +1,42 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter\Exception; + +use Exception; + +/** + * Zend_Db_Adapter_Exception + */ + +/** + * Zend_Db_Adapter_Db2_Exception + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class AdapterExceptionDb2 extends AdapterException +{ + protected $code = '00000'; + protected $message = 'unknown exception'; + + public function __construct($message = 'unknown exception', $code = '00000', Exception $e = null) + { + parent::__construct($message, $code, $e); + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionMysqli.php b/vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionMysqli.php new file mode 100644 index 0000000..99029b4 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionMysqli.php @@ -0,0 +1,30 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + * + */ +namespace gipfl\ZfDb\Adapter\Exception; + +/** + * Zend_Db_Adapter_Mysqli_Exception + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class AdapterExceptionMysqli extends AdapterException +{ +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionOracle.php b/vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionOracle.php new file mode 100644 index 0000000..2d8f45d --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionOracle.php @@ -0,0 +1,51 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter\Exception; + +/** + * Zend_Db_Adapter_Oracle_Exception + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class AdapterExceptionOracle extends AdapterException +{ + protected $message = 'Unknown exception'; + protected $code = 0; + + public function __construct($error = null, $code = 0) + { + if (is_array($error)) { + if (!isset($error['offset'])) { + $this->message = $error['code'] .' '. $error['message']; + } else { + $this->message = $error['code'] .' '. $error['message']." " + . substr($error['sqltext'], 0, $error['offset']) + . "*" + . substr($error['sqltext'], $error['offset']); + } + $this->code = $error['code']; + } elseif (is_string($error)) { + $this->message = $error; + } + if (!$this->code && $code) { + $this->code = $code; + } + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionSqlsrv.php b/vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionSqlsrv.php new file mode 100644 index 0000000..dde821a --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Exception/AdapterExceptionSqlsrv.php @@ -0,0 +1,55 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter\Exception; + +use Exception; + +/** + * Zend_Db_Adapter_Sqlsrv_Exception + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class AdapterExceptionSqlsrv extends AdapterException +{ + /** + * Constructor + * + * If $message is an array, the assumption is that the return value of + * sqlsrv_errors() was provided. If so, it then retrieves the most recent + * error from that stack, and sets the message and code based on it. + * + * @param null|array|string $message + * @param null|int $code + */ + public function __construct($message = null, $code = 0) + { + if (is_array($message)) { + // Error should be array of errors + // We only need first one (?) + if (isset($message[0])) { + $message = $message[0]; + } + + $code = (int) $message['code']; + $message = (string) $message['message']; + } + parent::__construct($message, $code, new Exception($message, $code)); + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Mysqli.php b/vendor/gipfl/zfdb/src/Adapter/Mysqli.php new file mode 100644 index 0000000..15a191f --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Mysqli.php @@ -0,0 +1,499 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter; + +use gipfl\ZfDb\Db; +use gipfl\ZfDb\Statement\Exception\StatementExceptionMysqli; +use gipfl\ZfDb\Statement\MysqliStatement; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Mysqli extends Adapter +{ + /** + * Keys are UPPERCASE SQL datatypes or the constants + * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE. + * + * Values are: + * 0 = 32-bit integer + * 1 = 64-bit integer + * 2 = float or decimal + * + * @var array Associative array of datatypes to values 0, 1, or 2. + */ + protected $_numericDataTypes = array( + Db::INT_TYPE => Db::INT_TYPE, + Db::BIGINT_TYPE => Db::BIGINT_TYPE, + Db::FLOAT_TYPE => Db::FLOAT_TYPE, + 'INT' => Db::INT_TYPE, + 'INTEGER' => Db::INT_TYPE, + 'MEDIUMINT' => Db::INT_TYPE, + 'SMALLINT' => Db::INT_TYPE, + 'TINYINT' => Db::INT_TYPE, + 'BIGINT' => Db::BIGINT_TYPE, + 'SERIAL' => Db::BIGINT_TYPE, + 'DEC' => Db::FLOAT_TYPE, + 'DECIMAL' => Db::FLOAT_TYPE, + 'DOUBLE' => Db::FLOAT_TYPE, + 'DOUBLE PRECISION' => Db::FLOAT_TYPE, + 'FIXED' => Db::FLOAT_TYPE, + 'FLOAT' => Db::FLOAT_TYPE + ); + + /** + * @var MysqliStatement + */ + protected $_stmt = null; + + /** + * Default class name for a DB statement. + * + * @var string + */ + protected $_defaultStmtClass = MysqliStatement::class; + + /** + * Quote a raw string. + * + * @param mixed $value Raw string + * + * @return string Quoted string + */ + protected function _quote($value) + { + if (is_int($value) || is_float($value)) { + return $value; + } + $this->_connect(); + return "'" . $this->_connection->real_escape_string($value) . "'"; + } + + /** + * Returns the symbol the adapter uses for delimiting identifiers. + * + * @return string + */ + public function getQuoteIdentifierSymbol() + { + return "`"; + } + + /** + * Returns a list of the tables in the database. + * + * @return array + * @throws StatementExceptionMysqli + */ + public function listTables() + { + $result = array(); + // Use mysqli extension API, because SHOW doesn't work + // well as a prepared statement on MySQL 4.1. + $sql = 'SHOW TABLES'; + if ($queryResult = $this->getConnection()->query($sql)) { + while ($row = $queryResult->fetch_row()) { + $result[] = $row[0]; + } + $queryResult->close(); + } else { + throw new StatementExceptionMysqli($this->getConnection()->error); + } + return $result; + } + + /** + * Returns the column descriptions for a table. + * + * The return value is an associative array keyed by the column name, + * as returned by the RDBMS. + * + * The value of each array element is an associative array + * with the following keys: + * + * SCHEMA_NAME => string; name of database or schema + * TABLE_NAME => string; + * COLUMN_NAME => string; column name + * COLUMN_POSITION => number; ordinal position of column in table + * DATA_TYPE => string; SQL datatype name of column + * DEFAULT => string; default expression of column, null if none + * NULLABLE => boolean; true if column can have nulls + * LENGTH => number; length of CHAR/VARCHAR + * SCALE => number; scale of NUMERIC/DECIMAL + * PRECISION => number; precision of NUMERIC/DECIMAL + * UNSIGNED => boolean; unsigned property of an integer type + * PRIMARY => boolean; true if column is part of the primary key + * PRIMARY_POSITION => integer; position of column in primary key + * IDENTITY => integer; true if column is auto-generated with unique values + * + * @param string $tableName + * @param string $schemaName OPTIONAL + * @return array + */ + public function describeTable($tableName, $schemaName = null) + { + /** + * @todo use INFORMATION_SCHEMA someday when + * MySQL's implementation isn't too slow. + */ + + if ($schemaName) { + $sql = 'DESCRIBE ' . $this->quoteIdentifier("$schemaName.$tableName", true); + } else { + $sql = 'DESCRIBE ' . $this->quoteIdentifier($tableName, true); + } + + /** + * Use mysqli extension API, because DESCRIBE doesn't work + * well as a prepared statement on MySQL 4.1. + */ + if ($queryResult = $this->getConnection()->query($sql)) { + while ($row = $queryResult->fetch_assoc()) { + $result[] = $row; + } + $queryResult->close(); + } else { + throw new StatementExceptionMysqli($this->getConnection()->error); + } + + $desc = array(); + + $row_defaults = array( + 'Length' => null, + 'Scale' => null, + 'Precision' => null, + 'Unsigned' => null, + 'Primary' => false, + 'PrimaryPosition' => null, + 'Identity' => false + ); + $i = 1; + $p = 1; + foreach ($result as $key => $row) { + $row = array_merge($row_defaults, $row); + if (preg_match('/unsigned/', $row['Type'])) { + $row['Unsigned'] = true; + } + if (preg_match('/^((?:var)?char)\((\d+)\)/', $row['Type'], $matches)) { + $row['Type'] = $matches[1]; + $row['Length'] = $matches[2]; + } elseif (preg_match('/^decimal\((\d+),(\d+)\)/', $row['Type'], $matches)) { + $row['Type'] = 'decimal'; + $row['Precision'] = $matches[1]; + $row['Scale'] = $matches[2]; + } elseif (preg_match('/^float\((\d+),(\d+)\)/', $row['Type'], $matches)) { + $row['Type'] = 'float'; + $row['Precision'] = $matches[1]; + $row['Scale'] = $matches[2]; + } elseif (preg_match('/^((?:big|medium|small|tiny)?int)\((\d+)\)/', $row['Type'], $matches)) { + $row['Type'] = $matches[1]; + /** + * The optional argument of a MySQL int type is not precision + * or length; it is only a hint for display width. + */ + } + if (strtoupper($row['Key']) == 'PRI') { + $row['Primary'] = true; + $row['PrimaryPosition'] = $p; + if ($row['Extra'] == 'auto_increment') { + $row['Identity'] = true; + } else { + $row['Identity'] = false; + } + ++$p; + } + $desc[$this->foldCase($row['Field'])] = array( + 'SCHEMA_NAME' => null, // @todo + 'TABLE_NAME' => $this->foldCase($tableName), + 'COLUMN_NAME' => $this->foldCase($row['Field']), + 'COLUMN_POSITION' => $i, + 'DATA_TYPE' => $row['Type'], + 'DEFAULT' => $row['Default'], + 'NULLABLE' => (bool) ($row['Null'] == 'YES'), + 'LENGTH' => $row['Length'], + 'SCALE' => $row['Scale'], + 'PRECISION' => $row['Precision'], + 'UNSIGNED' => $row['Unsigned'], + 'PRIMARY' => $row['Primary'], + 'PRIMARY_POSITION' => $row['PrimaryPosition'], + 'IDENTITY' => $row['Identity'] + ); + ++$i; + } + return $desc; + } + + /** + * Creates a connection to the database. + * + * @return void + * @throws StatementExceptionMysqli + */ + protected function _connect() + { + if ($this->_connection) { + return; + } + + if (!extension_loaded('mysqli')) { + throw new StatementExceptionMysqli( + 'The Mysqli extension is required for this adapter but the extension is not loaded' + ); + } + + if (isset($this->_config['port'])) { + $port = (integer) $this->_config['port']; + } else { + $port = null; + } + + if (isset($this->_config['socket'])) { + $socket = $this->_config['socket']; + } else { + $socket = null; + } + + $this->_connection = mysqli_init(); + + if (!empty($this->_config['driver_options'])) { + foreach ($this->_config['driver_options'] as $option => $value) { + if (is_string($option)) { + // Suppress warnings here + // Ignore it if it's not a valid constant + $option = @constant(strtoupper($option)); + if ($option === null) { + continue; + } + } + mysqli_options($this->_connection, $option, $value); + } + } + + // Suppress connection warnings here. + // Throw an exception instead. + $_isConnected = @mysqli_real_connect( + $this->_connection, + $this->_config['host'], + $this->_config['username'], + $this->_config['password'], + $this->_config['dbname'], + $port, + $socket + ); + + if ($_isConnected === false || mysqli_connect_errno()) { + $this->closeConnection(); + throw new StatementExceptionMysqli(mysqli_connect_error()); + } + + if (!empty($this->_config['charset'])) { + mysqli_set_charset($this->_connection, $this->_config['charset']); + } + } + + /** + * Test if a connection is active + * + * @return boolean + */ + public function isConnected() + { + return ((bool) ($this->_connection instanceof mysqli)); + } + + /** + * Force the connection to close. + * + * @return void + */ + public function closeConnection() + { + if ($this->isConnected()) { + $this->_connection->close(); + } + $this->_connection = null; + } + + /** + * Prepare a statement and return a PDOStatement-like object. + * + * @param string $sql SQL query + * @return MysqliStatement|bool + */ + public function prepare($sql) + { + $this->_connect(); + if ($this->_stmt) { + $this->_stmt->close(); + } + $stmtClass = $this->_defaultStmtClass; + $stmt = new $stmtClass($this, $sql); + if ($stmt === false) { + return false; + } + $stmt->setFetchMode($this->_fetchMode); + $this->_stmt = $stmt; + return $stmt; + } + + /** + * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column. + * + * As a convention, on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence + * from the arguments and returns the last id generated by that sequence. + * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method + * returns the last value generated for such a column, and the table name + * argument is disregarded. + * + * MySQL does not support sequences, so $tableName and $primaryKey are ignored. + * + * @param string $tableName OPTIONAL Name of table. + * @param string $primaryKey OPTIONAL Name of primary key column. + * @return string + * @todo Return value should be int? + */ + public function lastInsertId($tableName = null, $primaryKey = null) + { + $mysqli = $this->_connection; + return (string) $mysqli->insert_id; + } + + /** + * Begin a transaction. + * + * @return void + */ + protected function _beginTransaction() + { + $this->_connect(); + $this->_connection->autocommit(false); + } + + /** + * Commit a transaction. + * + * @return void + */ + protected function _commit() + { + $this->_connect(); + $this->_connection->commit(); + $this->_connection->autocommit(true); + } + + /** + * Roll-back a transaction. + * + * @return void + */ + protected function _rollBack() + { + $this->_connect(); + $this->_connection->rollback(); + $this->_connection->autocommit(true); + } + + /** + * Set the fetch mode. + * + * @param int $mode + * @return void + * @throws StatementExceptionMysqli + */ + public function setFetchMode($mode) + { + switch ($mode) { + case Db::FETCH_LAZY: + case Db::FETCH_ASSOC: + case Db::FETCH_NUM: + case Db::FETCH_BOTH: + case Db::FETCH_NAMED: + case Db::FETCH_OBJ: + $this->_fetchMode = $mode; + break; + case Db::FETCH_BOUND: // bound to PHP variable + throw new StatementExceptionMysqli('FETCH_BOUND is not supported yet'); + default: + throw new StatementExceptionMysqli("Invalid fetch mode '$mode' specified"); + } + } + + /** + * Adds an adapter-specific LIMIT clause to the SELECT statement. + * + * @param string $sql + * @param int $count + * @param int $offset OPTIONAL + * @return string + * @throws StatementExceptionMysqli + */ + public function limit($sql, $count, $offset = 0) + { + $count = intval($count); + if ($count <= 0) { + throw new StatementExceptionMysqli("LIMIT argument count=$count is not valid"); + } + + $offset = intval($offset); + if ($offset < 0) { + throw new StatementExceptionMysqli("LIMIT argument offset=$offset is not valid"); + } + + $sql .= " LIMIT $count"; + if ($offset > 0) { + $sql .= " OFFSET $offset"; + } + + return $sql; + } + + /** + * Check if the adapter supports real SQL parameters. + * + * @param string $type 'positional' or 'named' + * @return bool + */ + public function supportsParameters($type) + { + switch ($type) { + case 'positional': + return true; + case 'named': + default: + return false; + } + } + + /** + * Retrieve server version in PHP style + * + *@return string + */ + public function getServerVersion() + { + $this->_connect(); + $version = $this->_connection->server_version; + $major = (int) ($version / 10000); + $minor = (int) ($version % 10000 / 100); + $revision = (int) ($version % 100); + return $major . '.' . $minor . '.' . $revision; + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Oracle.php b/vendor/gipfl/zfdb/src/Adapter/Oracle.php new file mode 100644 index 0000000..3430834 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Oracle.php @@ -0,0 +1,595 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter; + +use gipfl\ZfDb\Adapter\Exception\AdapterExceptionOracle; +use gipfl\ZfDb\Db; +use gipfl\ZfDb\Expr; +use gipfl\ZfDb\Statement\OracleStatement; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Oracle extends Adapter +{ + /** + * User-provided configuration. + * + * Basic keys are: + * + * username => (string) Connect to the database as this username. + * password => (string) Password associated with the username. + * dbname => Either the name of the local Oracle instance, or the + * name of the entry in tnsnames.ora to which you want to connect. + * persistent => (boolean) Set TRUE to use a persistent connection + * @var array + */ + protected $_config = array( + 'dbname' => null, + 'username' => null, + 'password' => null, + 'persistent' => false + ); + + /** + * Keys are UPPERCASE SQL datatypes or the constants + * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE. + * + * Values are: + * 0 = 32-bit integer + * 1 = 64-bit integer + * 2 = float or decimal + * + * @var array Associative array of datatypes to values 0, 1, or 2. + */ + protected $_numericDataTypes = array( + Db::INT_TYPE => Db::INT_TYPE, + Db::BIGINT_TYPE => Db::BIGINT_TYPE, + Db::FLOAT_TYPE => Db::FLOAT_TYPE, + 'BINARY_DOUBLE' => Db::FLOAT_TYPE, + 'BINARY_FLOAT' => Db::FLOAT_TYPE, + 'NUMBER' => Db::FLOAT_TYPE, + ); + + /** + * @var integer + */ + protected $_execute_mode = null; + + /** + * Default class name for a DB statement. + * + * @var string + */ + protected $_defaultStmtClass = OracleStatement::class; + + /** + * Check if LOB field are returned as string + * instead of OCI-Lob object + * + * @var boolean + */ + protected $_lobAsString = null; + + /** + * Creates a connection resource. + * + * @return void + * @throws AdapterExceptionOracle + */ + protected function _connect() + { + if (is_resource($this->_connection)) { + // connection already exists + return; + } + + if (!extension_loaded('oci8')) { + throw new AdapterExceptionOracle( + 'The OCI8 extension is required for this adapter but the extension is not loaded' + ); + } + + $this->_setExecuteMode(OCI_COMMIT_ON_SUCCESS); + + $connectionFuncName = ($this->_config['persistent'] == true) ? 'oci_pconnect' : 'oci_connect'; + + $this->_connection = @$connectionFuncName( + $this->_config['username'], + $this->_config['password'], + $this->_config['dbname'], + $this->_config['charset'] + ); + + // check the connection + if (!$this->_connection) { + throw new AdapterExceptionOracle(oci_error()); + } + } + + /** + * Test if a connection is active + * + * @return boolean + */ + public function isConnected() + { + return ((bool) (is_resource($this->_connection) + && (get_resource_type($this->_connection) == 'oci8 connection' + || get_resource_type($this->_connection) == 'oci8 persistent connection'))); + } + + /** + * Force the connection to close. + * + * @return void + */ + public function closeConnection() + { + if ($this->isConnected()) { + oci_close($this->_connection); + } + $this->_connection = null; + } + + /** + * Activate/deactivate return of LOB as string + * + * @param string $lobAsString + * @return Oracle + */ + public function setLobAsString($lobAsString) + { + $this->_lobAsString = (bool) $lobAsString; + return $this; + } + + /** + * Return whether or not LOB are returned as string + * + * @return boolean + */ + public function getLobAsString() + { + if ($this->_lobAsString === null) { + // if never set by user, we use driver option if it exists otherwise false + if (isset($this->_config['driver_options']) && + isset($this->_config['driver_options']['lob_as_string'])) { + $this->_lobAsString = (bool) $this->_config['driver_options']['lob_as_string']; + } else { + $this->_lobAsString = false; + } + } + return $this->_lobAsString; + } + + /** + * Returns an SQL statement for preparation. + * + * @param string $sql The SQL statement with placeholders. + * @return OracleStatement + */ + public function prepare($sql) + { + $this->_connect(); + $stmtClass = $this->_defaultStmtClass; + $stmt = new $stmtClass($this, $sql); + if ($stmt instanceof OracleStatement) { + $stmt->setLobAsString($this->getLobAsString()); + } + $stmt->setFetchMode($this->_fetchMode); + return $stmt; + } + + /** + * Quote a raw string. + * + * @param string $value Raw string + * @return string Quoted string + */ + protected function _quote($value) + { + if (is_int($value) || is_float($value)) { + return $value; + } + $value = str_replace("'", "''", $value); + return "'" . addcslashes($value, "\000\n\r\\\032") . "'"; + } + + /** + * Quote a table identifier and alias. + * + * @param string|array|Expr $ident The identifier or expression. + * @param string $alias An alias for the table. + * @param boolean $auto If true, heed the AUTO_QUOTE_IDENTIFIERS config option. + * @return string The quoted identifier and alias. + */ + public function quoteTableAs($ident, $alias = null, $auto = false) + { + // Oracle doesn't allow the 'AS' keyword between the table identifier/expression and alias. + return $this->_quoteIdentifierAs($ident, $alias, $auto, ' '); + } + + /** + * Return the most recent value from the specified sequence in the database. + * This is supported only on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null. + * + * @param string $sequenceName + * @return string + */ + public function lastSequenceId($sequenceName) + { + $this->_connect(); + $sql = 'SELECT '.$this->quoteIdentifier($sequenceName, true).'.CURRVAL FROM dual'; + $value = $this->fetchOne($sql); + return $value; + } + + /** + * Generate a new value from the specified sequence in the database, and return it. + * This is supported only on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null. + * + * @param string $sequenceName + * @return string + */ + public function nextSequenceId($sequenceName) + { + $this->_connect(); + $sql = 'SELECT '.$this->quoteIdentifier($sequenceName, true).'.NEXTVAL FROM dual'; + $value = $this->fetchOne($sql); + return $value; + } + + /** + * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column. + * + * As a convention, on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence + * from the arguments and returns the last id generated by that sequence. + * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method + * returns the last value generated for such a column, and the table name + * argument is disregarded. + * + * Oracle does not support IDENTITY columns, so if the sequence is not + * specified, this method returns null. + * + * @param string $tableName OPTIONAL Name of table. + * @param string $primaryKey OPTIONAL Name of primary key column. + * @return string + */ + public function lastInsertId($tableName = null, $primaryKey = null) + { + if ($tableName !== null) { + $sequenceName = $tableName; + if ($primaryKey) { + $sequenceName .= "_$primaryKey"; + } + $sequenceName .= '_seq'; + return $this->lastSequenceId($sequenceName); + } + + // No support for IDENTITY columns; return null + return null; + } + + /** + * Returns a list of the tables in the database. + * + * @return array + */ + public function listTables() + { + $this->_connect(); + $data = $this->fetchCol('SELECT table_name FROM all_tables'); + return $data; + } + + /** + * Returns the column descriptions for a table. + * + * The return value is an associative array keyed by the column name, + * as returned by the RDBMS. + * + * The value of each array element is an associative array + * with the following keys: + * + * SCHEMA_NAME => string; name of schema + * TABLE_NAME => string; + * COLUMN_NAME => string; column name + * COLUMN_POSITION => number; ordinal position of column in table + * DATA_TYPE => string; SQL datatype name of column + * DEFAULT => string; default expression of column, null if none + * NULLABLE => boolean; true if column can have nulls + * LENGTH => number; length of CHAR/VARCHAR + * SCALE => number; scale of NUMERIC/DECIMAL + * PRECISION => number; precision of NUMERIC/DECIMAL + * UNSIGNED => boolean; unsigned property of an integer type + * PRIMARY => boolean; true if column is part of the primary key + * PRIMARY_POSITION => integer; position of column in primary key + * IDENTITY => integer; true if column is auto-generated with unique values + * + * @todo Discover integer unsigned property. + * + * @param string $tableName + * @param string $schemaName OPTIONAL + * @return array + */ + public function describeTable($tableName, $schemaName = null) + { + $version = $this->getServerVersion(); + if (($version === null) || version_compare($version, '9.0.0', '>=')) { + $sql = "SELECT TC.TABLE_NAME, TC.OWNER, TC.COLUMN_NAME, TC.DATA_TYPE, + TC.DATA_DEFAULT, TC.NULLABLE, TC.COLUMN_ID, TC.DATA_LENGTH, + TC.DATA_SCALE, TC.DATA_PRECISION, C.CONSTRAINT_TYPE, CC.POSITION + FROM ALL_TAB_COLUMNS TC + LEFT JOIN (ALL_CONS_COLUMNS CC JOIN ALL_CONSTRAINTS C + ON (CC.CONSTRAINT_NAME = C.CONSTRAINT_NAME AND CC.TABLE_NAME = C.TABLE_NAME + AND CC.OWNER = C.OWNER AND C.CONSTRAINT_TYPE = 'P')) + ON TC.TABLE_NAME = CC.TABLE_NAME AND TC.COLUMN_NAME = CC.COLUMN_NAME + WHERE UPPER(TC.TABLE_NAME) = UPPER(:TBNAME)"; + $bind[':TBNAME'] = $tableName; + if ($schemaName) { + $sql .= ' AND UPPER(TC.OWNER) = UPPER(:SCNAME)'; + $bind[':SCNAME'] = $schemaName; + } + $sql .= ' ORDER BY TC.COLUMN_ID'; + } else { + $subSql="SELECT AC.OWNER, AC.TABLE_NAME, ACC.COLUMN_NAME, AC.CONSTRAINT_TYPE, ACC.POSITION + from ALL_CONSTRAINTS AC, ALL_CONS_COLUMNS ACC + WHERE ACC.CONSTRAINT_NAME = AC.CONSTRAINT_NAME + AND ACC.TABLE_NAME = AC.TABLE_NAME + AND ACC.OWNER = AC.OWNER + AND AC.CONSTRAINT_TYPE = 'P' + AND UPPER(AC.TABLE_NAME) = UPPER(:TBNAME)"; + $bind[':TBNAME'] = $tableName; + if ($schemaName) { + $subSql .= ' AND UPPER(ACC.OWNER) = UPPER(:SCNAME)'; + $bind[':SCNAME'] = $schemaName; + } + $sql="SELECT TC.TABLE_NAME, TC.OWNER, TC.COLUMN_NAME, TC.DATA_TYPE, + TC.DATA_DEFAULT, TC.NULLABLE, TC.COLUMN_ID, TC.DATA_LENGTH, + TC.DATA_SCALE, TC.DATA_PRECISION, CC.CONSTRAINT_TYPE, CC.POSITION + FROM ALL_TAB_COLUMNS TC, ($subSql) CC + WHERE UPPER(TC.TABLE_NAME) = UPPER(:TBNAME) + AND TC.OWNER = CC.OWNER(+) AND TC.TABLE_NAME = CC.TABLE_NAME(+) + AND TC.COLUMN_NAME = CC.COLUMN_NAME(+)"; + if ($schemaName) { + $sql .= ' AND UPPER(TC.OWNER) = UPPER(:SCNAME)'; + } + $sql .= ' ORDER BY TC.COLUMN_ID'; + } + + $stmt = $this->query($sql, $bind); + + /** + * Use FETCH_NUM so we are not dependent on the CASE attribute of the PDO connection + */ + $result = $stmt->fetchAll(Db::FETCH_NUM); + + $table_name = 0; + $owner = 1; + $column_name = 2; + $data_type = 3; + $data_default = 4; + $nullable = 5; + $column_id = 6; + $data_length = 7; + $data_scale = 8; + $data_precision = 9; + $constraint_type = 10; + $position = 11; + + $desc = array(); + foreach ($result as $key => $row) { + list ($primary, $primaryPosition, $identity) = array(false, null, false); + if ($row[$constraint_type] == 'P') { + $primary = true; + $primaryPosition = $row[$position]; + /** + * Oracle does not support auto-increment keys. + */ + $identity = false; + } + $desc[$this->foldCase($row[$column_name])] = array( + 'SCHEMA_NAME' => $this->foldCase($row[$owner]), + 'TABLE_NAME' => $this->foldCase($row[$table_name]), + 'COLUMN_NAME' => $this->foldCase($row[$column_name]), + 'COLUMN_POSITION' => $row[$column_id], + 'DATA_TYPE' => $row[$data_type], + 'DEFAULT' => $row[$data_default], + 'NULLABLE' => (bool) ($row[$nullable] == 'Y'), + 'LENGTH' => $row[$data_length], + 'SCALE' => $row[$data_scale], + 'PRECISION' => $row[$data_precision], + 'UNSIGNED' => null, // @todo + 'PRIMARY' => $primary, + 'PRIMARY_POSITION' => $primaryPosition, + 'IDENTITY' => $identity + ); + } + return $desc; + } + + /** + * Leave autocommit mode and begin a transaction. + * + * @return void + */ + protected function _beginTransaction() + { + $this->_setExecuteMode(OCI_DEFAULT); + } + + /** + * Commit a transaction and return to autocommit mode. + * + * @return void + * @throws AdapterExceptionOracle + */ + protected function _commit() + { + if (!oci_commit($this->_connection)) { + throw new AdapterExceptionOracle(oci_error($this->_connection)); + } + $this->_setExecuteMode(OCI_COMMIT_ON_SUCCESS); + } + + /** + * Roll back a transaction and return to autocommit mode. + * + * @return void + * @throws AdapterExceptionOracle + */ + protected function _rollBack() + { + if (!oci_rollback($this->_connection)) { + throw new AdapterExceptionOracle(oci_error($this->_connection)); + } + $this->_setExecuteMode(OCI_COMMIT_ON_SUCCESS); + } + + /** + * Set the fetch mode. + * + * @param integer $mode A fetch mode. + * @return void + * @throws AdapterExceptionOracle + *@todo Support FETCH_CLASS and FETCH_INTO. + * + */ + public function setFetchMode($mode) + { + switch ($mode) { + case Db::FETCH_NUM: // seq array + case Db::FETCH_ASSOC: // assoc array + case Db::FETCH_BOTH: // seq+assoc array + case Db::FETCH_OBJ: // object + $this->_fetchMode = $mode; + break; + case Db::FETCH_BOUND: // bound to PHP variable + throw new AdapterExceptionOracle('FETCH_BOUND is not supported yet'); + default: + throw new AdapterExceptionOracle("Invalid fetch mode '$mode' specified"); + } + } + + /** + * Adds an adapter-specific LIMIT clause to the SELECT statement. + * + * @param string $sql + * @param integer $count + * @param integer $offset OPTIONAL + * @return string + * @throws AdapterExceptionOracle + */ + public function limit($sql, $count, $offset = 0) + { + $count = intval($count); + if ($count <= 0) { + throw new AdapterExceptionOracle("LIMIT argument count=$count is not valid"); + } + + $offset = intval($offset); + if ($offset < 0) { + throw new AdapterExceptionOracle("LIMIT argument offset=$offset is not valid"); + } + + /** + * Oracle does not implement the LIMIT clause as some RDBMS do. + * We have to simulate it with subqueries and ROWNUM. + * Unfortunately because we use the column wildcard "*", + * this puts an extra column into the query result set. + */ + $limit_sql = "SELECT z2.* + FROM ( + SELECT z1.*, ROWNUM AS \"zend_db_rownum\" + FROM ( + " . $sql . " + ) z1 + ) z2 + WHERE z2.\"zend_db_rownum\" BETWEEN " . ($offset+1) . " AND " . ($offset+$count); + return $limit_sql; + } + + /** + * @param integer $mode + * @throws AdapterExceptionOracle + */ + private function _setExecuteMode($mode) + { + switch ($mode) { + case OCI_COMMIT_ON_SUCCESS: + case OCI_DEFAULT: + case OCI_DESCRIBE_ONLY: + $this->_execute_mode = $mode; + break; + default: + throw new AdapterExceptionOracle("Invalid execution mode '$mode' specified"); + } + } + + /** + * @return int + */ + public function _getExecuteMode() + { + return $this->_execute_mode; + } + + /** + * Check if the adapter supports real SQL parameters. + * + * @param string $type 'positional' or 'named' + * @return bool + */ + public function supportsParameters($type) + { + switch ($type) { + case 'named': + return true; + case 'positional': + default: + return false; + } + } + + /** + * Retrieve server version in PHP style + * + * @return string + */ + public function getServerVersion() + { + $this->_connect(); + $version = oci_server_version($this->_connection); + if ($version !== false) { + $matches = null; + if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $version, $matches)) { + return $matches[1]; + } else { + return null; + } + } else { + return null; + } + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Pdo/Ibm.php b/vendor/gipfl/zfdb/src/Adapter/Pdo/Ibm.php new file mode 100644 index 0000000..c953349 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Pdo/Ibm.php @@ -0,0 +1,352 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter\Pdo; + +use gipfl\ZfDb\Adapter\Exception\AdapterException; +use gipfl\ZfDb\Adapter\Pdo\Ibm\Db2; +use gipfl\ZfDb\Adapter\Pdo\Ibm\Ids; +use gipfl\ZfDb\Db; +use PDO; +use PDOException; +use PDOStatement; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Ibm extends PdoAdapter +{ + /** + * PDO type. + * + * @var string + */ + protected $_pdoType = 'ibm'; + + /** + * The IBM data server connected to + * + * @var Db2|Ids + */ + protected $_serverType = null; + + /** + * Keys are UPPERCASE SQL datatypes or the constants + * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE. + * + * Values are: + * 0 = 32-bit integer + * 1 = 64-bit integer + * 2 = float or decimal + * + * @var array Associative array of datatypes to values 0, 1, or 2. + */ + protected $_numericDataTypes = array( + Db::INT_TYPE => Db::INT_TYPE, + Db::BIGINT_TYPE => Db::BIGINT_TYPE, + Db::FLOAT_TYPE => Db::FLOAT_TYPE, + 'INTEGER' => Db::INT_TYPE, + 'SMALLINT' => Db::INT_TYPE, + 'BIGINT' => Db::BIGINT_TYPE, + 'DECIMAL' => Db::FLOAT_TYPE, + 'DEC' => Db::FLOAT_TYPE, + 'REAL' => Db::FLOAT_TYPE, + 'NUMERIC' => Db::FLOAT_TYPE, + 'DOUBLE PRECISION' => Db::FLOAT_TYPE, + 'FLOAT' => Db::FLOAT_TYPE + ); + + /** + * Creates a PDO object and connects to the database. + * + * The IBM data server is set. + * Current options are DB2 or IDS + * @todo also differentiate between z/OS and i/5 + * + * @return void + * @throws AdapterException + */ + public function _connect() + { + if ($this->_connection) { + return; + } + parent::_connect(); + + $this->getConnection()->setAttribute(Db::ATTR_STRINGIFY_FETCHES, true); + + try { + if ($this->_serverType === null) { + $server = substr($this->getConnection()->getAttribute(PDO::ATTR_SERVER_INFO), 0, 3); + + switch ($server) { + case 'DB2': + $this->_serverType = new Db2($this); + + // Add DB2-specific numeric types + $this->_numericDataTypes['DECFLOAT'] = Db::FLOAT_TYPE; + $this->_numericDataTypes['DOUBLE'] = Db::FLOAT_TYPE; + $this->_numericDataTypes['NUM'] = Db::FLOAT_TYPE; + + break; + case 'IDS': + $this->_serverType = new Ids($this); + + // Add IDS-specific numeric types + $this->_numericDataTypes['SERIAL'] = Db::INT_TYPE; + $this->_numericDataTypes['SERIAL8'] = Db::BIGINT_TYPE; + $this->_numericDataTypes['INT8'] = Db::BIGINT_TYPE; + $this->_numericDataTypes['SMALLFLOAT'] = Db::FLOAT_TYPE; + $this->_numericDataTypes['MONEY'] = Db::FLOAT_TYPE; + + break; + } + } + } catch (PDOException $e) { + /** @see AdapterException */ + $error = strpos($e->getMessage(), 'driver does not support that attribute'); + if ($error) { + throw new AdapterException( + "PDO_IBM driver extension is downlevel. Please use driver release version 1.2.1 or later", + 0, + $e + ); + } else { + throw new AdapterException($e->getMessage(), $e->getCode(), $e); + } + } + } + + /** + * Creates a PDO DSN for the adapter from $this->_config settings. + * + * @return string + */ + protected function _dsn() + { + $this->_checkRequiredOptions($this->_config); + + // check if using full connection string + if (array_key_exists('host', $this->_config)) { + $dsn = ';DATABASE=' . $this->_config['dbname'] + . ';HOSTNAME=' . $this->_config['host'] + . ';PORT=' . $this->_config['port'] + // PDO_IBM supports only DB2 TCPIP protocol + . ';PROTOCOL=' . 'TCPIP;'; + } else { + // catalogued connection + $dsn = $this->_config['dbname']; + } + return $this->_pdoType . ': ' . $dsn; + } + + /** + * Checks required options + * + * @param array $config + * @throws AdapterException + * @return void + */ + protected function _checkRequiredOptions(array $config) + { + parent::_checkRequiredOptions($config); + + if (array_key_exists('host', $this->_config) && + !array_key_exists('port', $config)) { + throw new AdapterException("Configuration must have a key for 'port' when 'host' is specified"); + } + } + + /** + * Prepares an SQL statement. + * + * @param string $sql The SQL statement with placeholders. + * @param array $bind An array of data to bind to the placeholders. + * @return PDOStatement + */ + public function prepare($sql) + { + $this->_connect(); + $stmtClass = $this->_defaultStmtClass; + $stmt = new $stmtClass($this, $sql); + $stmt->setFetchMode($this->_fetchMode); + return $stmt; + } + + /** + * Returns a list of the tables in the database. + * + * @return array + */ + public function listTables() + { + $this->_connect(); + return $this->_serverType->listTables(); + } + + /** + * Returns the column descriptions for a table. + * + * The return value is an associative array keyed by the column name, + * as returned by the RDBMS. + * + * The value of each array element is an associative array + * with the following keys: + * + * SCHEMA_NAME => string; name of database or schema + * TABLE_NAME => string; + * COLUMN_NAME => string; column name + * COLUMN_POSITION => number; ordinal position of column in table + * DATA_TYPE => string; SQL datatype name of column + * DEFAULT => string; default expression of column, null if none + * NULLABLE => boolean; true if column can have nulls + * LENGTH => number; length of CHAR/VARCHAR + * SCALE => number; scale of NUMERIC/DECIMAL + * PRECISION => number; precision of NUMERIC/DECIMAL + * UNSIGNED => boolean; unsigned property of an integer type + * PRIMARY => boolean; true if column is part of the primary key + * PRIMARY_POSITION => integer; position of column in primary key + * + * @todo Discover integer unsigned property. + * + * @param string $tableName + * @param string $schemaName OPTIONAL + * @return array + */ + public function describeTable($tableName, $schemaName = null) + { + $this->_connect(); + return $this->_serverType->describeTable($tableName, $schemaName); + } + + /** + * Inserts a table row with specified data. + * Special handling for PDO_IBM + * remove empty slots + * + * @param mixed $table The table to insert data into. + * @param array $bind Column-value pairs. + * @return int The number of affected rows. + */ + public function insert($table, array $bind) + { + $this->_connect(); + $newbind = array(); + if (is_array($bind)) { + foreach ($bind as $name => $value) { + if ($value !== null) { + $newbind[$name] = $value; + } + } + } + + return parent::insert($table, $newbind); + } + + /** + * Adds an adapter-specific LIMIT clause to the SELECT statement. + * + * @param string $sql + * @param integer $count + * @param integer $offset OPTIONAL + * @return string + */ + public function limit($sql, $count, $offset = 0) + { + $this->_connect(); + return $this->_serverType->limit($sql, $count, $offset); + } + + /** + * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT + * column. + * + * @param string $tableName OPTIONAL + * @param string $primaryKey OPTIONAL + * @return integer + */ + public function lastInsertId($tableName = null, $primaryKey = null) + { + $this->_connect(); + + if ($tableName !== null) { + $sequenceName = $tableName; + if ($primaryKey) { + $sequenceName .= "_$primaryKey"; + } + $sequenceName .= '_seq'; + return $this->lastSequenceId($sequenceName); + } + + $id = $this->getConnection()->lastInsertId(); + + return $id; + } + + /** + * Return the most recent value from the specified sequence in the database. + * + * @param string $sequenceName + * @return integer + */ + public function lastSequenceId($sequenceName) + { + $this->_connect(); + return $this->_serverType->lastSequenceId($sequenceName); + } + + /** + * Generate a new value from the specified sequence in the database, + * and return it. + * + * @param string $sequenceName + * @return integer + */ + public function nextSequenceId($sequenceName) + { + $this->_connect(); + return $this->_serverType->nextSequenceId($sequenceName); + } + + /** + * Retrieve server version in PHP style + * Pdo_Idm doesn't support getAttribute(PDO::ATTR_SERVER_VERSION) + * @return string + */ + public function getServerVersion() + { + try { + $stmt = $this->query( + 'SELECT service_level, fixpack_num FROM TABLE (sysproc.env_get_inst_info()) as INSTANCEINFO' + ); + $result = $stmt->fetchAll(Db::FETCH_NUM); + if (count($result)) { + $matches = null; + if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $result[0][0], $matches)) { + return $matches[1]; + } else { + return null; + } + } + return null; + } catch (PDOException $e) { + return null; + } + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Pdo/Ibm/Db2.php b/vendor/gipfl/zfdb/src/Adapter/Pdo/Ibm/Db2.php new file mode 100644 index 0000000..7dea70a --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Pdo/Ibm/Db2.php @@ -0,0 +1,215 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter\Pdo\Ibm; + +use gipfl\ZfDb\Adapter\Adapter; +use gipfl\ZfDb\Adapter\Exception\AdapterException; +use gipfl\ZfDb\Db; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Db2 +{ + /** + * @var Adapter + */ + protected $_adapter = null; + + /** + * Construct the data server class. + * + * It will be used to generate non-generic SQL + * for a particular data server + * + * @param Adapter $adapter + */ + public function __construct($adapter) + { + $this->_adapter = $adapter; + } + + /** + * Returns a list of the tables in the database. + * + * @return array + */ + public function listTables() + { + $sql = "SELECT tabname " + . "FROM SYSCAT.TABLES "; + return $this->_adapter->fetchCol($sql); + } + + /** + * DB2 catalog lookup for describe table + * + * @param string $tableName + * @param string $schemaName OPTIONAL + * @return array + */ + public function describeTable($tableName, $schemaName = null) + { + $sql = "SELECT DISTINCT c.tabschema, c.tabname, c.colname, c.colno, + c.typename, c.default, c.nulls, c.length, c.scale, + c.identity, tc.type AS tabconsttype, k.colseq + FROM syscat.columns c + LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc + ON (k.tabschema = tc.tabschema + AND k.tabname = tc.tabname + AND tc.type = 'P')) + ON (c.tabschema = k.tabschema + AND c.tabname = k.tabname + AND c.colname = k.colname) + WHERE " + . $this->_adapter->quoteInto('UPPER(c.tabname) = UPPER(?)', $tableName); + if ($schemaName) { + $sql .= $this->_adapter->quoteInto(' AND UPPER(c.tabschema) = UPPER(?)', $schemaName); + } + $sql .= " ORDER BY c.colno"; + + $desc = array(); + $stmt = $this->_adapter->query($sql); + + /** + * To avoid case issues, fetch using FETCH_NUM + */ + $result = $stmt->fetchAll(Db::FETCH_NUM); + + /** + * The ordering of columns is defined by the query so we can map + * to variables to improve readability + */ + $tabschema = 0; + $tabname = 1; + $colname = 2; + $colno = 3; + $typename = 4; + $default = 5; + $nulls = 6; + $length = 7; + $scale = 8; + $identityCol = 9; + $tabconstype = 10; + $colseq = 11; + + foreach ($result as $key => $row) { + list ($primary, $primaryPosition, $identity) = array(false, null, false); + if ($row[$tabconstype] == 'P') { + $primary = true; + $primaryPosition = $row[$colseq]; + } + /** + * In IBM DB2, an column can be IDENTITY + * even if it is not part of the PRIMARY KEY. + */ + if ($row[$identityCol] == 'Y') { + $identity = true; + } + + $desc[$this->_adapter->foldCase($row[$colname])] = array( + 'SCHEMA_NAME' => $this->_adapter->foldCase($row[$tabschema]), + 'TABLE_NAME' => $this->_adapter->foldCase($row[$tabname]), + 'COLUMN_NAME' => $this->_adapter->foldCase($row[$colname]), + 'COLUMN_POSITION' => $row[$colno]+1, + 'DATA_TYPE' => $row[$typename], + 'DEFAULT' => $row[$default], + 'NULLABLE' => (bool) ($row[$nulls] == 'Y'), + 'LENGTH' => $row[$length], + 'SCALE' => $row[$scale], + 'PRECISION' => ($row[$typename] == 'DECIMAL' ? $row[$length] : 0), + 'UNSIGNED' => false, + 'PRIMARY' => $primary, + 'PRIMARY_POSITION' => $primaryPosition, + 'IDENTITY' => $identity + ); + } + + return $desc; + } + + /** + * Adds a DB2-specific LIMIT clause to the SELECT statement. + * + * @param string $sql + * @param integer $count + * @param integer $offset OPTIONAL + * @throws AdapterException + * @return string + */ + public function limit($sql, $count, $offset = 0) + { + $count = intval($count); + if ($count < 0) { + throw new AdapterException("LIMIT argument count=$count is not valid"); + } else { + $offset = intval($offset); + if ($offset < 0) { + throw new AdapterException("LIMIT argument offset=$offset is not valid"); + } + + if ($offset == 0 && $count > 0) { + $limit_sql = $sql . " FETCH FIRST $count ROWS ONLY"; + return $limit_sql; + } + /** + * DB2 does not implement the LIMIT clause as some RDBMS do. + * We have to simulate it with subqueries and ROWNUM. + * Unfortunately because we use the column wildcard "*", + * this puts an extra column into the query result set. + */ + $limit_sql = "SELECT z2.* + FROM ( + SELECT ROW_NUMBER() OVER() AS \"ZEND_DB_ROWNUM\", z1.* + FROM ( + " . $sql . " + ) z1 + ) z2 + WHERE z2.zend_db_rownum BETWEEN " . ($offset+1) . " AND " . ($offset+$count); + } + return $limit_sql; + } + + /** + * DB2-specific last sequence id + * + * @param string $sequenceName + * @return integer + */ + public function lastSequenceId($sequenceName) + { + $sql = 'SELECT PREVVAL FOR '.$this->_adapter->quoteIdentifier($sequenceName).' AS VAL FROM SYSIBM.SYSDUMMY1'; + $value = $this->_adapter->fetchOne($sql); + return $value; + } + + /** + * DB2-specific sequence id value + * + * @param string $sequenceName + * @return integer + */ + public function nextSequenceId($sequenceName) + { + $sql = 'SELECT NEXTVAL FOR '.$this->_adapter->quoteIdentifier($sequenceName).' AS VAL FROM SYSIBM.SYSDUMMY1'; + $value = $this->_adapter->fetchOne($sql); + return $value; + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Pdo/Ibm/Ids.php b/vendor/gipfl/zfdb/src/Adapter/Pdo/Ibm/Ids.php new file mode 100644 index 0000000..730b3b2 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Pdo/Ibm/Ids.php @@ -0,0 +1,290 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter\Pdo\Ibm; + +use gipfl\ZfDb\Adapter\Adapter; +use gipfl\ZfDb\Adapter\Exception\AdapterException; +use gipfl\ZfDb\Db; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Ids +{ + /** + * @var Adapter + */ + protected $_adapter = null; + + /** + * Construct the data server class. + * + * It will be used to generate non-generic SQL + * for a particular data server + * + * @param Adapter $adapter + */ + public function __construct($adapter) + { + $this->_adapter = $adapter; + } + + /** + * Returns a list of the tables in the database. + * + * @return array + */ + public function listTables() + { + $sql = "SELECT tabname " + . "FROM systables "; + + return $this->_adapter->fetchCol($sql); + } + + /** + * IDS catalog lookup for describe table + * + * @param string $tableName + * @param string $schemaName OPTIONAL + * @return array + */ + public function describeTable($tableName, $schemaName = null) + { + // this is still a work in progress + + $sql= "SELECT DISTINCT t.owner, t.tabname, c.colname, c.colno, c.coltype, + d.default, c.collength, t.tabid + FROM syscolumns c + JOIN systables t ON c.tabid = t.tabid + LEFT JOIN sysdefaults d ON c.tabid = d.tabid AND c.colno = d.colno + WHERE " + . $this->_adapter->quoteInto('UPPER(t.tabname) = UPPER(?)', $tableName); + if ($schemaName) { + $sql .= $this->_adapter->quoteInto(' AND UPPER(t.owner) = UPPER(?)', $schemaName); + } + $sql .= " ORDER BY c.colno"; + + $desc = array(); + $stmt = $this->_adapter->query($sql); + + $result = $stmt->fetchAll(Db::FETCH_NUM); + + /** + * The ordering of columns is defined by the query so we can map + * to variables to improve readability + */ + $tabschema = 0; + $tabname = 1; + $colname = 2; + $colno = 3; + $typename = 4; + $default = 5; + $length = 6; + $tabid = 7; + + $primaryCols = null; + + foreach ($result as $key => $row) { + $primary = false; + $primaryPosition = null; + + if (!$primaryCols) { + $primaryCols = $this->_getPrimaryInfo($row[$tabid]); + } + + if (array_key_exists($row[$colno], $primaryCols)) { + $primary = true; + $primaryPosition = $primaryCols[$row[$colno]]; + } + + $identity = false; + if ($row[$typename] == 6 + 256 || + $row[$typename] == 18 + 256) { + $identity = true; + } + + $desc[$this->_adapter->foldCase($row[$colname])] = array ( + 'SCHEMA_NAME' => $this->_adapter->foldCase($row[$tabschema]), + 'TABLE_NAME' => $this->_adapter->foldCase($row[$tabname]), + 'COLUMN_NAME' => $this->_adapter->foldCase($row[$colname]), + 'COLUMN_POSITION' => $row[$colno], + 'DATA_TYPE' => $this->_getDataType($row[$typename]), + 'DEFAULT' => $row[$default], + 'NULLABLE' => (bool) !($row[$typename] - 256 >= 0), + 'LENGTH' => $row[$length], + 'SCALE' => ($row[$typename] == 5 ? $row[$length]&255 : 0), + 'PRECISION' => ($row[$typename] == 5 ? (int)($row[$length]/256) : 0), + 'UNSIGNED' => false, + 'PRIMARY' => $primary, + 'PRIMARY_POSITION' => $primaryPosition, + 'IDENTITY' => $identity + ); + } + + return $desc; + } + + /** + * Map number representation of a data type + * to a string + * + * @param int $typeNo + * @return string + */ + protected function _getDataType($typeNo) + { + $typemap = array( + 0 => "CHAR", + 1 => "SMALLINT", + 2 => "INTEGER", + 3 => "FLOAT", + 4 => "SMALLFLOAT", + 5 => "DECIMAL", + 6 => "SERIAL", + 7 => "DATE", + 8 => "MONEY", + 9 => "NULL", + 10 => "DATETIME", + 11 => "BYTE", + 12 => "TEXT", + 13 => "VARCHAR", + 14 => "INTERVAL", + 15 => "NCHAR", + 16 => "NVARCHAR", + 17 => "INT8", + 18 => "SERIAL8", + 19 => "SET", + 20 => "MULTISET", + 21 => "LIST", + 22 => "Unnamed ROW", + 40 => "Variable-length opaque type", + 4118 => "Named ROW" + ); + + if ($typeNo - 256 >= 0) { + $typeNo = $typeNo - 256; + } + + return $typemap[$typeNo]; + } + + /** + * Helper method to retrieve primary key column + * and column location + * + * @param int $tabid + * @return array + */ + protected function _getPrimaryInfo($tabid) + { + $sql = "SELECT i.part1, i.part2, i.part3, i.part4, i.part5, i.part6, + i.part7, i.part8, i.part9, i.part10, i.part11, i.part12, + i.part13, i.part14, i.part15, i.part16 + FROM sysindexes i + JOIN sysconstraints c ON c.idxname = i.idxname + WHERE i.tabid = " . $tabid . " AND c.constrtype = 'P'"; + + $stmt = $this->_adapter->query($sql); + $results = $stmt->fetchAll(); + + $cols = array(); + + // this should return only 1 row + // unless there is no primary key, + // in which case, the empty array is returned + if ($results) { + $row = $results[0]; + } else { + return $cols; + } + + $position = 0; + foreach ($row as $key => $colno) { + $position++; + if ($colno == 0) { + return $cols; + } else { + $cols[$colno] = $position; + } + } + + return $cols; + } + + /** + * Adds an IDS-specific LIMIT clause to the SELECT statement. + * + * @param string $sql + * @param integer $count + * @param integer $offset OPTIONAL + * @throws AdapterException + * @return string + */ + public function limit($sql, $count, $offset = 0) + { + $count = intval($count); + if ($count < 0) { + throw new AdapterException("LIMIT argument count=$count is not valid"); + } elseif ($count == 0) { + $limit_sql = str_ireplace("SELECT", "SELECT * FROM (SELECT", $sql); + $limit_sql .= ") WHERE 0 = 1"; + } else { + $offset = intval($offset); + if ($offset < 0) { + throw new AdapterException("LIMIT argument offset=$offset is not valid"); + } + if ($offset == 0) { + $limit_sql = str_ireplace("SELECT", "SELECT FIRST $count", $sql); + } else { + $limit_sql = str_ireplace("SELECT", "SELECT SKIP $offset LIMIT $count", $sql); + } + } + return $limit_sql; + } + + /** + * IDS-specific last sequence id + * + * @param string $sequenceName + * @return integer + */ + public function lastSequenceId($sequenceName) + { + $sql = 'SELECT '.$this->_adapter->quoteIdentifier($sequenceName).'.CURRVAL FROM ' + .'systables WHERE tabid = 1'; + $value = $this->_adapter->fetchOne($sql); + return $value; + } + + /** + * IDS-specific sequence id value + * + * @param string $sequenceName + * @return integer + */ + public function nextSequenceId($sequenceName) + { + $sql = 'SELECT '.$this->_adapter->quoteIdentifier($sequenceName).'.NEXTVAL FROM ' + .'systables WHERE tabid = 1'; + $value = $this->_adapter->fetchOne($sql); + return $value; + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Pdo/Mssql.php b/vendor/gipfl/zfdb/src/Adapter/Pdo/Mssql.php new file mode 100644 index 0000000..a75097c --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Pdo/Mssql.php @@ -0,0 +1,384 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter\Pdo; + +use gipfl\ZfDb\Adapter\Exception\AdapterException; +use gipfl\ZfDb\Db; +use PDOException; + +/** + * Class for connecting to Microsoft SQL Server databases and performing common operations. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Mssql extends PdoAdapter +{ + /** + * PDO type. + * + * @var string + */ + protected $_pdoType = 'mssql'; + + /** + * Keys are UPPERCASE SQL datatypes or the constants + * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE. + * + * Values are: + * 0 = 32-bit integer + * 1 = 64-bit integer + * 2 = float or decimal + * + * @var array Associative array of datatypes to values 0, 1, or 2. + */ + protected $_numericDataTypes = array( + Db::INT_TYPE => Db::INT_TYPE, + Db::BIGINT_TYPE => Db::BIGINT_TYPE, + Db::FLOAT_TYPE => Db::FLOAT_TYPE, + 'INT' => Db::INT_TYPE, + 'SMALLINT' => Db::INT_TYPE, + 'TINYINT' => Db::INT_TYPE, + 'BIGINT' => Db::BIGINT_TYPE, + 'DECIMAL' => Db::FLOAT_TYPE, + 'FLOAT' => Db::FLOAT_TYPE, + 'MONEY' => Db::FLOAT_TYPE, + 'NUMERIC' => Db::FLOAT_TYPE, + 'REAL' => Db::FLOAT_TYPE, + 'SMALLMONEY' => Db::FLOAT_TYPE + ); + + /** + * Creates a PDO DSN for the adapter from $this->_config settings. + * + * @return string + */ + protected function _dsn() + { + // baseline of DSN parts + $dsn = $this->_config; + + // don't pass the username and password in the DSN + unset($dsn['username']); + unset($dsn['password']); + unset($dsn['options']); + unset($dsn['persistent']); + unset($dsn['driver_options']); + + if (isset($dsn['port'])) { + $seperator = ':'; + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + $seperator = ','; + } + $dsn['host'] .= $seperator . $dsn['port']; + unset($dsn['port']); + } + + // this driver supports multiple DSN prefixes + // @see http://www.php.net/manual/en/ref.pdo-dblib.connection.php + if (isset($dsn['pdoType'])) { + switch (strtolower($dsn['pdoType'])) { + case 'freetds': + case 'sybase': + $this->_pdoType = 'sybase'; + break; + case 'mssql': + $this->_pdoType = 'mssql'; + break; + case 'dblib': + default: + $this->_pdoType = 'dblib'; + break; + } + unset($dsn['pdoType']); + } + + // use all remaining parts in the DSN + foreach ($dsn as $key => $val) { + $dsn[$key] = "$key=$val"; + } + + $dsn = $this->_pdoType . ':' . implode(';', $dsn); + return $dsn; + } + + /** + * @return void + */ + protected function _connect() + { + if ($this->_connection) { + return; + } + parent::_connect(); + $this->_connection->exec('SET QUOTED_IDENTIFIER ON'); + } + + /** + * Begin a transaction. + * + * It is necessary to override the abstract PDO transaction functions here, as + * the PDO driver for MSSQL does not support transactions. + */ + protected function _beginTransaction() + { + $this->_connect(); + $this->_connection->exec('BEGIN TRANSACTION'); + return true; + } + + /** + * Commit a transaction. + * + * It is necessary to override the abstract PDO transaction functions here, as + * the PDO driver for MSSQL does not support transactions. + */ + protected function _commit() + { + $this->_connect(); + $this->_connection->exec('COMMIT TRANSACTION'); + return true; + } + + /** + * Roll-back a transaction. + * + * It is necessary to override the abstract PDO transaction functions here, as + * the PDO driver for MSSQL does not support transactions. + */ + protected function _rollBack() + { + $this->_connect(); + $this->_connection->exec('ROLLBACK TRANSACTION'); + return true; + } + + /** + * Returns a list of the tables in the database. + * + * @return array + */ + public function listTables() + { + $sql = "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name"; + return $this->fetchCol($sql); + } + + /** + * Returns the column descriptions for a table. + * + * The return value is an associative array keyed by the column name, + * as returned by the RDBMS. + * + * The value of each array element is an associative array + * with the following keys: + * + * SCHEMA_NAME => string; name of database or schema + * TABLE_NAME => string; + * COLUMN_NAME => string; column name + * COLUMN_POSITION => number; ordinal position of column in table + * DATA_TYPE => string; SQL datatype name of column + * DEFAULT => string; default expression of column, null if none + * NULLABLE => boolean; true if column can have nulls + * LENGTH => number; length of CHAR/VARCHAR + * SCALE => number; scale of NUMERIC/DECIMAL + * PRECISION => number; precision of NUMERIC/DECIMAL + * UNSIGNED => boolean; unsigned property of an integer type + * PRIMARY => boolean; true if column is part of the primary key + * PRIMARY_POSITION => integer; position of column in primary key + * PRIMARY_AUTO => integer; position of auto-generated column in primary key + * + * @todo Discover column primary key position. + * @todo Discover integer unsigned property. + * + * @param string $tableName + * @param string $schemaName OPTIONAL + * @return array + */ + public function describeTable($tableName, $schemaName = null) + { + if ($schemaName != null) { + if (strpos($schemaName, '.') !== false) { + $result = explode('.', $schemaName); + $schemaName = $result[1]; + } + } + /** + * Discover metadata information about this table. + */ + $sql = "exec sp_columns @table_name = " . $this->quoteIdentifier($tableName, true); + if ($schemaName != null) { + $sql .= ", @table_owner = " . $this->quoteIdentifier($schemaName, true); + } + + $stmt = $this->query($sql); + $result = $stmt->fetchAll(Db::FETCH_NUM); + + $table_name = 2; + $column_name = 3; + $type_name = 5; + $precision = 6; + $length = 7; + $scale = 8; + $nullable = 10; + $column_def = 12; + $column_position = 16; + + /** + * Discover primary key column(s) for this table. + */ + $sql = "exec sp_pkeys @table_name = " . $this->quoteIdentifier($tableName, true); + if ($schemaName != null) { + $sql .= ", @table_owner = " . $this->quoteIdentifier($schemaName, true); + } + + $stmt = $this->query($sql); + $primaryKeysResult = $stmt->fetchAll(Db::FETCH_NUM); + $primaryKeyColumn = array(); + $pkey_column_name = 3; + $pkey_key_seq = 4; + foreach ($primaryKeysResult as $pkeysRow) { + $primaryKeyColumn[$pkeysRow[$pkey_column_name]] = $pkeysRow[$pkey_key_seq]; + } + + $desc = array(); + $p = 1; + foreach ($result as $key => $row) { + $identity = false; + $words = explode(' ', $row[$type_name], 2); + if (isset($words[0])) { + $type = $words[0]; + if (isset($words[1])) { + $identity = (bool) preg_match('/identity/', $words[1]); + } + } + + $isPrimary = array_key_exists($row[$column_name], $primaryKeyColumn); + if ($isPrimary) { + $primaryPosition = $primaryKeyColumn[$row[$column_name]]; + } else { + $primaryPosition = null; + } + + $desc[$this->foldCase($row[$column_name])] = array( + 'SCHEMA_NAME' => null, // @todo + 'TABLE_NAME' => $this->foldCase($row[$table_name]), + 'COLUMN_NAME' => $this->foldCase($row[$column_name]), + 'COLUMN_POSITION' => (int) $row[$column_position], + 'DATA_TYPE' => $type, + 'DEFAULT' => $row[$column_def], + 'NULLABLE' => (bool) $row[$nullable], + 'LENGTH' => $row[$length], + 'SCALE' => $row[$scale], + 'PRECISION' => $row[$precision], + 'UNSIGNED' => null, // @todo + 'PRIMARY' => $isPrimary, + 'PRIMARY_POSITION' => $primaryPosition, + 'IDENTITY' => $identity + ); + } + return $desc; + } + + /** + * Adds an adapter-specific LIMIT clause to the SELECT statement. + * + * @link http://lists.bestpractical.com/pipermail/rt-devel/2005-June/007339.html + * + * @param string $sql + * @param integer $count + * @param integer $offset OPTIONAL + * @throws AdapterException + * @return string + */ + public function limit($sql, $count, $offset = 0) + { + $count = intval($count); + if ($count <= 0) { + throw new AdapterException("LIMIT argument count=$count is not valid"); + } + + $offset = intval($offset); + if ($offset < 0) { + throw new AdapterException("LIMIT argument offset=$offset is not valid"); + } + + $sql .= " OFFSET $offset ROWS FETCH NEXT $count ROWS ONLY"; + + return $sql; + } + + /** + * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column. + * + * As a convention, on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence + * from the arguments and returns the last id generated by that sequence. + * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method + * returns the last value generated for such a column, and the table name + * argument is disregarded. + * + * Microsoft SQL Server does not support sequences, so the arguments to + * this method are ignored. + * + * @param string $tableName OPTIONAL Name of table. + * @param string $primaryKey OPTIONAL Name of primary key column. + * @return string + * @throws AdapterException + */ + public function lastInsertId($tableName = null, $primaryKey = null) + { + $sql = 'SELECT SCOPE_IDENTITY()'; + return (int)$this->fetchOne($sql); + } + + /** + * Retrieve server version in PHP style + * Pdo_Mssql doesn't support getAttribute(PDO::ATTR_SERVER_VERSION) + * @return string + */ + public function getServerVersion() + { + try { + $stmt = $this->query("SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR)"); + $result = $stmt->fetchAll(Db::FETCH_NUM); + if (count($result)) { + return $result[0][0]; + } + return null; + } catch (PDOException $e) { + return null; + } + } + + /** + * Quote a raw string. + * + * @param string $value Raw string + * @return string Quoted string + */ + protected function _quote($value) + { + if (!is_int($value) && !is_float($value)) { + // Fix for null-byte injection + $value = addcslashes($value, "\000\032"); + } + return parent::_quote($value); + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Pdo/Mysql.php b/vendor/gipfl/zfdb/src/Adapter/Pdo/Mysql.php new file mode 100644 index 0000000..a84ae84 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Pdo/Mysql.php @@ -0,0 +1,258 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter\Pdo; + +use gipfl\ZfDb\Adapter\Exception\AdapterException; +use gipfl\ZfDb\Db; + +/** + * Class for connecting to MySQL databases and performing common operations. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Mysql extends PdoAdapter +{ + + /** + * PDO type. + * + * @var string + */ + protected $_pdoType = 'mysql'; + + /** + * Keys are UPPERCASE SQL datatypes or the constants + * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE. + * + * Values are: + * 0 = 32-bit integer + * 1 = 64-bit integer + * 2 = float or decimal + * + * @var array Associative array of datatypes to values 0, 1, or 2. + */ + protected $_numericDataTypes = array( + Db::INT_TYPE => Db::INT_TYPE, + Db::BIGINT_TYPE => Db::BIGINT_TYPE, + Db::FLOAT_TYPE => Db::FLOAT_TYPE, + 'INT' => Db::INT_TYPE, + 'INTEGER' => Db::INT_TYPE, + 'MEDIUMINT' => Db::INT_TYPE, + 'SMALLINT' => Db::INT_TYPE, + 'TINYINT' => Db::INT_TYPE, + 'BIGINT' => Db::BIGINT_TYPE, + 'SERIAL' => Db::BIGINT_TYPE, + 'DEC' => Db::FLOAT_TYPE, + 'DECIMAL' => Db::FLOAT_TYPE, + 'DOUBLE' => Db::FLOAT_TYPE, + 'DOUBLE PRECISION' => Db::FLOAT_TYPE, + 'FIXED' => Db::FLOAT_TYPE, + 'FLOAT' => Db::FLOAT_TYPE + ); + + /** + * Override _dsn() and ensure that charset is incorporated in mysql + * @see PdoAdapter::_dsn() + */ + protected function _dsn() + { + $dsn = parent::_dsn(); + if (isset($this->_config['charset'])) { + $dsn .= ';charset=' . $this->_config['charset']; + } + return $dsn; + } + + /** + * Creates a PDO object and connects to the database. + * + * @return void + * @throws AdapterException + */ + protected function _connect() + { + if ($this->_connection) { + return; + } + + if (!empty($this->_config['charset']) + && version_compare(PHP_VERSION, '5.3.6', '<') + ) { + $initCommand = "SET NAMES '" . $this->_config['charset'] . "'"; + $this->_config['driver_options'][1002] = $initCommand; // 1002 = PDO::MYSQL_ATTR_INIT_COMMAND + } + + parent::_connect(); + } + + /** + * @return string + */ + public function getQuoteIdentifierSymbol() + { + return "`"; + } + + /** + * Returns a list of the tables in the database. + * + * @return array + */ + public function listTables() + { + return $this->fetchCol('SHOW TABLES'); + } + + /** + * Returns the column descriptions for a table. + * + * The return value is an associative array keyed by the column name, + * as returned by the RDBMS. + * + * The value of each array element is an associative array + * with the following keys: + * + * SCHEMA_NAME => string; name of database or schema + * TABLE_NAME => string; + * COLUMN_NAME => string; column name + * COLUMN_POSITION => number; ordinal position of column in table + * DATA_TYPE => string; SQL datatype name of column + * DEFAULT => string; default expression of column, null if none + * NULLABLE => boolean; true if column can have nulls + * LENGTH => number; length of CHAR/VARCHAR + * SCALE => number; scale of NUMERIC/DECIMAL + * PRECISION => number; precision of NUMERIC/DECIMAL + * UNSIGNED => boolean; unsigned property of an integer type + * PRIMARY => boolean; true if column is part of the primary key + * PRIMARY_POSITION => integer; position of column in primary key + * IDENTITY => integer; true if column is auto-generated with unique values + * + * @param string $tableName + * @param string $schemaName OPTIONAL + * @return array + */ + public function describeTable($tableName, $schemaName = null) + { + // @todo use INFORMATION_SCHEMA someday when MySQL's + // implementation has reasonably good performance and + // the version with this improvement is in wide use. + + if ($schemaName) { + $sql = 'DESCRIBE ' . $this->quoteIdentifier("$schemaName.$tableName", true); + } else { + $sql = 'DESCRIBE ' . $this->quoteIdentifier($tableName, true); + } + $stmt = $this->query($sql); + + // Use FETCH_NUM so we are not dependent on the CASE attribute of the PDO connection + $result = $stmt->fetchAll(Db::FETCH_NUM); + + $field = 0; + $type = 1; + $null = 2; + $key = 3; + $default = 4; + $extra = 5; + + $desc = array(); + $i = 1; + $p = 1; + foreach ($result as $row) { + list($length, $scale, $precision, $unsigned, $primary, $primaryPosition, $identity) + = array(null, null, null, null, false, null, false); + if (preg_match('/unsigned/', $row[$type])) { + $unsigned = true; + } + if (preg_match('/^((?:var)?char)\((\d+)\)/', $row[$type], $matches)) { + $row[$type] = $matches[1]; + $length = $matches[2]; + } elseif (preg_match('/^decimal\((\d+),(\d+)\)/', $row[$type], $matches)) { + $row[$type] = 'decimal'; + $precision = $matches[1]; + $scale = $matches[2]; + } elseif (preg_match('/^float\((\d+),(\d+)\)/', $row[$type], $matches)) { + $row[$type] = 'float'; + $precision = $matches[1]; + $scale = $matches[2]; + } elseif (preg_match('/^((?:big|medium|small|tiny)?int)\((\d+)\)/', $row[$type], $matches)) { + $row[$type] = $matches[1]; + // The optional argument of a MySQL int type is not precision + // or length; it is only a hint for display width. + } + if (strtoupper($row[$key]) == 'PRI') { + $primary = true; + $primaryPosition = $p; + if ($row[$extra] == 'auto_increment') { + $identity = true; + } else { + $identity = false; + } + ++$p; + } + $desc[$this->foldCase($row[$field])] = array( + 'SCHEMA_NAME' => null, // @todo + 'TABLE_NAME' => $this->foldCase($tableName), + 'COLUMN_NAME' => $this->foldCase($row[$field]), + 'COLUMN_POSITION' => $i, + 'DATA_TYPE' => $row[$type], + 'DEFAULT' => $row[$default], + 'NULLABLE' => (bool) ($row[$null] == 'YES'), + 'LENGTH' => $length, + 'SCALE' => $scale, + 'PRECISION' => $precision, + 'UNSIGNED' => $unsigned, + 'PRIMARY' => $primary, + 'PRIMARY_POSITION' => $primaryPosition, + 'IDENTITY' => $identity + ); + ++$i; + } + return $desc; + } + + /** + * Adds an adapter-specific LIMIT clause to the SELECT statement. + * + * @param string $sql + * @param integer $count + * @param integer $offset OPTIONAL + * @throws AdapterException + * @return string + */ + public function limit($sql, $count, $offset = 0) + { + $count = intval($count); + if ($count <= 0) { + throw new AdapterException("LIMIT argument count=$count is not valid"); + } + + $offset = intval($offset); + if ($offset < 0) { + throw new AdapterException("LIMIT argument offset=$offset is not valid"); + } + + $sql .= " LIMIT $count"; + if ($offset > 0) { + $sql .= " OFFSET $offset"; + } + + return $sql; + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Pdo/Oci.php b/vendor/gipfl/zfdb/src/Adapter/Pdo/Oci.php new file mode 100644 index 0000000..b76af36 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Pdo/Oci.php @@ -0,0 +1,367 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter\Pdo; + +use gipfl\ZfDb\Adapter\Exception\AdapterException; +use gipfl\ZfDb\Adapter\Exception\AdapterExceptionOracle; +use gipfl\ZfDb\Db; +use gipfl\ZfDb\Expr; + +/** + * Class for connecting to Oracle databases and performing common operations. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Oci extends PdoAdapter +{ + /** + * PDO type. + * + * @var string + */ + protected $_pdoType = 'oci'; + + /** + * Default class name for a DB statement. + * + * @var string + */ + protected $_defaultStmtClass = 'Zend_Db_Statement_Pdo_Oci'; + + /** + * Keys are UPPERCASE SQL datatypes or the constants + * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE. + * + * Values are: + * 0 = 32-bit integer + * 1 = 64-bit integer + * 2 = float or decimal + * + * @var array Associative array of datatypes to values 0, 1, or 2. + */ + protected $_numericDataTypes = array( + Db::INT_TYPE => Db::INT_TYPE, + Db::BIGINT_TYPE => Db::BIGINT_TYPE, + Db::FLOAT_TYPE => Db::FLOAT_TYPE, + 'BINARY_DOUBLE' => Db::FLOAT_TYPE, + 'BINARY_FLOAT' => Db::FLOAT_TYPE, + 'NUMBER' => Db::FLOAT_TYPE + ); + + /** + * Creates a PDO DSN for the adapter from $this->_config settings. + * + * @return string + */ + protected function _dsn() + { + // baseline of DSN parts + $dsn = $this->_config; + + if (isset($dsn['host'])) { + $tns = 'dbname=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)' . + '(HOST=' . $dsn['host'] . ')'; + + if (isset($dsn['port'])) { + $tns .= '(PORT=' . $dsn['port'] . ')'; + } else { + $tns .= '(PORT=1521)'; + } + + $tns .= '))(CONNECT_DATA=(SID=' . $dsn['dbname'] . ')))'; + } else { + $tns = 'dbname=' . $dsn['dbname']; + } + + if (isset($dsn['charset'])) { + $tns .= ';charset=' . $dsn['charset']; + } + + return $this->_pdoType . ':' . $tns; + } + + /** + * Quote a raw string. + * Most PDO drivers have an implementation for the quote() method, + * but the Oracle OCI driver must use the same implementation as the + * Zend_Db_Adapter_Abstract class. + * + * @param string $value Raw string + * @return string Quoted string + */ + protected function _quote($value) + { + if (is_int($value) || is_float($value)) { + return $value; + } + $value = str_replace("'", "''", $value); + return "'" . addcslashes($value, "\000\n\r\\\032") . "'"; + } + + /** + * Quote a table identifier and alias. + * + * @param string|array|Expr $ident The identifier or expression. + * @param string $alias An alias for the table. + * @return string The quoted identifier and alias. + */ + public function quoteTableAs($ident, $alias = null, $auto = false) + { + // Oracle doesn't allow the 'AS' keyword between the table identifier/expression and alias. + return $this->_quoteIdentifierAs($ident, $alias, $auto, ' '); + } + + /** + * Returns a list of the tables in the database. + * + * @return array + */ + public function listTables() + { + $data = $this->fetchCol('SELECT table_name FROM all_tables'); + return $data; + } + + /** + * Returns the column descriptions for a table. + * + * The return value is an associative array keyed by the column name, + * as returned by the RDBMS. + * + * The value of each array element is an associative array + * with the following keys: + * + * SCHEMA_NAME => string; name of schema + * TABLE_NAME => string; + * COLUMN_NAME => string; column name + * COLUMN_POSITION => number; ordinal position of column in table + * DATA_TYPE => string; SQL datatype name of column + * DEFAULT => string; default expression of column, null if none + * NULLABLE => boolean; true if column can have nulls + * LENGTH => number; length of CHAR/VARCHAR + * SCALE => number; scale of NUMERIC/DECIMAL + * PRECISION => number; precision of NUMERIC/DECIMAL + * UNSIGNED => boolean; unsigned property of an integer type + * PRIMARY => boolean; true if column is part of the primary key + * PRIMARY_POSITION => integer; position of column in primary key + * IDENTITY => integer; true if column is auto-generated with unique values + * + * @todo Discover integer unsigned property. + * + * @param string $tableName + * @param string $schemaName OPTIONAL + * @return array + */ + public function describeTable($tableName, $schemaName = null) + { + $version = $this->getServerVersion(); + if (($version === null) || version_compare($version, '9.0.0', '>=')) { + $sql = "SELECT TC.TABLE_NAME, TC.OWNER, TC.COLUMN_NAME, TC.DATA_TYPE, + TC.DATA_DEFAULT, TC.NULLABLE, TC.COLUMN_ID, TC.DATA_LENGTH, + TC.DATA_SCALE, TC.DATA_PRECISION, C.CONSTRAINT_TYPE, CC.POSITION + FROM ALL_TAB_COLUMNS TC + LEFT JOIN (ALL_CONS_COLUMNS CC JOIN ALL_CONSTRAINTS C + ON (CC.CONSTRAINT_NAME = C.CONSTRAINT_NAME AND CC.TABLE_NAME = C.TABLE_NAME + AND CC.OWNER = C.OWNER AND C.CONSTRAINT_TYPE = 'P')) + ON TC.TABLE_NAME = CC.TABLE_NAME AND TC.COLUMN_NAME = CC.COLUMN_NAME + WHERE UPPER(TC.TABLE_NAME) = UPPER(:TBNAME)"; + $bind[':TBNAME'] = $tableName; + if ($schemaName) { + $sql .= ' AND UPPER(TC.OWNER) = UPPER(:SCNAME)'; + $bind[':SCNAME'] = $schemaName; + } + $sql .= ' ORDER BY TC.COLUMN_ID'; + } else { + $subSql="SELECT AC.OWNER, AC.TABLE_NAME, ACC.COLUMN_NAME, AC.CONSTRAINT_TYPE, ACC.POSITION + from ALL_CONSTRAINTS AC, ALL_CONS_COLUMNS ACC + WHERE ACC.CONSTRAINT_NAME = AC.CONSTRAINT_NAME + AND ACC.TABLE_NAME = AC.TABLE_NAME + AND ACC.OWNER = AC.OWNER + AND AC.CONSTRAINT_TYPE = 'P' + AND UPPER(AC.TABLE_NAME) = UPPER(:TBNAME)"; + $bind[':TBNAME'] = $tableName; + if ($schemaName) { + $subSql .= ' AND UPPER(ACC.OWNER) = UPPER(:SCNAME)'; + $bind[':SCNAME'] = $schemaName; + } + $sql="SELECT TC.TABLE_NAME, TC.OWNER, TC.COLUMN_NAME, TC.DATA_TYPE, + TC.DATA_DEFAULT, TC.NULLABLE, TC.COLUMN_ID, TC.DATA_LENGTH, + TC.DATA_SCALE, TC.DATA_PRECISION, CC.CONSTRAINT_TYPE, CC.POSITION + FROM ALL_TAB_COLUMNS TC, ($subSql) CC + WHERE UPPER(TC.TABLE_NAME) = UPPER(:TBNAME) + AND TC.OWNER = CC.OWNER(+) AND TC.TABLE_NAME = CC.TABLE_NAME(+) + AND TC.COLUMN_NAME = CC.COLUMN_NAME(+)"; + if ($schemaName) { + $sql .= ' AND UPPER(TC.OWNER) = UPPER(:SCNAME)'; + } + $sql .= ' ORDER BY TC.COLUMN_ID'; + } + + $stmt = $this->query($sql, $bind); + + /** + * Use FETCH_NUM so we are not dependent on the CASE attribute of the PDO connection + */ + $result = $stmt->fetchAll(Db::FETCH_NUM); + + $table_name = 0; + $owner = 1; + $column_name = 2; + $data_type = 3; + $data_default = 4; + $nullable = 5; + $column_id = 6; + $data_length = 7; + $data_scale = 8; + $data_precision = 9; + $constraint_type = 10; + $position = 11; + + $desc = array(); + foreach ($result as $key => $row) { + list ($primary, $primaryPosition, $identity) = array(false, null, false); + if ($row[$constraint_type] == 'P') { + $primary = true; + $primaryPosition = $row[$position]; + /** + * Oracle does not support auto-increment keys. + */ + $identity = false; + } + $desc[$this->foldCase($row[$column_name])] = array( + 'SCHEMA_NAME' => $this->foldCase($row[$owner]), + 'TABLE_NAME' => $this->foldCase($row[$table_name]), + 'COLUMN_NAME' => $this->foldCase($row[$column_name]), + 'COLUMN_POSITION' => $row[$column_id], + 'DATA_TYPE' => $row[$data_type], + 'DEFAULT' => $row[$data_default], + 'NULLABLE' => (bool) ($row[$nullable] == 'Y'), + 'LENGTH' => $row[$data_length], + 'SCALE' => $row[$data_scale], + 'PRECISION' => $row[$data_precision], + 'UNSIGNED' => null, // @todo + 'PRIMARY' => $primary, + 'PRIMARY_POSITION' => $primaryPosition, + 'IDENTITY' => $identity + ); + } + return $desc; + } + + /** + * Return the most recent value from the specified sequence in the database. + * This is supported only on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null. + * + * @param string $sequenceName + * @return integer + */ + public function lastSequenceId($sequenceName) + { + $this->_connect(); + $value = $this->fetchOne('SELECT '.$this->quoteIdentifier($sequenceName, true).'.CURRVAL FROM dual'); + return $value; + } + + /** + * Generate a new value from the specified sequence in the database, and return it. + * This is supported only on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null. + * + * @param string $sequenceName + * @return integer + */ + public function nextSequenceId($sequenceName) + { + $this->_connect(); + $value = $this->fetchOne('SELECT '.$this->quoteIdentifier($sequenceName, true).'.NEXTVAL FROM dual'); + return $value; + } + + /** + * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column. + * + * As a convention, on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence + * from the arguments and returns the last id generated by that sequence. + * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method + * returns the last value generated for such a column, and the table name + * argument is disregarded. + * + * Oracle does not support IDENTITY columns, so if the sequence is not + * specified, this method returns null. + * + * @param string $tableName OPTIONAL Name of table. + * @param string $primaryKey OPTIONAL Name of primary key column. + * @return string + * @throws AdapterExceptionOracle + */ + public function lastInsertId($tableName = null, $primaryKey = null) + { + if ($tableName !== null) { + $sequenceName = $tableName; + if ($primaryKey) { + $sequenceName .= $this->foldCase("_$primaryKey"); + } + $sequenceName .= $this->foldCase('_seq'); + return $this->lastSequenceId($sequenceName); + } + // No support for IDENTITY columns; return null + return null; + } + + /** + * Adds an adapter-specific LIMIT clause to the SELECT statement. + * + * @param string $sql + * @param integer $count + * @param integer $offset + * @throws AdapterException + * @return string + */ + public function limit($sql, $count, $offset = 0) + { + $count = intval($count); + if ($count <= 0) { + throw new AdapterException("LIMIT argument count=$count is not valid"); + } + + $offset = intval($offset); + if ($offset < 0) { + throw new AdapterException("LIMIT argument offset=$offset is not valid"); + } + + /** + * Oracle does not implement the LIMIT clause as some RDBMS do. + * We have to simulate it with subqueries and ROWNUM. + * Unfortunately because we use the column wildcard "*", + * this puts an extra column into the query result set. + */ + $limit_sql = "SELECT z2.* + FROM ( + SELECT z1.*, ROWNUM AS \"zend_db_rownum\" + FROM ( + " . $sql . " + ) z1 + ) z2 + WHERE z2.\"zend_db_rownum\" BETWEEN " . ($offset+1) . " AND " . ($offset+$count); + return $limit_sql; + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Pdo/PdoAdapter.php b/vendor/gipfl/zfdb/src/Adapter/Pdo/PdoAdapter.php new file mode 100644 index 0000000..8d84e5d --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Pdo/PdoAdapter.php @@ -0,0 +1,367 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter\Pdo; + +use gipfl\ZfDb\Adapter\Adapter; +use gipfl\ZfDb\Adapter\Exception\AdapterException; +use gipfl\ZfDb\Profiler; +use gipfl\ZfDb\Select; +use gipfl\ZfDb\Statement\PdoStatement; +use gipfl\ZfDb\Statement\Exception\StatementException; +use PDO; +use PDOException; + +/** + * Class for connecting to SQL databases and performing common operations using PDO. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +abstract class PdoAdapter extends Adapter +{ + /** + * Default class name for a DB statement. + * + * @var string + */ + protected $_defaultStmtClass = PdoStatement::class; + + /** + * Creates a PDO DSN for the adapter from $this->_config settings. + * + * @return string + */ + protected function _dsn() + { + // baseline of DSN parts + $dsn = $this->_config; + + // don't pass the username, password, charset, persistent and driver_options in the DSN + unset($dsn['username']); + unset($dsn['password']); + unset($dsn['options']); + unset($dsn['charset']); + unset($dsn['persistent']); + unset($dsn['driver_options']); + + // use all remaining parts in the DSN + foreach ($dsn as $key => $val) { + $dsn[$key] = "$key=$val"; + } + + return $this->_pdoType . ':' . implode(';', $dsn); + } + + /** + * Creates a PDO object and connects to the database. + * + * @return void + * @throws AdapterException + */ + protected function _connect() + { + // if we already have a PDO object, no need to re-connect. + if ($this->_connection) { + return; + } + + // get the dsn first, because some adapters alter the $_pdoType + $dsn = $this->_dsn(); + + // check for PDO extension + if (!extension_loaded('pdo')) { + throw new AdapterException( + 'The PDO extension is required for this adapter but the extension is not loaded' + ); + } + + // check the PDO driver is available + if (!in_array($this->_pdoType, PDO::getAvailableDrivers())) { + throw new AdapterException('The ' . $this->_pdoType . ' driver is not currently installed'); + } + + // create PDO connection + $q = $this->_profiler->queryStart('connect', Profiler::CONNECT); + + // add the persistence flag if we find it in our config array + if (isset($this->_config['persistent']) && ($this->_config['persistent'] == true)) { + $this->_config['driver_options'][PDO::ATTR_PERSISTENT] = true; + } + + try { + $this->_connection = new PDO( + $dsn, + $this->_config['username'], + $this->_config['password'], + $this->_config['driver_options'] + ); + + $this->_profiler->queryEnd($q); + + // set the PDO connection to perform case-folding on array keys, or not + $this->_connection->setAttribute(PDO::ATTR_CASE, $this->_caseFolding); + + // always use exceptions. + $this->_connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } catch (PDOException $e) { + $message = $e->getMessage(); + if ($e->getPrevious() !== null && preg_match('~^SQLSTATE\[HY000\] \[\d{1,4}\]\s$~', $message)) { + // See https://bugs.php.net/bug.php?id=76604 + $message .= $e->getPrevious()->getMessage(); + } + + throw new AdapterException($message, $e->getCode(), $e); + } + } + + /** + * Test if a connection is active + * + * @return boolean + */ + public function isConnected() + { + return ((bool) ($this->_connection instanceof PDO)); + } + + /** + * Force the connection to close. + * + * @return void + */ + public function closeConnection() + { + $this->_connection = null; + } + + /** + * Prepares an SQL statement. + * + * @param string $sql The SQL statement with placeholders. + * @param array $bind An array of data to bind to the placeholders. + * @return PdoStatement + */ + public function prepare($sql) + { + $this->_connect(); + $stmtClass = $this->_defaultStmtClass; + $stmt = new $stmtClass($this, $sql); + $stmt->setFetchMode($this->_fetchMode); + return $stmt; + } + + /** + * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column. + * + * As a convention, on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence + * from the arguments and returns the last id generated by that sequence. + * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method + * returns the last value generated for such a column, and the table name + * argument is disregarded. + * + * On RDBMS brands that don't support sequences, $tableName and $primaryKey + * are ignored. + * + * @param string $tableName OPTIONAL Name of table. + * @param string $primaryKey OPTIONAL Name of primary key column. + * @return string + */ + public function lastInsertId($tableName = null, $primaryKey = null) + { + $this->_connect(); + return $this->_connection->lastInsertId(); + } + + /** + * Special handling for PDO query(). + * All bind parameter names must begin with ':' + * + * @param string|Select $sql The SQL statement with placeholders. + * @param array $bind An array of data to bind to the placeholders. + * @return Statement + * @throws AdapterException To re-throw PDOException. + */ + public function query($sql, $bind = array()) + { + if (empty($bind) && $sql instanceof Select) { + $bind = $sql->getBind(); + } + + if (is_array($bind)) { + foreach ($bind as $name => $value) { + if (!is_int($name) && !preg_match('/^:/', $name)) { + $newName = ":$name"; + unset($bind[$name]); + $bind[$newName] = $value; + } + } + } + + try { + return parent::query($sql, $bind); + } catch (PDOException $e) { + /** + * @see StatementException + */ + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Executes an SQL statement and return the number of affected rows + * + * @param mixed $sql The SQL statement with placeholders. + * May be a string or Zend_Db_Select. + * @return integer Number of rows that were modified + * or deleted by the SQL statement + */ + public function exec($sql) + { + if ($sql instanceof Select) { + $sql = $sql->assemble(); + } + + try { + $affected = $this->getConnection()->exec($sql); + + if ($affected === false) { + $errorInfo = $this->getConnection()->errorInfo(); + throw new AdapterException($errorInfo[2]); + } + + return $affected; + } catch (PDOException $e) { + $code = $e->getCode(); + $code = ctype_digit($code) ? (int) $code : 0; + throw new AdapterException($e->getMessage(), $code, $e); + } + } + + /** + * Quote a raw string. + * + * @param string $value Raw string + * @return string Quoted string + */ + protected function _quote($value) + { + if (is_int($value) || is_float($value)) { + return $value; + } + $this->_connect(); + return $this->_connection->quote($value); + } + + /** + * Begin a transaction. + */ + protected function _beginTransaction() + { + $this->_connect(); + $this->_connection->beginTransaction(); + } + + /** + * Commit a transaction. + */ + protected function _commit() + { + $this->_connect(); + $this->_connection->commit(); + } + + /** + * Roll-back a transaction. + */ + protected function _rollBack() + { + $this->_connect(); + $this->_connection->rollBack(); + } + + /** + * Set the PDO fetch mode. + * + * @todo Support FETCH_CLASS and FETCH_INTO. + * + * @param int $mode A PDO fetch mode. + * @return void + * @throws AdapterException + */ + public function setFetchMode($mode) + { + //check for PDO extension + if (!extension_loaded('pdo')) { + throw new AdapterException( + 'The PDO extension is required for this adapter but the extension is not loaded' + ); + } + switch ($mode) { + case PDO::FETCH_LAZY: + case PDO::FETCH_ASSOC: + case PDO::FETCH_NUM: + case PDO::FETCH_BOTH: + case PDO::FETCH_NAMED: + case PDO::FETCH_OBJ: + $this->_fetchMode = $mode; + break; + default: + throw new AdapterException("Invalid fetch mode '$mode' specified"); + } + } + + /** + * Check if the adapter supports real SQL parameters. + * + * @param string $type 'positional' or 'named' + * @return bool + */ + public function supportsParameters($type) + { + switch ($type) { + case 'positional': + case 'named': + default: + return true; + } + } + + /** + * Retrieve server version in PHP style + * + * @return string + */ + public function getServerVersion() + { + $this->_connect(); + try { + $version = $this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION); + } catch (PDOException $e) { + // In case of the driver doesn't support getting attributes + return null; + } + $matches = null; + if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $version, $matches)) { + return $matches[1]; + } else { + return null; + } + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Pdo/Pgsql.php b/vendor/gipfl/zfdb/src/Adapter/Pdo/Pgsql.php new file mode 100644 index 0000000..fc08d38 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Pdo/Pgsql.php @@ -0,0 +1,318 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter\Pdo; + +use gipfl\ZfDb\Adapter\Exception\AdapterException; +use gipfl\ZfDb\Db; + +/** + * Class for connecting to PostgreSQL databases and performing common operations. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Pgsql extends PdoAdapter +{ + + /** + * PDO type. + * + * @var string + */ + protected $_pdoType = 'pgsql'; + + /** + * Keys are UPPERCASE SQL datatypes or the constants + * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE. + * + * Values are: + * 0 = 32-bit integer + * 1 = 64-bit integer + * 2 = float or decimal + * + * @var array Associative array of datatypes to values 0, 1, or 2. + */ + protected $_numericDataTypes = array( + Db::INT_TYPE => Db::INT_TYPE, + Db::BIGINT_TYPE => Db::BIGINT_TYPE, + Db::FLOAT_TYPE => Db::FLOAT_TYPE, + 'INTEGER' => Db::INT_TYPE, + 'SERIAL' => Db::INT_TYPE, + 'SMALLINT' => Db::INT_TYPE, + 'BIGINT' => Db::BIGINT_TYPE, + 'BIGSERIAL' => Db::BIGINT_TYPE, + 'DECIMAL' => Db::FLOAT_TYPE, + 'DOUBLE PRECISION' => Db::FLOAT_TYPE, + 'NUMERIC' => Db::FLOAT_TYPE, + 'REAL' => Db::FLOAT_TYPE + ); + + /** + * Creates a PDO object and connects to the database. + * + * @return void + * @throws AdapterException + */ + protected function _connect() + { + if ($this->_connection) { + return; + } + + parent::_connect(); + + if (!empty($this->_config['charset'])) { + $sql = "SET NAMES '" . $this->_config['charset'] . "'"; + $this->_connection->exec($sql); + } + } + + /** + * Returns a list of the tables in the database. + * + * @return array + */ + public function listTables() + { + // @todo use a better query with joins instead of subqueries + $sql = "SELECT c.relname AS table_name " + . "FROM pg_class c, pg_user u " + . "WHERE c.relowner = u.usesysid AND c.relkind = 'r' " + . "AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) " + . "AND c.relname !~ '^(pg_|sql_)' " + . "UNION " + . "SELECT c.relname AS table_name " + . "FROM pg_class c " + . "WHERE c.relkind = 'r' " + . "AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) " + . "AND NOT EXISTS (SELECT 1 FROM pg_user WHERE usesysid = c.relowner) " + . "AND c.relname !~ '^pg_'"; + + return $this->fetchCol($sql); + } + + /** + * Returns the column descriptions for a table. + * + * The return value is an associative array keyed by the column name, + * as returned by the RDBMS. + * + * The value of each array element is an associative array + * with the following keys: + * + * SCHEMA_NAME => string; name of database or schema + * TABLE_NAME => string; + * COLUMN_NAME => string; column name + * COLUMN_POSITION => number; ordinal position of column in table + * DATA_TYPE => string; SQL datatype name of column + * DEFAULT => string; default expression of column, null if none + * NULLABLE => boolean; true if column can have nulls + * LENGTH => number; length of CHAR/VARCHAR + * SCALE => number; scale of NUMERIC/DECIMAL + * PRECISION => number; precision of NUMERIC/DECIMAL + * UNSIGNED => boolean; unsigned property of an integer type + * PRIMARY => boolean; true if column is part of the primary key + * PRIMARY_POSITION => integer; position of column in primary key + * IDENTITY => integer; true if column is auto-generated with unique values + * + * @todo Discover integer unsigned property. + * + * @param string $tableName + * @param string $schemaName OPTIONAL + * @return array + */ + public function describeTable($tableName, $schemaName = null) + { + $sql = "SELECT + a.attnum, + n.nspname, + c.relname, + a.attname AS colname, + t.typname AS type, + a.atttypmod, + FORMAT_TYPE(a.atttypid, a.atttypmod) AS complete_type, + d.adsrc AS default_value, + a.attnotnull AS notnull, + a.attlen AS length, + co.contype, + ARRAY_TO_STRING(co.conkey, ',') AS conkey + FROM pg_attribute AS a + JOIN pg_class AS c ON a.attrelid = c.oid + JOIN pg_namespace AS n ON c.relnamespace = n.oid + JOIN pg_type AS t ON a.atttypid = t.oid + LEFT OUTER JOIN pg_constraint AS co ON (co.conrelid = c.oid + AND a.attnum = ANY(co.conkey) AND co.contype = 'p') + LEFT OUTER JOIN pg_attrdef AS d ON d.adrelid = c.oid AND d.adnum = a.attnum + WHERE a.attnum > 0 AND c.relname = ".$this->quote($tableName); + if ($schemaName) { + $sql .= " AND n.nspname = ".$this->quote($schemaName); + } + $sql .= ' ORDER BY a.attnum'; + + $stmt = $this->query($sql); + + // Use FETCH_NUM so we are not dependent on the CASE attribute of the PDO connection + $result = $stmt->fetchAll(Db::FETCH_NUM); + + $attnum = 0; + $nspname = 1; + $relname = 2; + $colname = 3; + $type = 4; + $atttypemod = 5; + $complete_type = 6; + $default_value = 7; + $notnull = 8; + $length = 9; + $contype = 10; + $conkey = 11; + + $desc = array(); + foreach ($result as $key => $row) { + $defaultValue = $row[$default_value]; + if ($row[$type] == 'varchar' || $row[$type] == 'bpchar') { + if (preg_match('/character(?: varying)?(?:\((\d+)\))?/', $row[$complete_type], $matches)) { + if (isset($matches[1])) { + $row[$length] = $matches[1]; + } else { + $row[$length] = null; // unlimited + } + } + if (preg_match("/^'(.*?)'::(?:character varying|bpchar)$/", $defaultValue, $matches)) { + $defaultValue = $matches[1]; + } + } + list($primary, $primaryPosition, $identity) = array(false, null, false); + if ($row[$contype] == 'p') { + $primary = true; + $primaryPosition = array_search($row[$attnum], explode(',', $row[$conkey])) + 1; + $identity = (bool) (preg_match('/^nextval/', $row[$default_value])); + } + $desc[$this->foldCase($row[$colname])] = array( + 'SCHEMA_NAME' => $this->foldCase($row[$nspname]), + 'TABLE_NAME' => $this->foldCase($row[$relname]), + 'COLUMN_NAME' => $this->foldCase($row[$colname]), + 'COLUMN_POSITION' => $row[$attnum], + 'DATA_TYPE' => $row[$type], + 'DEFAULT' => $defaultValue, + 'NULLABLE' => (bool) ($row[$notnull] != 't'), + 'LENGTH' => $row[$length], + 'SCALE' => null, // @todo + 'PRECISION' => null, // @todo + 'UNSIGNED' => null, // @todo + 'PRIMARY' => $primary, + 'PRIMARY_POSITION' => $primaryPosition, + 'IDENTITY' => $identity + ); + } + return $desc; + } + + + /** + * Adds an adapter-specific LIMIT clause to the SELECT statement. + * + * @param string $sql + * @param integer $count + * @param integer $offset OPTIONAL + * @return string + */ + public function limit($sql, $count, $offset = 0) + { + $count = intval($count); + if ($count <= 0) { + throw new AdapterException("LIMIT argument count=$count is not valid"); + } + + $offset = intval($offset); + if ($offset < 0) { + throw new AdapterException("LIMIT argument offset=$offset is not valid"); + } + + $sql .= " LIMIT $count"; + if ($offset > 0) { + $sql .= " OFFSET $offset"; + } + + return $sql; + } + + /** + * Return the most recent value from the specified sequence in the database. + * This is supported only on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null. + * + * @param string $sequenceName + * @return string + */ + public function lastSequenceId($sequenceName) + { + $this->_connect(); + $sequenceName = str_replace($this->getQuoteIdentifierSymbol(), '', (string) $sequenceName); + $value = $this->fetchOne("SELECT CURRVAL(" + . $this->quote($this->quoteIdentifier($sequenceName, true)) + . ")"); + return $value; + } + + /** + * Generate a new value from the specified sequence in the database, and return it. + * This is supported only on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null. + * + * @param string $sequenceName + * @return string + */ + public function nextSequenceId($sequenceName) + { + $this->_connect(); + $sequenceName = str_replace($this->getQuoteIdentifierSymbol(), '', (string) $sequenceName); + $value = $this->fetchOne("SELECT NEXTVAL(" + . $this->quote($this->quoteIdentifier($sequenceName, true)) + . ")"); + return $value; + } + + /** + * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column. + * + * As a convention, on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence + * from the arguments and returns the last id generated by that sequence. + * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method + * returns the last value generated for such a column, and the table name + * argument is disregarded. + * + * @param string $tableName OPTIONAL Name of table. + * @param string $primaryKey OPTIONAL Name of primary key column. + * @return string + */ + public function lastInsertId($tableName = null, $primaryKey = null) + { + if ($tableName !== null) { + $sequenceName = $tableName; + if ($primaryKey) { + $sequenceName .= "_$primaryKey"; + } + $sequenceName .= '_seq'; + return $this->lastSequenceId($sequenceName); + } + return $this->_connection->lastInsertId($tableName); + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Pdo/Sqlite.php b/vendor/gipfl/zfdb/src/Adapter/Pdo/Sqlite.php new file mode 100644 index 0000000..5a12912 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Pdo/Sqlite.php @@ -0,0 +1,293 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter\Pdo; + +use gipfl\ZfDb\Adapter\Exception\AdapterException; +use gipfl\ZfDb\Db; + +/** + * Class for connecting to SQLite2 and SQLite3 databases and performing common operations. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Sqlite extends PdoAdapter +{ + /** + * PDO type + * + * @var string + */ + protected $_pdoType = 'sqlite'; + + /** + * Keys are UPPERCASE SQL datatypes or the constants + * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE. + * + * Values are: + * 0 = 32-bit integer + * 1 = 64-bit integer + * 2 = float or decimal + * + * @var array Associative array of datatypes to values 0, 1, or 2. + */ + protected $_numericDataTypes = array( + Db::INT_TYPE => Db::INT_TYPE, + Db::BIGINT_TYPE => Db::BIGINT_TYPE, + Db::FLOAT_TYPE => Db::FLOAT_TYPE, + 'INTEGER' => Db::BIGINT_TYPE, + 'REAL' => Db::FLOAT_TYPE + ); + + /** + * Constructor. + * + * $config is an array of key/value pairs containing configuration + * options. Note that the SQLite options are different than most of + * the other PDO adapters in that no username or password are needed. + * Also, an extra config key "sqlite2" specifies compatibility mode. + * + * dbname => (string) The name of the database to user (required, + * use :memory: for memory-based database) + * + * sqlite2 => (boolean) PDO_SQLITE defaults to SQLite 3. For compatibility + * with an older SQLite 2 database, set this to TRUE. + * + * @param array $config An array of configuration keys. + */ + public function __construct(array $config = array()) + { + if (isset($config['sqlite2']) && $config['sqlite2']) { + $this->_pdoType = 'sqlite2'; + } + + // SQLite uses no username/password. Stub to satisfy parent::_connect() + $this->_config['username'] = null; + $this->_config['password'] = null; + + return parent::__construct($config); + } + + /** + * Check for config options that are mandatory. + * Throw exceptions if any are missing. + * + * @param array $config + * @throws AdapterException + */ + protected function _checkRequiredOptions(array $config) + { + // we need at least a dbname + if (! array_key_exists('dbname', $config)) { + throw new AdapterException( + "Configuration array must have a key for 'dbname' that names the database instance" + ); + } + } + + /** + * DSN builder + */ + protected function _dsn() + { + return $this->_pdoType .':'. $this->_config['dbname']; + } + + /** + * Special configuration for SQLite behavior: make sure that result sets + * contain keys like 'column' instead of 'table.column'. + * + * @throws AdapterException + */ + protected function _connect() + { + /** + * if we already have a PDO object, no need to re-connect. + */ + if ($this->_connection) { + return; + } + + parent::_connect(); + + $retval = $this->_connection->exec('PRAGMA full_column_names=0'); + if ($retval === false) { + $error = $this->_connection->errorInfo(); + throw new AdapterException($error[2]); + } + + $retval = $this->_connection->exec('PRAGMA short_column_names=1'); + if ($retval === false) { + $error = $this->_connection->errorInfo(); + throw new AdapterException($error[2]); + } + } + + /** + * Returns a list of the tables in the database. + * + * @return array + */ + public function listTables() + { + $sql = "SELECT name FROM sqlite_master WHERE type='table' " + . "UNION ALL SELECT name FROM sqlite_temp_master " + . "WHERE type='table' ORDER BY name"; + + return $this->fetchCol($sql); + } + + /** + * Returns the column descriptions for a table. + * + * The return value is an associative array keyed by the column name, + * as returned by the RDBMS. + * + * The value of each array element is an associative array + * with the following keys: + * + * SCHEMA_NAME => string; name of database or schema + * TABLE_NAME => string; + * COLUMN_NAME => string; column name + * COLUMN_POSITION => number; ordinal position of column in table + * DATA_TYPE => string; SQL datatype name of column + * DEFAULT => string; default expression of column, null if none + * NULLABLE => boolean; true if column can have nulls + * LENGTH => number; length of CHAR/VARCHAR + * SCALE => number; scale of NUMERIC/DECIMAL + * PRECISION => number; precision of NUMERIC/DECIMAL + * UNSIGNED => boolean; unsigned property of an integer type + * PRIMARY => boolean; true if column is part of the primary key + * PRIMARY_POSITION => integer; position of column in primary key + * IDENTITY => integer; true if column is auto-generated with unique values + * + * @param string $tableName + * @param string $schemaName OPTIONAL + * @return array + */ + public function describeTable($tableName, $schemaName = null) + { + $sql = 'PRAGMA '; + + if ($schemaName) { + $sql .= $this->quoteIdentifier($schemaName) . '.'; + } + + $sql .= 'table_info('.$this->quoteIdentifier($tableName).')'; + + $stmt = $this->query($sql); + + /** + * Use FETCH_NUM so we are not dependent on the CASE attribute of the PDO connection + */ + $result = $stmt->fetchAll(Db::FETCH_NUM); + + $cid = 0; + $name = 1; + $type = 2; + $notnull = 3; + $dflt_value = 4; + $pk = 5; + + $desc = array(); + + $p = 1; + foreach ($result as $key => $row) { + list($length, $scale, $precision, $primary, $primaryPosition, $identity) = + array(null, null, null, false, null, false); + if (preg_match('/^((?:var)?char)\((\d+)\)/i', $row[$type], $matches)) { + $row[$type] = $matches[1]; + $length = $matches[2]; + } elseif (preg_match('/^decimal\((\d+),(\d+)\)/i', $row[$type], $matches)) { + $row[$type] = 'DECIMAL'; + $precision = $matches[1]; + $scale = $matches[2]; + } + if ((bool) $row[$pk]) { + $primary = true; + $primaryPosition = $p; + /** + * SQLite INTEGER primary key is always auto-increment. + */ + $identity = (bool) ($row[$type] == 'INTEGER'); + ++$p; + } + $desc[$this->foldCase($row[$name])] = array( + 'SCHEMA_NAME' => $this->foldCase($schemaName), + 'TABLE_NAME' => $this->foldCase($tableName), + 'COLUMN_NAME' => $this->foldCase($row[$name]), + 'COLUMN_POSITION' => $row[$cid]+1, + 'DATA_TYPE' => $row[$type], + 'DEFAULT' => $row[$dflt_value], + 'NULLABLE' => ! (bool) $row[$notnull], + 'LENGTH' => $length, + 'SCALE' => $scale, + 'PRECISION' => $precision, + 'UNSIGNED' => null, // Sqlite3 does not support unsigned data + 'PRIMARY' => $primary, + 'PRIMARY_POSITION' => $primaryPosition, + 'IDENTITY' => $identity + ); + } + return $desc; + } + + /** + * Adds an adapter-specific LIMIT clause to the SELECT statement. + * + * @param string $sql + * @param integer $count + * @param integer $offset OPTIONAL + * @return string + */ + public function limit($sql, $count, $offset = 0) + { + $count = intval($count); + if ($count <= 0) { + throw new AdapterException("LIMIT argument count=$count is not valid"); + } + + $offset = intval($offset); + if ($offset < 0) { + throw new AdapterException("LIMIT argument offset=$offset is not valid"); + } + + $sql .= " LIMIT $count"; + if ($offset > 0) { + $sql .= " OFFSET $offset"; + } + + return $sql; + } + + /** + * Quote a raw string. + * + * @param string $value Raw string + * @return string Quoted string + */ + protected function _quote($value) + { + if (!is_int($value) && !is_float($value)) { + // Fix for null-byte injection + $value = addcslashes($value, "\000\032"); + } + return parent::_quote($value); + } +} diff --git a/vendor/gipfl/zfdb/src/Adapter/Sqlsrv.php b/vendor/gipfl/zfdb/src/Adapter/Sqlsrv.php new file mode 100644 index 0000000..c940a75 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Adapter/Sqlsrv.php @@ -0,0 +1,640 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Adapter; + +use gipfl\ZfDb\Adapter\Exception\AdapterException; +use gipfl\ZfDb\Adapter\Exception\AdapterExceptionSqlsrv; +use gipfl\ZfDb\Db; +use gipfl\ZfDb\Expr; +use gipfl\ZfDb\Statement\SqlsrvStatement; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Sqlsrv extends Adapter +{ + /** + * User-provided configuration. + * + * Basic keys are: + * + * username => (string) Connect to the database as this username. + * password => (string) Password associated with the username. + * dbname => The name of the local SQL Server instance + * + * @var array + */ + protected $_config = array( + 'dbname' => null, + 'username' => null, + 'password' => null, + ); + + /** + * Last insert id from INSERT query + * + * @var int + */ + protected $_lastInsertId; + + /** + * Query used to fetch last insert id + * + * @var string + */ + protected $_lastInsertSQL = 'SELECT SCOPE_IDENTITY() as Current_Identity'; + + /** + * Keys are UPPERCASE SQL datatypes or the constants + * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE. + * + * Values are: + * 0 = 32-bit integer + * 1 = 64-bit integer + * 2 = float or decimal + * + * @var array Associative array of datatypes to values 0, 1, or 2. + */ + protected $_numericDataTypes = array( + Db::INT_TYPE => Db::INT_TYPE, + Db::BIGINT_TYPE => Db::BIGINT_TYPE, + Db::FLOAT_TYPE => Db::FLOAT_TYPE, + 'INT' => Db::INT_TYPE, + 'SMALLINT' => Db::INT_TYPE, + 'TINYINT' => Db::INT_TYPE, + 'BIGINT' => Db::BIGINT_TYPE, + 'DECIMAL' => Db::FLOAT_TYPE, + 'FLOAT' => Db::FLOAT_TYPE, + 'MONEY' => Db::FLOAT_TYPE, + 'NUMERIC' => Db::FLOAT_TYPE, + 'REAL' => Db::FLOAT_TYPE, + 'SMALLMONEY' => Db::FLOAT_TYPE, + ); + + /** + * Default class name for a DB statement. + * + * @var string + */ + protected $_defaultStmtClass = SqlsrvStatement::class; + + /** + * Creates a connection resource. + * + * @return void + * @throws AdapterExceptionSqlsrv + */ + protected function _connect() + { + if (is_resource($this->_connection)) { + // connection already exists + return; + } + + if (!extension_loaded('sqlsrv')) { + throw new AdapterExceptionSqlsrv( + 'The Sqlsrv extension is required for this adapter but the extension is not loaded' + ); + } + + $serverName = $this->_config['host']; + if (isset($this->_config['port'])) { + $port = (integer) $this->_config['port']; + $serverName .= ', ' . $port; + } + + $connectionInfo = array( + 'Database' => $this->_config['dbname'], + ); + + if (isset($this->_config['username']) && isset($this->_config['password'])) { + $connectionInfo += array( + 'UID' => $this->_config['username'], + 'PWD' => $this->_config['password'], + ); + } + // else - windows authentication + + if (!empty($this->_config['driver_options'])) { + foreach ($this->_config['driver_options'] as $option => $value) { + // A value may be a constant. + if (is_string($value)) { + $constantName = strtoupper($value); + if (defined($constantName)) { + $connectionInfo[$option] = constant($constantName); + } else { + $connectionInfo[$option] = $value; + } + } + } + } + + $this->_connection = sqlsrv_connect($serverName, $connectionInfo); + + if (!$this->_connection) { + throw new AdapterExceptionSqlsrv(sqlsrv_errors()); + } + } + + /** + * Check for config options that are mandatory. + * Throw exceptions if any are missing. + * + * @param array $config + * @throws AdapterException + */ + protected function _checkRequiredOptions(array $config) + { + // we need at least a dbname + if (! array_key_exists('dbname', $config)) { + throw new AdapterException( + "Configuration array must have a key for 'dbname' that names the database instance" + ); + } + + if (! array_key_exists('password', $config) && array_key_exists('username', $config)) { + throw new AdapterException( + "Configuration array must have a key for 'password' for login credentials." + . " If Windows Authentication is desired, both keys 'username' and 'password'" + . " should be ommited from config." + ); + } + + if (array_key_exists('password', $config) && !array_key_exists('username', $config)) { + throw new AdapterException( + "Configuration array must have a key for 'username' for login credentials." + . " If Windows Authentication is desired, both keys 'username' and 'password'" + . " should be ommited from config." + ); + } + } + + /** + * Set the transaction isoltion level. + * + * @param integer|null $level A fetch mode from SQLSRV_TXN_*. + * @return true + * @throws AdapterExceptionSqlsrv + */ + public function setTransactionIsolationLevel($level = null) + { + $this->_connect(); + $sql = null; + + // Default transaction level in sql server + if ($level === null) { + $level = SQLSRV_TXN_READ_COMMITTED; + } + + switch ($level) { + case SQLSRV_TXN_READ_UNCOMMITTED: + $sql = "READ UNCOMMITTED"; + break; + case SQLSRV_TXN_READ_COMMITTED: + $sql = "READ COMMITTED"; + break; + case SQLSRV_TXN_REPEATABLE_READ: + $sql = "REPEATABLE READ"; + break; + case SQLSRV_TXN_SNAPSHOT: + $sql = "SNAPSHOT"; + break; + case SQLSRV_TXN_SERIALIZABLE: + $sql = "SERIALIZABLE"; + break; + default: + throw new AdapterExceptionSqlsrv("Invalid transaction isolation level mode '$level' specified"); + } + + if (!sqlsrv_query($this->_connection, "SET TRANSACTION ISOLATION LEVEL $sql;")) { + throw new AdapterExceptionSqlsrv("Transaction cannot be changed to '$level'"); + } + + return true; + } + + /** + * Test if a connection is active + * + * @return boolean + */ + public function isConnected() + { + return (is_resource($this->_connection) + && (get_resource_type($this->_connection) == 'SQL Server Connection') + ); + } + + /** + * Force the connection to close. + * + * @return void + */ + public function closeConnection() + { + if ($this->isConnected()) { + sqlsrv_close($this->_connection); + } + $this->_connection = null; + } + + /** + * Returns an SQL statement for preparation. + * + * @param string $sql The SQL statement with placeholders. + * @return SqlsrvStatement + */ + public function prepare($sql) + { + $this->_connect(); + $stmtClass = $this->_defaultStmtClass; + $stmt = new $stmtClass($this, $sql); + $stmt->setFetchMode($this->_fetchMode); + return $stmt; + } + + /** + * Quote a raw string. + * + * @param string $value Raw string + * @return string Quoted string + */ + protected function _quote($value) + { + if (is_int($value)) { + return $value; + } elseif (is_float($value)) { + return sprintf('%F', $value); + } + + $value = addcslashes($value, "\000\032"); + return "'" . str_replace("'", "''", $value) . "'"; + } + + /** + * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column. + * + * As a convention, on RDBMS brands that support sequences + * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence + * from the arguments and returns the last id generated by that sequence. + * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method + * returns the last value generated for such a column, and the table name + * argument is disregarded. + * + * @param string $tableName OPTIONAL Name of table. + * @param string $primaryKey OPTIONAL Name of primary key column. + * @return string + */ + public function lastInsertId($tableName = null, $primaryKey = null) + { + if ($tableName) { + $tableName = $this->quote($tableName); + $sql = 'SELECT IDENT_CURRENT (' . $tableName . ') as Current_Identity'; + return (string) $this->fetchOne($sql); + } + + if ($this->_lastInsertId > 0) { + return (string) $this->_lastInsertId; + } + + $sql = $this->_lastInsertSQL; + return (string) $this->fetchOne($sql); + } + + /** + * Inserts a table row with specified data. + * + * @param mixed $table The table to insert data into. + * @param array $bind Column-value pairs. + * @return int The number of affected rows. + */ + public function insert($table, array $bind) + { + // extract and quote col names from the array keys + $cols = array(); + $vals = array(); + foreach ($bind as $col => $val) { + $cols[] = $this->quoteIdentifier($col, true); + if ($val instanceof Expr) { + $vals[] = $val->__toString(); + unset($bind[$col]); + } else { + $vals[] = '?'; + } + } + + // build the statement + $sql = "INSERT INTO " + . $this->quoteIdentifier($table, true) + . ' (' . implode(', ', $cols) . ') ' + . 'VALUES (' . implode(', ', $vals) . ')' + . ' ' . $this->_lastInsertSQL; + + // execute the statement and return the number of affected rows + $stmt = $this->query($sql, array_values($bind)); + $result = $stmt->rowCount(); + + $stmt->nextRowset(); + + $this->_lastInsertId = $stmt->fetchColumn(); + + return $result; + } + + /** + * Returns a list of the tables in the database. + * + * @return array + */ + public function listTables() + { + $this->_connect(); + $sql = "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name"; + return $this->fetchCol($sql); + } + + /** + * Returns the column descriptions for a table. + * + * The return value is an associative array keyed by the column name, + * as returned by the RDBMS. + * + * The value of each array element is an associative array + * with the following keys: + * + * SCHEMA_NAME => string; name of schema + * TABLE_NAME => string; + * COLUMN_NAME => string; column name + * COLUMN_POSITION => number; ordinal position of column in table + * DATA_TYPE => string; SQL datatype name of column + * DEFAULT => string; default expression of column, null if none + * NULLABLE => boolean; true if column can have nulls + * LENGTH => number; length of CHAR/VARCHAR + * SCALE => number; scale of NUMERIC/DECIMAL + * PRECISION => number; precision of NUMERIC/DECIMAL + * UNSIGNED => boolean; unsigned property of an integer type + * PRIMARY => boolean; true if column is part of the primary key + * PRIMARY_POSITION => integer; position of column in primary key + * IDENTITY => integer; true if column is auto-generated with unique values + * + * @todo Discover integer unsigned property. + * + * @param string $tableName + * @param string $schemaName OPTIONAL + * @return array + */ + public function describeTable($tableName, $schemaName = null) + { + /** + * Discover metadata information about this table. + */ + $sql = "exec sp_columns @table_name = " . $this->quoteIdentifier($tableName, true); + $stmt = $this->query($sql); + $result = $stmt->fetchAll(Db::FETCH_NUM); + + // ZF-7698 + $stmt->closeCursor(); + + if (count($result) == 0) { + return array(); + } + + $owner = 1; + $table_name = 2; + $column_name = 3; + $type_name = 5; + $precision = 6; + $length = 7; + $scale = 8; + $nullable = 10; + $column_def = 12; + $column_position = 16; + + /** + * Discover primary key column(s) for this table. + */ + $tableOwner = $result[0][$owner]; + $sql = "exec sp_pkeys @table_owner = " . $tableOwner + . ", @table_name = " . $this->quoteIdentifier($tableName, true); + $stmt = $this->query($sql); + + $primaryKeysResult = $stmt->fetchAll(Db::FETCH_NUM); + $primaryKeyColumn = array(); + + // Per http://msdn.microsoft.com/en-us/library/ms189813.aspx, + // results from sp_keys stored procedure are: + // 0=TABLE_QUALIFIER 1=TABLE_OWNER 2=TABLE_NAME 3=COLUMN_NAME 4=KEY_SEQ 5=PK_NAME + + $pkey_column_name = 3; + $pkey_key_seq = 4; + foreach ($primaryKeysResult as $pkeysRow) { + $primaryKeyColumn[$pkeysRow[$pkey_column_name]] = $pkeysRow[$pkey_key_seq]; + } + + $desc = array(); + $p = 1; + foreach ($result as $key => $row) { + $identity = false; + $words = explode(' ', $row[$type_name], 2); + if (isset($words[0])) { + $type = $words[0]; + if (isset($words[1])) { + $identity = (bool) preg_match('/identity/', $words[1]); + } + } + + $isPrimary = array_key_exists($row[$column_name], $primaryKeyColumn); + if ($isPrimary) { + $primaryPosition = $primaryKeyColumn[$row[$column_name]]; + } else { + $primaryPosition = null; + } + + $desc[$this->foldCase($row[$column_name])] = array( + 'SCHEMA_NAME' => null, // @todo + 'TABLE_NAME' => $this->foldCase($row[$table_name]), + 'COLUMN_NAME' => $this->foldCase($row[$column_name]), + 'COLUMN_POSITION' => (int) $row[$column_position], + 'DATA_TYPE' => $type, + 'DEFAULT' => $row[$column_def], + 'NULLABLE' => (bool) $row[$nullable], + 'LENGTH' => $row[$length], + 'SCALE' => $row[$scale], + 'PRECISION' => $row[$precision], + 'UNSIGNED' => null, // @todo + 'PRIMARY' => $isPrimary, + 'PRIMARY_POSITION' => $primaryPosition, + 'IDENTITY' => $identity, + ); + } + + return $desc; + } + + /** + * Leave autocommit mode and begin a transaction. + * + * @return void + * @throws AdapterExceptionSqlsrv + */ + protected function _beginTransaction() + { + if (!sqlsrv_begin_transaction($this->_connection)) { + throw new AdapterExceptionSqlsrv(sqlsrv_errors()); + } + } + + /** + * Commit a transaction and return to autocommit mode. + * + * @return void + * @throws AdapterExceptionSqlsrv + */ + protected function _commit() + { + if (!sqlsrv_commit($this->_connection)) { + throw new AdapterExceptionSqlsrv(sqlsrv_errors()); + } + } + + /** + * Roll back a transaction and return to autocommit mode. + * + * @return void + * @throws AdapterExceptionSqlsrv + */ + protected function _rollBack() + { + if (!sqlsrv_rollback($this->_connection)) { + throw new AdapterExceptionSqlsrv(sqlsrv_errors()); + } + } + + /** + * Set the fetch mode. + * + * @param integer $mode A fetch mode. + * @return void + * @throws AdapterExceptionSqlsrv + *@todo Support FETCH_CLASS and FETCH_INTO. + * + */ + public function setFetchMode($mode) + { + switch ($mode) { + case Db::FETCH_NUM: // seq array + case Db::FETCH_ASSOC: // assoc array + case Db::FETCH_BOTH: // seq+assoc array + case Db::FETCH_OBJ: // object + $this->_fetchMode = $mode; + break; + case Db::FETCH_BOUND: // bound to PHP variable + throw new AdapterExceptionSqlsrv('FETCH_BOUND is not supported yet'); + default: + throw new AdapterExceptionSqlsrv("Invalid fetch mode '$mode' specified"); + } + } + + /** + * Adds an adapter-specific LIMIT clause to the SELECT statement. + * + * @param string $sql + * @param integer $count + * @param integer $offset OPTIONAL + * @return string + * @throws AdapterException + */ + public function limit($sql, $count, $offset = 0) + { + $count = intval($count); + if ($count <= 0) { + throw new AdapterException("LIMIT argument count=$count is not valid"); + } + + $offset = intval($offset); + if ($offset < 0) { + /** @see AdapterException */ + throw new AdapterException("LIMIT argument offset=$offset is not valid"); + } + + if ($offset == 0) { + $sql = preg_replace('/^SELECT\s/i', 'SELECT TOP ' . $count . ' ', $sql); + } else { + $orderby = stristr($sql, 'ORDER BY'); + + if (!$orderby) { + $over = 'ORDER BY (SELECT 0)'; + } else { + $over = preg_replace('/\"[^,]*\".\"([^,]*)\"/i', '"inner_tbl"."$1"', $orderby); + } + + // Remove ORDER BY clause from $sql + $sql = preg_replace('/\s+ORDER BY(.*)/', '', $sql); + + // Add ORDER BY clause as an argument for ROW_NUMBER() + $sql = "SELECT ROW_NUMBER() OVER ($over) AS \"ZEND_DB_ROWNUM\", * FROM ($sql) AS inner_tbl"; + + $start = $offset + 1; + + if ($count == PHP_INT_MAX) { + $sql = "WITH outer_tbl AS ($sql) SELECT * FROM outer_tbl WHERE \"ZEND_DB_ROWNUM\" >= $start"; + } else { + $end = $offset + $count; + $sql = "WITH outer_tbl AS ($sql) SELECT * FROM outer_tbl" + . " WHERE \"ZEND_DB_ROWNUM\" BETWEEN $start AND $end"; + } + } + + return $sql; + } + + /** + * Check if the adapter supports real SQL parameters. + * + * @param string $type 'positional' or 'named' + * @return bool + */ + public function supportsParameters($type) + { + if ($type == 'positional') { + return true; + } + + // if its 'named' or anything else + return false; + } + + /** + * Retrieve server version in PHP style + * + * @return string + */ + public function getServerVersion() + { + $this->_connect(); + $serverInfo = sqlsrv_server_info($this->_connection); + + if ($serverInfo !== false) { + return $serverInfo['SQLServerVersion']; + } + + return null; + } +} diff --git a/vendor/gipfl/zfdb/src/Db.php b/vendor/gipfl/zfdb/src/Db.php new file mode 100644 index 0000000..529461b --- /dev/null +++ b/vendor/gipfl/zfdb/src/Db.php @@ -0,0 +1,248 @@ +<?php + +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb; + +use gipfl\ZfDb\Adapter\Adapter; +use gipfl\ZfDb\Exception\DbException; + +/** + * Class for connecting to SQL databases and performing common operations. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Db +{ + /** + * Use the PROFILER constant in the config of a Zend_Db_Adapter. + */ + const PROFILER = 'profiler'; + + /** + * Use the CASE_FOLDING constant in the config of a Zend_Db_Adapter. + */ + const CASE_FOLDING = 'caseFolding'; + + /** + * Use the FETCH_MODE constant in the config of a Zend_Db_Adapter. + */ + const FETCH_MODE = 'fetchMode'; + + /** + * Use the AUTO_QUOTE_IDENTIFIERS constant in the config of a Zend_Db_Adapter. + */ + const AUTO_QUOTE_IDENTIFIERS = 'autoQuoteIdentifiers'; + + /** + * Use the ALLOW_SERIALIZATION constant in the config of a Zend_Db_Adapter. + */ + const ALLOW_SERIALIZATION = 'allowSerialization'; + + /** + * Use the AUTO_RECONNECT_ON_UNSERIALIZE constant in the config of a Zend_Db_Adapter. + */ + const AUTO_RECONNECT_ON_UNSERIALIZE = 'autoReconnectOnUnserialize'; + + /** + * Use the INT_TYPE, BIGINT_TYPE, and FLOAT_TYPE with the quote() method. + */ + const INT_TYPE = 0; + const BIGINT_TYPE = 1; + const FLOAT_TYPE = 2; + + /** + * PDO constant values discovered by this script result: + * + * $list = array( + * 'PARAM_BOOL', 'PARAM_NULL', 'PARAM_INT', 'PARAM_STR', 'PARAM_LOB', + * 'PARAM_STMT', 'PARAM_INPUT_OUTPUT', 'FETCH_LAZY', 'FETCH_ASSOC', + * 'FETCH_NUM', 'FETCH_BOTH', 'FETCH_OBJ', 'FETCH_BOUND', + * 'FETCH_COLUMN', 'FETCH_CLASS', 'FETCH_INTO', 'FETCH_FUNC', + * 'FETCH_GROUP', 'FETCH_UNIQUE', 'FETCH_CLASSTYPE', 'FETCH_SERIALIZE', + * 'FETCH_NAMED', 'ATTR_AUTOCOMMIT', 'ATTR_PREFETCH', 'ATTR_TIMEOUT', + * 'ATTR_ERRMODE', 'ATTR_SERVER_VERSION', 'ATTR_CLIENT_VERSION', + * 'ATTR_SERVER_INFO', 'ATTR_CONNECTION_STATUS', 'ATTR_CASE', + * 'ATTR_CURSOR_NAME', 'ATTR_CURSOR', 'ATTR_ORACLE_NULLS', + * 'ATTR_PERSISTENT', 'ATTR_STATEMENT_CLASS', 'ATTR_FETCH_TABLE_NAMES', + * 'ATTR_FETCH_CATALOG_NAMES', 'ATTR_DRIVER_NAME', + * 'ATTR_STRINGIFY_FETCHES', 'ATTR_MAX_COLUMN_LEN', 'ERRMODE_SILENT', + * 'ERRMODE_WARNING', 'ERRMODE_EXCEPTION', 'CASE_NATURAL', + * 'CASE_LOWER', 'CASE_UPPER', 'NULL_NATURAL', 'NULL_EMPTY_STRING', + * 'NULL_TO_STRING', 'ERR_NONE', 'FETCH_ORI_NEXT', + * 'FETCH_ORI_PRIOR', 'FETCH_ORI_FIRST', 'FETCH_ORI_LAST', + * 'FETCH_ORI_ABS', 'FETCH_ORI_REL', 'CURSOR_FWDONLY', 'CURSOR_SCROLL', + * 'ERR_CANT_MAP', 'ERR_SYNTAX', 'ERR_CONSTRAINT', 'ERR_NOT_FOUND', + * 'ERR_ALREADY_EXISTS', 'ERR_NOT_IMPLEMENTED', 'ERR_MISMATCH', + * 'ERR_TRUNCATED', 'ERR_DISCONNECTED', 'ERR_NO_PERM', + * ); + * + * $const = array(); + * foreach ($list as $name) { + * $const[$name] = constant("PDO::$name"); + * } + * var_export($const); + */ + const ATTR_AUTOCOMMIT = 0; + const ATTR_CASE = 8; + const ATTR_CLIENT_VERSION = 5; + const ATTR_CONNECTION_STATUS = 7; + const ATTR_CURSOR = 10; + const ATTR_CURSOR_NAME = 9; + const ATTR_DRIVER_NAME = 16; + const ATTR_ERRMODE = 3; + const ATTR_FETCH_CATALOG_NAMES = 15; + const ATTR_FETCH_TABLE_NAMES = 14; + const ATTR_MAX_COLUMN_LEN = 18; + const ATTR_ORACLE_NULLS = 11; + const ATTR_PERSISTENT = 12; + const ATTR_PREFETCH = 1; + const ATTR_SERVER_INFO = 6; + const ATTR_SERVER_VERSION = 4; + const ATTR_STATEMENT_CLASS = 13; + const ATTR_STRINGIFY_FETCHES = 17; + const ATTR_TIMEOUT = 2; + const CASE_LOWER = 2; + const CASE_NATURAL = 0; + const CASE_UPPER = 1; + const CURSOR_FWDONLY = 0; + const CURSOR_SCROLL = 1; + const ERR_ALREADY_EXISTS = null; + const ERR_CANT_MAP = null; + const ERR_CONSTRAINT = null; + const ERR_DISCONNECTED = null; + const ERR_MISMATCH = null; + const ERR_NO_PERM = null; + const ERR_NONE = '00000'; + const ERR_NOT_FOUND = null; + const ERR_NOT_IMPLEMENTED = null; + const ERR_SYNTAX = null; + const ERR_TRUNCATED = null; + const ERRMODE_EXCEPTION = 2; + const ERRMODE_SILENT = 0; + const ERRMODE_WARNING = 1; + const FETCH_ASSOC = 2; + const FETCH_BOTH = 4; + const FETCH_BOUND = 6; + const FETCH_CLASS = 8; + const FETCH_CLASSTYPE = 262144; + const FETCH_COLUMN = 7; + const FETCH_FUNC = 10; + const FETCH_GROUP = 65536; + const FETCH_INTO = 9; + const FETCH_LAZY = 1; + const FETCH_NAMED = 11; + const FETCH_NUM = 3; + const FETCH_OBJ = 5; + const FETCH_ORI_ABS = 4; + const FETCH_ORI_FIRST = 2; + const FETCH_ORI_LAST = 3; + const FETCH_ORI_NEXT = 0; + const FETCH_ORI_PRIOR = 1; + const FETCH_ORI_REL = 5; + const FETCH_SERIALIZE = 524288; + const FETCH_UNIQUE = 196608; + const NULL_EMPTY_STRING = 1; + const NULL_NATURAL = 0; + const NULL_TO_STRING = null; + const PARAM_BOOL = 5; + const PARAM_INPUT_OUTPUT = -2147483648; + const PARAM_INT = 1; + const PARAM_LOB = 3; + const PARAM_NULL = 0; + const PARAM_STMT = 4; + const PARAM_STR = 2; + + /** + * Factory for Zend_Db_Adapter_Abstract classes. + * + * First argument may be a string containing the base of the adapter class + * name, e.g. 'Mysqli' corresponds to class Zend_Db_Adapter_Mysqli. This + * name is currently case-insensitive, but is not ideal to rely on this behavior. + * If your class is named 'My_Company_Pdo_Mysql', where 'My_Company' is the namespace + * and 'Pdo_Mysql' is the adapter name, it is best to use the name exactly as it + * is defined in the class. This will ensure proper use of the factory API. + * + * First argument may alternatively be an object of type Zend_Config. + * The adapter class base name is read from the 'adapter' property. + * The adapter config parameters are read from the 'params' property. + * + * Second argument is optional and may be an associative array of key-value + * pairs. This is used as the argument to the adapter constructor. + * + * If the first argument is of type Zend_Config, it is assumed to contain + * all parameters, and the second argument is ignored. + * + * @param mixed $adapter String name of base adapter class, or Zend_Config object. + * @param mixed $config OPTIONAL; an array or Zend_Config object with adapter parameters. + * @return Adapter + * @throws DbException + */ + public static function factory($adapter, $config = array()) + { + if ($config instanceof Zend_Config) { + $config = $config->toArray(); + } + + /* + * Convert Zend_Config argument to plain string + * adapter name and separate config object. + */ + if ($adapter instanceof Zend_Config) { + if (isset($adapter->params)) { + $config = $adapter->params->toArray(); + } + if (isset($adapter->adapter)) { + $adapter = (string) $adapter->adapter; + } else { + $adapter = null; + } + } + + /* + * Verify that adapter parameters are in an array. + */ + if (!is_array($config)) { + throw new DbException('Adapter parameters must be in an array or a Zend_Config object'); + } + + /* + * Verify that an adapter name has been specified. + */ + if (!is_string($adapter) || empty($adapter)) { + throw new DbException('Adapter name must be specified in a string'); + } + + /* + * Form full adapter class name + */ + $adapterClass = '\\gipfl\\ZfDb\\Adapter\\' + . str_replace(' ', '\\', ucwords(str_replace('_', ' ', strtolower($adapter)))); + $dbAdapter = new $adapterClass($config); + + /* + * Verify that the object created is a descendent of the abstract adapter type. + */ + if (! $dbAdapter instanceof Adapter) { + throw new DbException("Adapter class '$adapterClass' does not extend gipfl\ZfDb\Abstract"); + } + + return $dbAdapter; + } +} diff --git a/vendor/gipfl/zfdb/src/Exception/DbException.php b/vendor/gipfl/zfdb/src/Exception/DbException.php new file mode 100644 index 0000000..ab8c740 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Exception/DbException.php @@ -0,0 +1,29 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Exception; + +use Exception; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class DbException extends Exception +{ +} diff --git a/vendor/gipfl/zfdb/src/Exception/SelectException.php b/vendor/gipfl/zfdb/src/Exception/SelectException.php new file mode 100644 index 0000000..8764741 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Exception/SelectException.php @@ -0,0 +1,27 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Exception; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class SelectException extends DbException +{ +} diff --git a/vendor/gipfl/zfdb/src/Expr.php b/vendor/gipfl/zfdb/src/Expr.php new file mode 100644 index 0000000..2184c5a --- /dev/null +++ b/vendor/gipfl/zfdb/src/Expr.php @@ -0,0 +1,70 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb; + +/** + * Class for SQL SELECT fragments. + * + * This class simply holds a string, so that fragments of SQL statements can be + * distinguished from identifiers and values that should be implicitly quoted + * when interpolated into SQL statements. + * + * For example, when specifying a primary key value when inserting into a new + * row, some RDBMS brands may require you to use an expression to generate the + * new value of a sequence. If this expression is treated as an identifier, + * it will be quoted and the expression will not be evaluated. Another example + * is that you can use Expr in the Select::order() method to + * order by an expression instead of simply a column name. + * + * The way this works is that in each context in which a column name can be + * specified to methods of Zend_Db classes, if the value is an instance of + * Expr instead of a plain string, then the expression is not quoted. + * If it is a plain string, it is assumed to be a plain column name. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Expr +{ + /** + * Storage for the SQL expression. + * + * @var string + */ + protected $_expression; + + /** + * Instantiate an expression, which is just a string stored as + * an instance member variable. + * + * @param string $expression The string containing a SQL expression. + */ + public function __construct($expression) + { + $this->_expression = (string) $expression; + } + + /** + * @return string The string of the SQL expression stored in this object. + */ + public function __toString() + { + return $this->_expression; + } +} diff --git a/vendor/gipfl/zfdb/src/Profiler.php b/vendor/gipfl/zfdb/src/Profiler.php new file mode 100644 index 0000000..026fa0e --- /dev/null +++ b/vendor/gipfl/zfdb/src/Profiler.php @@ -0,0 +1,463 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb; + +use gipfl\ZfDb\Profiler\ProfilerException; +use gipfl\ZfDb\Profiler\ProfilerQuery; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Profiler +{ + /** + * A connection operation or selecting a database. + */ + const CONNECT = 1; + + /** + * Any general database query that does not fit into the other constants. + */ + const QUERY = 2; + + /** + * Adding new data to the database, such as SQL's INSERT. + */ + const INSERT = 4; + + /** + * Updating existing information in the database, such as SQL's UPDATE. + * + */ + const UPDATE = 8; + + /** + * An operation related to deleting data in the database, + * such as SQL's DELETE. + */ + const DELETE = 16; + + /** + * Retrieving information from the database, such as SQL's SELECT. + */ + const SELECT = 32; + + /** + * Transactional operation, such as start transaction, commit, or rollback. + */ + const TRANSACTION = 64; + + /** + * Inform that a query is stored (in case of filtering) + */ + const STORED = 'stored'; + + /** + * Inform that a query is ignored (in case of filtering) + */ + const IGNORED = 'ignored'; + + /** + * Array of ProfilerQuery objects. + * + * @var array + */ + protected $_queryProfiles = array(); + + /** + * Stores enabled state of the profiler. If set to False, calls to + * queryStart() will simply be ignored. + * + * @var boolean + */ + protected $_enabled = false; + + /** + * Stores the number of seconds to filter. NULL if filtering by time is + * disabled. If an integer is stored here, profiles whose elapsed time + * is less than this value in seconds will be unset from + * the self::$_queryProfiles array. + * + * @var integer + */ + protected $_filterElapsedSecs = null; + + /** + * Logical OR of any of the filter constants. NULL if filtering by query + * type is disable. If an integer is stored here, it is the logical OR of + * any of the query type constants. When the query ends, if it is not + * one of the types specified, it will be unset from the + * self::$_queryProfiles array. + * + * @var integer + */ + protected $_filterTypes = null; + + /** + * Class constructor. The profiler is disabled by default unless it is + * specifically enabled by passing in $enabled here or calling setEnabled(). + * + * @param boolean $enabled + * @return void + */ + public function __construct($enabled = false) + { + $this->setEnabled($enabled); + } + + /** + * Enable or disable the profiler. If $enable is false, the profiler + * is disabled and will not log any queries sent to it. + * + * @param boolean $enable + * @return Profiler Provides a fluent interface + */ + public function setEnabled($enable) + { + $this->_enabled = (boolean) $enable; + + return $this; + } + + /** + * Get the current state of enable. If True is returned, + * the profiler is enabled. + * + * @return boolean + */ + public function getEnabled() + { + return $this->_enabled; + } + + /** + * Sets a minimum number of seconds for saving query profiles. If this + * is set, only those queries whose elapsed time is equal or greater than + * $minimumSeconds will be saved. To save all queries regardless of + * elapsed time, set $minimumSeconds to null. + * + * @param integer $minimumSeconds OPTIONAL + * @return Profiler Provides a fluent interface + */ + public function setFilterElapsedSecs($minimumSeconds = null) + { + if (null === $minimumSeconds) { + $this->_filterElapsedSecs = null; + } else { + $this->_filterElapsedSecs = (integer) $minimumSeconds; + } + + return $this; + } + + /** + * Returns the minimum number of seconds for saving query profiles, or null if + * query profiles are saved regardless of elapsed time. + * + * @return integer|null + */ + public function getFilterElapsedSecs() + { + return $this->_filterElapsedSecs; + } + + /** + * Sets the types of query profiles to save. Set $queryType to one of + * the Zend_Db_Profiler::* constants to only save profiles for that type of + * query. To save more than one type, logical OR them together. To + * save all queries regardless of type, set $queryType to null. + * + * @param integer $queryTypes OPTIONAL + * @return Profiler Provides a fluent interface + */ + public function setFilterQueryType($queryTypes = null) + { + $this->_filterTypes = $queryTypes; + + return $this; + } + + /** + * Returns the types of query profiles saved, or null if queries are saved regardless + * of their types. + * + * @return integer|null + * @see Profiler::setFilterQueryType() + */ + public function getFilterQueryType() + { + return $this->_filterTypes; + } + + /** + * Clears the history of any past query profiles. This is relentless + * and will even clear queries that were started and may not have + * been marked as ended. + * + * @return Profiler Provides a fluent interface + */ + public function clear() + { + $this->_queryProfiles = array(); + + return $this; + } + + /** + * Clone a profiler query + * + * @param ProfilerQuery $query + * @return integer or null + */ + public function queryClone(ProfilerQuery $query) + { + $this->_queryProfiles[] = clone $query; + + end($this->_queryProfiles); + + return key($this->_queryProfiles); + } + + /** + * Starts a query. Creates a new query profile object (ProfilerQuery) + * and returns the "query profiler handle". Run the query, then call + * queryEnd() and pass it this handle to make the query as ended and + * record the time. If the profiler is not enabled, this takes no + * action and immediately returns null. + * + * @param string $queryText SQL statement + * @param integer $queryType OPTIONAL Type of query, one of the Zend_Db_Profiler::* constants + * @return integer|null + */ + public function queryStart($queryText, $queryType = null) + { + if (!$this->_enabled) { + return null; + } + + // make sure we have a query type + if (null === $queryType) { + switch (strtolower(substr(ltrim($queryText), 0, 6))) { + case 'insert': + $queryType = self::INSERT; + break; + case 'update': + $queryType = self::UPDATE; + break; + case 'delete': + $queryType = self::DELETE; + break; + case 'select': + $queryType = self::SELECT; + break; + default: + $queryType = self::QUERY; + break; + } + } + + /** + * @see ProfilerQuery + */ + $this->_queryProfiles[] = new ProfilerQuery($queryText, $queryType); + + end($this->_queryProfiles); + + return key($this->_queryProfiles); + } + + /** + * Ends a query. Pass it the handle that was returned by queryStart(). + * This will mark the query as ended and save the time. + * + * @param integer $queryId + * @throws ProfilerException + * @return string Inform that a query is stored or ignored. + */ + public function queryEnd($queryId) + { + // Don't do anything if the Zend_Db_Profiler is not enabled. + if (!$this->_enabled) { + return self::IGNORED; + } + + // Check for a valid query handle. + if (!isset($this->_queryProfiles[$queryId])) { + /** + * @see ProfilerException + */ + throw new ProfilerException("Profiler has no query with handle '$queryId'."); + } + + $qp = $this->_queryProfiles[$queryId]; + + // Ensure that the query profile has not already ended + if ($qp->hasEnded()) { + /** + * @see ProfilerException + */ + throw new ProfilerException("Query with profiler handle '$queryId' has already ended."); + } + + // End the query profile so that the elapsed time can be calculated. + $qp->end(); + + /** + * If filtering by elapsed time is enabled, only keep the profile if + * it ran for the minimum time. + */ + if (null !== $this->_filterElapsedSecs && $qp->getElapsedSecs() < $this->_filterElapsedSecs) { + unset($this->_queryProfiles[$queryId]); + return self::IGNORED; + } + + /** + * If filtering by query type is enabled, only keep the query if + * it was one of the allowed types. + */ + if (null !== $this->_filterTypes && !($qp->getQueryType() & $this->_filterTypes)) { + unset($this->_queryProfiles[$queryId]); + return self::IGNORED; + } + + return self::STORED; + } + + /** + * Get a profile for a query. Pass it the same handle that was returned + * by queryStart() and it will return a ProfilerQuery object. + * + * @param integer $queryId + * @throws ProfilerException + * @return ProfilerQuery + */ + public function getQueryProfile($queryId) + { + if (!array_key_exists($queryId, $this->_queryProfiles)) { + /** + * @see ProfilerException + */ + throw new ProfilerException("Query handle '$queryId' not found in profiler log."); + } + + return $this->_queryProfiles[$queryId]; + } + + /** + * Get an array of query profiles (ProfilerQuery objects). If $queryType + * is set to one of the Zend_Db_Profiler::* constants then only queries of that + * type will be returned. Normally, queries that have not yet ended will + * not be returned unless $showUnfinished is set to True. If no + * queries were found, False is returned. The returned array is indexed by the query + * profile handles. + * + * @param integer $queryType + * @param boolean $showUnfinished + * @return array|false + */ + public function getQueryProfiles($queryType = null, $showUnfinished = false) + { + $queryProfiles = array(); + foreach ($this->_queryProfiles as $key => $qp) { + if ($queryType === null) { + $condition = true; + } else { + $condition = ($qp->getQueryType() & $queryType); + } + + if (($qp->hasEnded() || $showUnfinished) && $condition) { + $queryProfiles[$key] = $qp; + } + } + + if (empty($queryProfiles)) { + $queryProfiles = false; + } + + return $queryProfiles; + } + + /** + * Get the total elapsed time (in seconds) of all of the profiled queries. + * Only queries that have ended will be counted. If $queryType is set to + * one or more of the Zend_Db_Profiler::* constants, the elapsed time will be calculated + * only for queries of the given type(s). + * + * @param integer $queryType OPTIONAL + * @return float + */ + public function getTotalElapsedSecs($queryType = null) + { + $elapsedSecs = 0; + foreach ($this->_queryProfiles as $key => $qp) { + if (null === $queryType) { + $condition = true; + } else { + $condition = ($qp->getQueryType() & $queryType); + } + if (($qp->hasEnded()) && $condition) { + $elapsedSecs += $qp->getElapsedSecs(); + } + } + return $elapsedSecs; + } + + /** + * Get the total number of queries that have been profiled. Only queries that have ended will + * be counted. If $queryType is set to one of the Zend_Db_Profiler::* constants, only queries of + * that type will be counted. + * + * @param integer $queryType OPTIONAL + * @return integer + */ + public function getTotalNumQueries($queryType = null) + { + if (null === $queryType) { + return count($this->_queryProfiles); + } + + $numQueries = 0; + foreach ($this->_queryProfiles as $qp) { + if ($qp->hasEnded() && ($qp->getQueryType() & $queryType)) { + $numQueries++; + } + } + + return $numQueries; + } + + /** + * Get the ProfilerQuery object for the last query that was run, regardless if it has + * ended or not. If the query has not ended, its end time will be null. If no queries have + * been profiled, false is returned. + * + * @return ProfilerQuery|false + */ + public function getLastQueryProfile() + { + if (empty($this->_queryProfiles)) { + return false; + } + + end($this->_queryProfiles); + + return current($this->_queryProfiles); + } +} diff --git a/vendor/gipfl/zfdb/src/Profiler/ProfilerException.php b/vendor/gipfl/zfdb/src/Profiler/ProfilerException.php new file mode 100644 index 0000000..3722793 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Profiler/ProfilerException.php @@ -0,0 +1,29 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Profiler; + +use gipfl\ZfDb\Exception\DbException; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class ProfilerException extends DbException +{ +} diff --git a/vendor/gipfl/zfdb/src/Profiler/ProfilerQuery.php b/vendor/gipfl/zfdb/src/Profiler/ProfilerQuery.php new file mode 100644 index 0000000..7f79396 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Profiler/ProfilerQuery.php @@ -0,0 +1,206 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Profiler; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class ProfilerQuery +{ + + /** + * SQL query string or user comment, set by $query argument in constructor. + * + * @var string + */ + protected $_query = ''; + + /** + * One of the Zend_Db_Profiler constants for query type, set by $queryType argument in constructor. + * + * @var integer + */ + protected $_queryType = 0; + + /** + * Unix timestamp with microseconds when instantiated. + * + * @var float + */ + protected $_startedMicrotime = null; + + /** + * Unix timestamp with microseconds when self::queryEnd() was called. + * + * @var integer + */ + protected $_endedMicrotime = null; + + /** + * @var array + */ + protected $_boundParams = array(); + + /** + * @var array + */ + + /** + * Class constructor. A query is about to be started, save the query text ($query) and its + * type (one of the Zend_Db_Profiler::* constants). + * + * @param string $query + * @param integer $queryType + * @return void + */ + public function __construct($query, $queryType) + { + $this->_query = $query; + $this->_queryType = $queryType; + // by default, and for backward-compatibility, start the click ticking + $this->start(); + } + + /** + * Clone handler for the query object. + * @return void + */ + public function __clone() + { + $this->_boundParams = array(); + $this->_endedMicrotime = null; + $this->start(); + } + + /** + * Starts the elapsed time click ticking. + * This can be called subsequent to object creation, + * to restart the clock. For instance, this is useful + * right before executing a prepared query. + * + * @return void + */ + public function start() + { + $this->_startedMicrotime = microtime(true); + } + + /** + * Ends the query and records the time so that the elapsed time can be determined later. + * + * @return void + */ + public function end() + { + $this->_endedMicrotime = microtime(true); + } + + /** + * Returns true if and only if the query has ended. + * + * @return boolean + */ + public function hasEnded() + { + return $this->_endedMicrotime !== null; + } + + /** + * Get the original SQL text of the query. + * + * @return string + */ + public function getQuery() + { + return $this->_query; + } + + /** + * Get the type of this query (one of the Zend_Db_Profiler::* constants) + * + * @return integer + */ + public function getQueryType() + { + return $this->_queryType; + } + + /** + * @param string $param + * @param mixed $variable + * @return void + */ + public function bindParam($param, $variable) + { + $this->_boundParams[$param] = $variable; + } + + /** + * @param array $param + * @return void + */ + public function bindParams(array $params) + { + if (array_key_exists(0, $params)) { + array_unshift($params, null); + unset($params[0]); + } + foreach ($params as $param => $value) { + $this->bindParam($param, $value); + } + } + + /** + * @return array + */ + public function getQueryParams() + { + return $this->_boundParams; + } + + /** + * Get the elapsed time (in seconds) that the query ran. + * If the query has not yet ended, false is returned. + * + * @return float|false + */ + public function getElapsedSecs() + { + if (null === $this->_endedMicrotime) { + return false; + } + + return $this->_endedMicrotime - $this->_startedMicrotime; + } + + /** + * Get the time (in seconds) when the profiler started running. + * + * @return bool|float + */ + public function getStartedMicrotime() + { + if (null === $this->_startedMicrotime) { + return false; + } + + return $this->_startedMicrotime; + } +} diff --git a/vendor/gipfl/zfdb/src/Select.php b/vendor/gipfl/zfdb/src/Select.php new file mode 100644 index 0000000..3a66e86 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Select.php @@ -0,0 +1,1350 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb; + +use gipfl\ZfDb\Adapter\Adapter; +use gipfl\ZfDb\Exception\DbException; +use gipfl\ZfDb\Exception\SelectException; +use PDOStatement; + +/** + * Class for SQL SELECT generation and results. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Select +{ + const DISTINCT = 'distinct'; + const COLUMNS = 'columns'; + const FROM = 'from'; + const UNION = 'union'; + const WHERE = 'where'; + const GROUP = 'group'; + const HAVING = 'having'; + const ORDER = 'order'; + const LIMIT_COUNT = 'limitcount'; + const LIMIT_OFFSET = 'limitoffset'; + const FOR_UPDATE = 'forupdate'; + + const INNER_JOIN = 'inner join'; + const LEFT_JOIN = 'left join'; + const RIGHT_JOIN = 'right join'; + const FULL_JOIN = 'full join'; + const CROSS_JOIN = 'cross join'; + const NATURAL_JOIN = 'natural join'; + + const SQL_WILDCARD = '*'; + const SQL_SELECT = 'SELECT'; + const SQL_UNION = 'UNION'; + const SQL_UNION_ALL = 'UNION ALL'; + const SQL_FROM = 'FROM'; + const SQL_WHERE = 'WHERE'; + const SQL_DISTINCT = 'DISTINCT'; + const SQL_GROUP_BY = 'GROUP BY'; + const SQL_ORDER_BY = 'ORDER BY'; + const SQL_HAVING = 'HAVING'; + const SQL_FOR_UPDATE = 'FOR UPDATE'; + const SQL_AND = 'AND'; + const SQL_AS = 'AS'; + const SQL_OR = 'OR'; + const SQL_ON = 'ON'; + const SQL_ASC = 'ASC'; + const SQL_DESC = 'DESC'; + + const REGEX_COLUMN_EXPR = '/^([\w]*\s*\(([^\(\)]|(?1))*\))$/'; + const REGEX_COLUMN_EXPR_ORDER = '/^([\w]+\s*\(([^\(\)]|(?1))*\))$/'; + const REGEX_COLUMN_EXPR_GROUP = '/^([\w]+\s*\(([^\(\)]|(?1))*\))$/'; + + // @see http://stackoverflow.com/a/13823184/2028814 + const REGEX_SQL_COMMENTS = '@ + (([\'"]).*?[^\\\]\2) # $1 : Skip single & double quoted expressions + |( # $3 : Match comments + (?:\#|--).*?$ # - Single line comments + | # - Multi line (nested) comments + /\* # . comment open marker + (?: [^/*] # . non comment-marker characters + |/(?!\*) # . ! not a comment open + |\*(?!/) # . ! not a comment close + |(?R) # . recursive case + )* # . repeat eventually + \*\/ # . comment close marker + )\s* # Trim after comments + |(?<=;)\s+ # Trim after semi-colon + @msx'; + + /** + * Bind variables for query + * + * @var array + */ + protected $_bind = array(); + + /** + * Zend_Db_Adapter_Abstract object. + * + * @var Adapter + */ + protected $_adapter; + + /** + * The initial values for the $_parts array. + * NOTE: It is important for the 'FOR_UPDATE' part to be last to ensure + * meximum compatibility with database adapters. + * + * @var array + */ + protected static $_partsInit = array( + self::DISTINCT => false, + self::COLUMNS => array(), + self::UNION => array(), + self::FROM => array(), + self::WHERE => array(), + self::GROUP => array(), + self::HAVING => array(), + self::ORDER => array(), + self::LIMIT_COUNT => null, + self::LIMIT_OFFSET => null, + self::FOR_UPDATE => false + ); + + /** + * Specify legal join types. + * + * @var array + */ + protected static $_joinTypes = array( + self::INNER_JOIN, + self::LEFT_JOIN, + self::RIGHT_JOIN, + self::FULL_JOIN, + self::CROSS_JOIN, + self::NATURAL_JOIN, + ); + + /** + * Specify legal union types. + * + * @var array + */ + protected static $_unionTypes = array( + self::SQL_UNION, + self::SQL_UNION_ALL + ); + + /** + * The component parts of a SELECT statement. + * Initialized to the $_partsInit array in the constructor. + * + * @var array + */ + protected $_parts = array(); + + /** + * Tracks which columns are being select from each table and join. + * + * @var array + */ + protected $_tableCols = array(); + + /** + * Class constructor + * + * @param Adapter $adapter + */ + public function __construct(Adapter $adapter) + { + $this->_adapter = $adapter; + $this->_parts = self::$_partsInit; + } + + /** + * Get bind variables + * + * @return array + */ + public function getBind() + { + return $this->_bind; + } + + /** + * Set bind variables + * + * @param mixed $bind + * @return Select + */ + public function bind($bind) + { + $this->_bind = $bind; + + return $this; + } + + /** + * Makes the query SELECT DISTINCT. + * + * @param bool $flag Whether or not the SELECT is DISTINCT (default true). + * @return Select This Zend_Db_Select object. + */ + public function distinct($flag = true) + { + $this->_parts[self::DISTINCT] = (bool) $flag; + return $this; + } + + /** + * Adds a FROM table and optional columns to the query. + * + * The first parameter $name can be a simple string, in which case the + * correlation name is generated automatically. If you want to specify + * the correlation name, the first parameter must be an associative + * array in which the key is the correlation name, and the value is + * the physical table name. For example, array('alias' => 'table'). + * The correlation name is prepended to all columns fetched for this + * table. + * + * The second parameter can be a single string or Zend_Db_Expr object, + * or else an array of strings or Zend_Db_Expr objects. + * + * The first parameter can be null or an empty string, in which case + * no correlation name is generated or prepended to the columns named + * in the second parameter. + * + * @param array|string|Expr $name The table name or an associative array + * relating correlation name to table name. + * @param array|string|Expr $cols The columns to select from this table. + * @param string $schema The schema name to specify, if any. + * @return Select This Zend_Db_Select object. + */ + public function from($name, $cols = '*', $schema = null) + { + return $this->_join(self::FROM, $name, null, $cols, $schema); + } + + /** + * Specifies the columns used in the FROM clause. + * + * The parameter can be a single string or Zend_Db_Expr object, + * or else an array of strings or Zend_Db_Expr objects. + * + * @param array|string|Expr $cols The columns to select from this table. + * @param string $correlationName Correlation name of target table. OPTIONAL + * @return Select This Zend_Db_Select object. + */ + public function columns($cols = '*', $correlationName = null) + { + if ($correlationName === null && count($this->_parts[self::FROM])) { + $correlationNameKeys = array_keys($this->_parts[self::FROM]); + $correlationName = current($correlationNameKeys); + } + + if (!array_key_exists($correlationName, $this->_parts[self::FROM])) { + throw new SelectException("No table has been specified for the FROM clause"); + } + + $this->_tableCols($correlationName, $cols); + + return $this; + } + + /** + * Adds a UNION clause to the query. + * + * The first parameter has to be an array of Zend_Db_Select or + * sql query strings. + * + * <code> + * $sql1 = $db->select(); + * $sql2 = "SELECT ..."; + * $select = $db->select() + * ->union(array($sql1, $sql2)) + * ->order("id"); + * </code> + * + * @param array $select Array of select clauses for the union. + * @return Select This Zend_Db_Select object. + */ + public function union($select = array(), $type = self::SQL_UNION) + { + if (!is_array($select)) { + throw new SelectException( + "union() only accepts an array of Zend_Db_Select instances of sql query strings." + ); + } + + if (!in_array($type, self::$_unionTypes)) { + throw new SelectException("Invalid union type '{$type}'"); + } + + foreach ($select as $target) { + $this->_parts[self::UNION][] = array($target, $type); + } + + return $this; + } + + /** + * Adds a JOIN table and columns to the query. + * + * The $name and $cols parameters follow the same logic + * as described in the from() method. + * + * @param array|string|Expr $name The table name. + * @param string $cond Join on this condition. + * @param array|string $cols The columns to select from the joined table. + * @param string $schema The database name to specify, if any. + * @return Select This Zend_Db_Select object. + */ + public function join($name, $cond, $cols = self::SQL_WILDCARD, $schema = null) + { + return $this->joinInner($name, $cond, $cols, $schema); + } + + /** + * Add an INNER JOIN table and colums to the query + * Rows in both tables are matched according to the expression + * in the $cond argument. The result set is comprised + * of all cases where rows from the left table match + * rows from the right table. + * + * The $name and $cols parameters follow the same logic + * as described in the from() method. + * + * @param array|string|Expr $name The table name. + * @param string $cond Join on this condition. + * @param array|string $cols The columns to select from the joined table. + * @param string $schema The database name to specify, if any. + * @return Select This Zend_Db_Select object. + */ + public function joinInner($name, $cond, $cols = self::SQL_WILDCARD, $schema = null) + { + return $this->_join(self::INNER_JOIN, $name, $cond, $cols, $schema); + } + + /** + * Add a LEFT OUTER JOIN table and colums to the query + * All rows from the left operand table are included, + * matching rows from the right operand table included, + * and the columns from the right operand table are filled + * with NULLs if no row exists matching the left table. + * + * The $name and $cols parameters follow the same logic + * as described in the from() method. + * + * @param array|string|Expr $name The table name. + * @param string $cond Join on this condition. + * @param array|string $cols The columns to select from the joined table. + * @param string $schema The database name to specify, if any. + * @return Select This Zend_Db_Select object. + */ + public function joinLeft($name, $cond, $cols = self::SQL_WILDCARD, $schema = null) + { + return $this->_join(self::LEFT_JOIN, $name, $cond, $cols, $schema); + } + + /** + * Add a RIGHT OUTER JOIN table and colums to the query. + * Right outer join is the complement of left outer join. + * All rows from the right operand table are included, + * matching rows from the left operand table included, + * and the columns from the left operand table are filled + * with NULLs if no row exists matching the right table. + * + * The $name and $cols parameters follow the same logic + * as described in the from() method. + * + * @param array|string|Expr $name The table name. + * @param string $cond Join on this condition. + * @param array|string $cols The columns to select from the joined table. + * @param string $schema The database name to specify, if any. + * @return Select This Zend_Db_Select object. + */ + public function joinRight($name, $cond, $cols = self::SQL_WILDCARD, $schema = null) + { + return $this->_join(self::RIGHT_JOIN, $name, $cond, $cols, $schema); + } + + /** + * Add a FULL OUTER JOIN table and colums to the query. + * A full outer join is like combining a left outer join + * and a right outer join. All rows from both tables are + * included, paired with each other on the same row of the + * result set if they satisfy the join condition, and otherwise + * paired with NULLs in place of columns from the other table. + * + * The $name and $cols parameters follow the same logic + * as described in the from() method. + * + * @param array|string|Expr $name The table name. + * @param string $cond Join on this condition. + * @param array|string $cols The columns to select from the joined table. + * @param string $schema The database name to specify, if any. + * @return Select This Zend_Db_Select object. + */ + public function joinFull($name, $cond, $cols = self::SQL_WILDCARD, $schema = null) + { + return $this->_join(self::FULL_JOIN, $name, $cond, $cols, $schema); + } + + /** + * Add a CROSS JOIN table and colums to the query. + * A cross join is a cartesian product; there is no join condition. + * + * The $name and $cols parameters follow the same logic + * as described in the from() method. + * + * @param array|string|Expr $name The table name. + * @param array|string $cols The columns to select from the joined table. + * @param string $schema The database name to specify, if any. + * @return Select This Zend_Db_Select object. + */ + public function joinCross($name, $cols = self::SQL_WILDCARD, $schema = null) + { + return $this->_join(self::CROSS_JOIN, $name, null, $cols, $schema); + } + + /** + * Add a NATURAL JOIN table and colums to the query. + * A natural join assumes an equi-join across any column(s) + * that appear with the same name in both tables. + * Only natural inner joins are supported by this API, + * even though SQL permits natural outer joins as well. + * + * The $name and $cols parameters follow the same logic + * as described in the from() method. + * + * @param array|string|Expr $name The table name. + * @param array|string $cols The columns to select from the joined table. + * @param string $schema The database name to specify, if any. + * @return Select This Zend_Db_Select object. + */ + public function joinNatural($name, $cols = self::SQL_WILDCARD, $schema = null) + { + return $this->_join(self::NATURAL_JOIN, $name, null, $cols, $schema); + } + + /** + * Adds a WHERE condition to the query by AND. + * + * If a value is passed as the second param, it will be quoted + * and replaced into the condition wherever a question-mark + * appears. Array values are quoted and comma-separated. + * + * <code> + * // simplest but non-secure + * $select->where("id = $id"); + * + * // secure (ID is quoted but matched anyway) + * $select->where('id = ?', $id); + * + * // alternatively, with named binding + * $select->where('id = :id'); + * </code> + * + * Note that it is more correct to use named bindings in your + * queries for values other than strings. When you use named + * bindings, don't forget to pass the values when actually + * making a query: + * + * <code> + * $db->fetchAll($select, array('id' => 5)); + * </code> + * + * @param string $cond The WHERE condition. + * @param mixed $value OPTIONAL The value to quote into the condition. + * @param int $type OPTIONAL The type of the given value + * @return Select This Zend_Db_Select object. + */ + public function where($cond, $value = null, $type = null) + { + $this->_parts[self::WHERE][] = $this->_where($cond, $value, $type, true); + + return $this; + } + + /** + * Adds a WHERE condition to the query by OR. + * + * Otherwise identical to where(). + * + * @param string $cond The WHERE condition. + * @param mixed $value OPTIONAL The value to quote into the condition. + * @param int $type OPTIONAL The type of the given value + * @return Select This Zend_Db_Select object. + * + * @see where() + */ + public function orWhere($cond, $value = null, $type = null) + { + $this->_parts[self::WHERE][] = $this->_where($cond, $value, $type, false); + + return $this; + } + + /** + * Adds grouping to the query. + * + * @param array|string $spec The column(s) to group by. + * @return Select This Zend_Db_Select object. + */ + public function group($spec) + { + if (!is_array($spec)) { + $spec = array($spec); + } + + foreach ($spec as $val) { + // Remove comments from SQL statement + $noComments = preg_replace(self::REGEX_SQL_COMMENTS, '$1', (string) $val); + if (preg_match(self::REGEX_COLUMN_EXPR_GROUP, $noComments)) { + $val = new Expr($val); + } + $this->_parts[self::GROUP][] = $val; + } + + return $this; + } + + /** + * Adds a HAVING condition to the query by AND. + * + * If a value is passed as the second param, it will be quoted + * and replaced into the condition wherever a question-mark + * appears. See {@link where()} for an example + * + * @param string $cond The HAVING condition. + * @param mixed $value OPTIONAL The value to quote into the condition. + * @param int $type OPTIONAL The type of the given value + * @return Select This Zend_Db_Select object. + */ + public function having($cond, $value = null, $type = null) + { + if ($value !== null) { + $cond = $this->_adapter->quoteInto($cond, $value, $type); + } + + if ($this->_parts[self::HAVING]) { + $this->_parts[self::HAVING][] = self::SQL_AND . " ($cond)"; + } else { + $this->_parts[self::HAVING][] = "($cond)"; + } + + return $this; + } + + /** + * Adds a HAVING condition to the query by OR. + * + * Otherwise identical to orHaving(). + * + * @param string $cond The HAVING condition. + * @param mixed $value OPTIONAL The value to quote into the condition. + * @param int $type OPTIONAL The type of the given value + * @return Select This Zend_Db_Select object. + * + * @see having() + */ + public function orHaving($cond, $value = null, $type = null) + { + if ($value !== null) { + $cond = $this->_adapter->quoteInto($cond, $value, $type); + } + + if ($this->_parts[self::HAVING]) { + $this->_parts[self::HAVING][] = self::SQL_OR . " ($cond)"; + } else { + $this->_parts[self::HAVING][] = "($cond)"; + } + + return $this; + } + + /** + * Adds a row order to the query. + * + * @param mixed $spec The column(s) and direction to order by. + * @return Select This Zend_Db_Select object. + */ + public function order($spec) + { + if (!is_array($spec)) { + $spec = array($spec); + } + + // force 'ASC' or 'DESC' on each order spec, default is ASC. + foreach ($spec as $val) { + if ($val instanceof Expr) { + $expr = $val->__toString(); + if (empty($expr)) { + continue; + } + $this->_parts[self::ORDER][] = $val; + } else { + if (empty($val)) { + continue; + } + $direction = self::SQL_ASC; + if (preg_match('/(.*\W)(' . self::SQL_ASC . '|' . self::SQL_DESC . ')\b/si', $val, $matches)) { + $val = trim($matches[1]); + $direction = $matches[2]; + } + // Remove comments from SQL statement + $noComments = preg_replace(self::REGEX_SQL_COMMENTS, '$1', (string) $val); + if (preg_match(self::REGEX_COLUMN_EXPR_ORDER, $noComments)) { + $val = new Expr($val); + } + $this->_parts[self::ORDER][] = array($val, $direction); + } + } + + return $this; + } + + /** + * Sets a limit count and offset to the query. + * + * @param int $count OPTIONAL The number of rows to return. + * @param int $offset OPTIONAL Start returning after this many rows. + * @return Select This Zend_Db_Select object. + */ + public function limit($count = null, $offset = null) + { + $this->_parts[self::LIMIT_COUNT] = (int) $count; + $this->_parts[self::LIMIT_OFFSET] = (int) $offset; + return $this; + } + + /** + * Sets the limit and count by page number. + * + * @param int $page Limit results to this page number. + * @param int $rowCount Use this many rows per page. + * @return Select This Zend_Db_Select object. + */ + public function limitPage($page, $rowCount) + { + $page = ($page > 0) ? $page : 1; + $rowCount = ($rowCount > 0) ? $rowCount : 1; + $this->_parts[self::LIMIT_COUNT] = (int) $rowCount; + $this->_parts[self::LIMIT_OFFSET] = (int) $rowCount * ($page - 1); + return $this; + } + + /** + * Makes the query SELECT FOR UPDATE. + * + * @param bool $flag Whether or not the SELECT is FOR UPDATE (default true). + * @return Select This Zend_Db_Select object. + */ + public function forUpdate($flag = true) + { + $this->_parts[self::FOR_UPDATE] = (bool) $flag; + return $this; + } + + /** + * Get part of the structured information for the current query. + * + * @param string $part + * @return mixed + * @throws SelectException + */ + public function getPart($part) + { + $part = strtolower($part); + if (!array_key_exists($part, $this->_parts)) { + throw new SelectException("Invalid Select part '$part'"); + } + return $this->_parts[$part]; + } + + /** + * Executes the current select object and returns the result + * + * @param integer $fetchMode OPTIONAL + * @param mixed $bind An array of data to bind to the placeholders. + * @return PDOStatement|Statement + */ + public function query($fetchMode = null, $bind = array()) + { + if (!empty($bind)) { + $this->bind($bind); + } + + $stmt = $this->_adapter->query($this); + if ($fetchMode == null) { + $fetchMode = $this->_adapter->getFetchMode(); + } + $stmt->setFetchMode($fetchMode); + return $stmt; + } + + /** + * Converts this object to an SQL SELECT string. + * + * @return string|null This object as a SELECT string. (or null if a string cannot be produced.) + * @throws SelectException + */ + public function assemble() + { + $sql = self::SQL_SELECT; + foreach (array_keys(self::$_partsInit) as $part) { + $method = '_render' . ucfirst($part); + if (method_exists($this, $method)) { + $sql = $this->$method($sql); + } + } + return $sql; + } + + /** + * Clear parts of the Select object, or an individual part. + * + * @param string $part OPTIONAL + * @return Select + */ + public function reset($part = null) + { + if ($part == null) { + $this->_parts = self::$_partsInit; + } elseif (array_key_exists($part, self::$_partsInit)) { + $this->_parts[$part] = self::$_partsInit[$part]; + } + return $this; + } + + /** + * Gets the Zend_Db_Adapter_Abstract for this + * particular Zend_Db_Select object. + * + * @return Adapter + */ + public function getAdapter() + { + return $this->_adapter; + } + + /** + * Populate the {@link $_parts} 'join' key + * + * Does the dirty work of populating the join key. + * + * The $name and $cols parameters follow the same logic + * as described in the from() method. + * + * @param null|string $type Type of join; inner, left, and null are currently supported + * @param array|string|Expr $name Table name + * @param string $cond Join on this condition + * @param array|string $cols The columns to select from the joined table + * @param string $schema The database name to specify, if any. + * @return Select This Zend_Db_Select object + * @throws SelectException + */ + protected function _join($type, $name, $cond, $cols, $schema = null) + { + if (!in_array($type, self::$_joinTypes) && $type != self::FROM) { + throw new SelectException("Invalid join type '$type'"); + } + + if (count($this->_parts[self::UNION])) { + throw new SelectException("Invalid use of table with " . self::SQL_UNION); + } + + if (empty($name)) { + $correlationName = $tableName = ''; + } elseif (is_array($name)) { + // Must be array($correlationName => $tableName) or array($ident, ...) + foreach ($name as $_correlationName => $_tableName) { + if (is_string($_correlationName)) { + // We assume the key is the correlation name and value is the table name + $tableName = $_tableName; + $correlationName = $_correlationName; + } else { + // We assume just an array of identifiers, with no correlation name + $tableName = $_tableName; + $correlationName = $this->_uniqueCorrelation($tableName); + } + break; + } + } elseif ($name instanceof Expr|| $name instanceof Select) { + $tableName = $name; + $correlationName = $this->_uniqueCorrelation('t'); + } elseif (preg_match('/^(.+)\s+AS\s+(.+)$/i', $name, $m)) { + $tableName = $m[1]; + $correlationName = $m[2]; + } else { + $tableName = $name; + $correlationName = $this->_uniqueCorrelation($tableName); + } + + // Schema from table name overrides schema argument + if (!is_object($tableName) && false !== strpos($tableName, '.')) { + list($schema, $tableName) = explode('.', $tableName); + } + + $lastFromCorrelationName = null; + if (!empty($correlationName)) { + if (array_key_exists($correlationName, $this->_parts[self::FROM])) { + /** + * @see SelectException + */ + throw new SelectException("You cannot define a correlation name '$correlationName' more than once"); + } + + if ($type == self::FROM) { + // append this from after the last from joinType + $tmpFromParts = $this->_parts[self::FROM]; + $this->_parts[self::FROM] = array(); + // move all the froms onto the stack + while ($tmpFromParts) { + $currentCorrelationName = key($tmpFromParts); + if ($tmpFromParts[$currentCorrelationName]['joinType'] != self::FROM) { + break; + } + $lastFromCorrelationName = $currentCorrelationName; + $this->_parts[self::FROM][$currentCorrelationName] = array_shift($tmpFromParts); + } + } else { + $tmpFromParts = array(); + } + $this->_parts[self::FROM][$correlationName] = array( + 'joinType' => $type, + 'schema' => $schema, + 'tableName' => $tableName, + 'joinCondition' => $cond + ); + while ($tmpFromParts) { + $currentCorrelationName = key($tmpFromParts); + $this->_parts[self::FROM][$currentCorrelationName] = array_shift($tmpFromParts); + } + } + + // add to the columns from this joined table + if ($type == self::FROM && $lastFromCorrelationName == null) { + $lastFromCorrelationName = true; + } + $this->_tableCols($correlationName, $cols, $lastFromCorrelationName); + + return $this; + } + + /** + * Handle JOIN... USING... syntax + * + * This is functionality identical to the existing JOIN methods, however + * the join condition can be passed as a single column name. This method + * then completes the ON condition by using the same field for the FROM + * table and the JOIN table. + * + * <code> + * $select = $db->select()->from('table1') + * ->joinUsing('table2', 'column1'); + * + * // SELECT * FROM table1 JOIN table2 ON table1.column1 = table2.column2 + * </code> + * + * These joins are called by the developer simply by adding 'Using' to the + * method name. E.g. + * * joinUsing + * * joinInnerUsing + * * joinFullUsing + * * joinRightUsing + * * joinLeftUsing + * + * @return Select This Zend_Db_Select object. + */ + public function _joinUsing($type, $name, $cond, $cols = '*', $schema = null) + { + if (empty($this->_parts[self::FROM])) { + throw new SelectException("You can only perform a joinUsing after specifying a FROM table"); + } + + $join = $this->_adapter->quoteIdentifier(key($this->_parts[self::FROM]), true); + $from = $this->_adapter->quoteIdentifier($this->_uniqueCorrelation($name), true); + + $joinCond = array(); + foreach ((array)$cond as $fieldName) { + $cond1 = $from . '.' . $fieldName; + $cond2 = $join . '.' . $fieldName; + $joinCond[] = $cond1 . ' = ' . $cond2; + } + $cond = implode(' '.self::SQL_AND.' ', $joinCond); + + return $this->_join($type, $name, $cond, $cols, $schema); + } + + /** + * Generate a unique correlation name + * + * @param string|array $name A qualified identifier. + * @return string A unique correlation name. + */ + private function _uniqueCorrelation($name) + { + if (is_array($name)) { + $k = key($name); + $c = is_string($k) ? $k : end($name); + } else { + // Extract just the last name of a qualified table name + $dot = strrpos($name, '.'); + $c = ($dot === false) ? $name : substr($name, $dot+1); + } + for ($i = 2; array_key_exists($c, $this->_parts[self::FROM]); ++$i) { + $c = $name . '_' . (string) $i; + } + return $c; + } + + /** + * Adds to the internal table-to-column mapping array. + * + * @param string $tbl The table/join the columns come from. + * @param array|string $cols The list of columns; preferably as + * an array, but possibly as a string containing one column. + * @param bool|string True if it should be prepended, a correlation name if it should be inserted + * @return void + */ + protected function _tableCols($correlationName, $cols, $afterCorrelationName = null) + { + if (!is_array($cols)) { + $cols = array($cols); + } + + if ($correlationName == null) { + $correlationName = ''; + } + + $columnValues = array(); + + foreach (array_filter($cols) as $alias => $col) { + $currentCorrelationName = $correlationName; + if (is_string($col)) { + // Check for a column matching "<column> AS <alias>" and extract the alias name + $col = trim(str_replace("\n", ' ', $col)); + if (preg_match('/^(.+)\s+' . self::SQL_AS . '\s+(.+)$/i', $col, $m)) { + $col = $m[1]; + $alias = $m[2]; + } + // Check for columns that look like functions and convert to Zend_Db_Expr + if (preg_match(self::REGEX_COLUMN_EXPR, (string) $col)) { + $col = new Expr($col); + } elseif (preg_match('/(.+)\.(.+)/', $col, $m)) { + $currentCorrelationName = $m[1]; + $col = $m[2]; + } + } + $columnValues[] = array($currentCorrelationName, $col, is_string($alias) ? $alias : null); + } + + if ($columnValues) { + // should we attempt to prepend or insert these values? + if ($afterCorrelationName === true || is_string($afterCorrelationName)) { + $tmpColumns = $this->_parts[self::COLUMNS]; + $this->_parts[self::COLUMNS] = array(); + } else { + $tmpColumns = array(); + } + + // find the correlation name to insert after + if (is_string($afterCorrelationName)) { + while ($tmpColumns) { + $this->_parts[self::COLUMNS][] = $currentColumn = array_shift($tmpColumns); + if ($currentColumn[0] == $afterCorrelationName) { + break; + } + } + } + + // apply current values to current stack + foreach ($columnValues as $columnValue) { + array_push($this->_parts[self::COLUMNS], $columnValue); + } + + // finish ensuring that all previous values are applied (if they exist) + while ($tmpColumns) { + array_push($this->_parts[self::COLUMNS], array_shift($tmpColumns)); + } + } + } + + /** + * Internal function for creating the where clause + * + * @param string $condition + * @param mixed $value optional + * @param string $type optional + * @param boolean $bool true = AND, false = OR + * @return string clause + */ + protected function _where($condition, $value = null, $type = null, $bool = true) + { + if (count($this->_parts[self::UNION])) { + throw new SelectException("Invalid use of where clause with " . self::SQL_UNION); + } + + if ($value !== null) { + $condition = $this->_adapter->quoteInto($condition, $value, $type); + } + + $cond = ""; + if ($this->_parts[self::WHERE]) { + if ($bool === true) { + $cond = self::SQL_AND . ' '; + } else { + $cond = self::SQL_OR . ' '; + } + } + + return $cond . "($condition)"; + } + + /** + * @return array + */ + protected function _getDummyTable() + { + return array(); + } + + /** + * Return a quoted schema name + * + * @param string $schema The schema name OPTIONAL + * @return string|null + */ + protected function _getQuotedSchema($schema = null) + { + if ($schema === null) { + return null; + } + return $this->_adapter->quoteIdentifier($schema, true) . '.'; + } + + /** + * Return a quoted table name + * + * @param string $tableName The table name + * @param string $correlationName The correlation name OPTIONAL + * @return string + */ + protected function _getQuotedTable($tableName, $correlationName = null) + { + return $this->_adapter->quoteTableAs($tableName, $correlationName, true); + } + + /** + * Render DISTINCT clause + * + * @param string $sql SQL query + * @return string + */ + protected function _renderDistinct($sql) + { + if ($this->_parts[self::DISTINCT]) { + $sql .= ' ' . self::SQL_DISTINCT; + } + + return $sql; + } + + /** + * Render DISTINCT clause + * + * @param string $sql SQL query + * @return string|null + */ + protected function _renderColumns($sql) + { + if (!count($this->_parts[self::COLUMNS])) { + return null; + } + + $columns = array(); + foreach ($this->_parts[self::COLUMNS] as $columnEntry) { + list($correlationName, $column, $alias) = $columnEntry; + if ($column instanceof Expr) { + $columns[] = $this->_adapter->quoteColumnAs($column, $alias, true); + } else { + if ($column == self::SQL_WILDCARD) { + $column = new Expr(self::SQL_WILDCARD); + $alias = null; + } + if (empty($correlationName)) { + $columns[] = $this->_adapter->quoteColumnAs($column, $alias, true); + } else { + $columns[] = $this->_adapter->quoteColumnAs(array($correlationName, $column), $alias, true); + } + } + } + + return $sql . ' ' . implode(', ', $columns); + } + + /** + * Render FROM clause + * + * @param string $sql SQL query + * @return string + */ + protected function _renderFrom($sql) + { + /* + * If no table specified, use RDBMS-dependent solution + * for table-less query. e.g. DUAL in Oracle. + */ + if (empty($this->_parts[self::FROM])) { + $this->_parts[self::FROM] = $this->_getDummyTable(); + } + + $from = array(); + + foreach ($this->_parts[self::FROM] as $correlationName => $table) { + $tmp = ''; + + $joinType = ($table['joinType'] == self::FROM) ? self::INNER_JOIN : $table['joinType']; + + // Add join clause (if applicable) + if (! empty($from)) { + $tmp .= ' ' . strtoupper($joinType) . ' '; + } + + $tmp .= $this->_getQuotedSchema($table['schema']); + $tmp .= $this->_getQuotedTable($table['tableName'], $correlationName); + + // Add join conditions (if applicable) + if (!empty($from) && ! empty($table['joinCondition'])) { + $tmp .= ' ' . self::SQL_ON . ' ' . $table['joinCondition']; + } + + // Add the table name and condition add to the list + $from[] = $tmp; + } + + // Add the list of all joins + if (!empty($from)) { + $sql .= ' ' . self::SQL_FROM . ' ' . implode("\n", $from); + } + + return $sql; + } + + /** + * Render UNION query + * + * @param string $sql SQL query + * @return string + */ + protected function _renderUnion($sql) + { + if ($this->_parts[self::UNION]) { + $parts = count($this->_parts[self::UNION]); + foreach ($this->_parts[self::UNION] as $cnt => $union) { + list($target, $type) = $union; + if ($target instanceof Select) { + $target = $target->assemble(); + } + $sql .= $target; + if ($cnt < $parts - 1) { + $sql .= ' ' . $type . ' '; + } + } + } + + return $sql; + } + + /** + * Render WHERE clause + * + * @param string $sql SQL query + * @return string + */ + protected function _renderWhere($sql) + { + if ($this->_parts[self::FROM] && $this->_parts[self::WHERE]) { + $sql .= ' ' . self::SQL_WHERE . ' ' . implode(' ', $this->_parts[self::WHERE]); + } + + return $sql; + } + + /** + * Render GROUP clause + * + * @param string $sql SQL query + * @return string + */ + protected function _renderGroup($sql) + { + if ($this->_parts[self::FROM] && $this->_parts[self::GROUP]) { + $group = array(); + foreach ($this->_parts[self::GROUP] as $term) { + $group[] = $this->_adapter->quoteIdentifier($term, true); + } + $sql .= ' ' . self::SQL_GROUP_BY . ' ' . implode(",\n\t", $group); + } + + return $sql; + } + + /** + * Render HAVING clause + * + * @param string $sql SQL query + * @return string + */ + protected function _renderHaving($sql) + { + if ($this->_parts[self::FROM] && $this->_parts[self::HAVING]) { + $sql .= ' ' . self::SQL_HAVING . ' ' . implode(' ', $this->_parts[self::HAVING]); + } + + return $sql; + } + + /** + * Render ORDER clause + * + * @param string $sql SQL query + * @return string + */ + protected function _renderOrder($sql) + { + if ($this->_parts[self::ORDER]) { + $order = array(); + foreach ($this->_parts[self::ORDER] as $term) { + if (is_array($term)) { + if (is_numeric($term[0]) && strval(intval($term[0])) == $term[0]) { + $order[] = (int)trim($term[0]) . ' ' . $term[1]; + } else { + $order[] = $this->_adapter->quoteIdentifier($term[0], true) . ' ' . $term[1]; + } + } elseif (is_numeric($term) && strval(intval($term)) == $term) { + $order[] = (int)trim($term); + } else { + $order[] = $this->_adapter->quoteIdentifier($term, true); + } + } + $sql .= ' ' . self::SQL_ORDER_BY . ' ' . implode(', ', $order); + } + + return $sql; + } + + /** + * Render LIMIT OFFSET clause + * + * @param string $sql SQL query + * @return string + */ + protected function _renderLimitoffset($sql) + { + $count = 0; + $offset = 0; + + if (!empty($this->_parts[self::LIMIT_OFFSET])) { + $offset = (int) $this->_parts[self::LIMIT_OFFSET]; + $count = PHP_INT_MAX; + } + + if (!empty($this->_parts[self::LIMIT_COUNT])) { + $count = (int) $this->_parts[self::LIMIT_COUNT]; + } + + /* + * Add limits clause + */ + if ($count > 0) { + $sql = trim($this->_adapter->limit($sql, $count, $offset)); + } + + return $sql; + } + + /** + * Render FOR UPDATE clause + * + * @param string $sql SQL query + * @return string + */ + protected function _renderForupdate($sql) + { + if ($this->_parts[self::FOR_UPDATE]) { + $sql .= ' ' . self::SQL_FOR_UPDATE; + } + + return $sql; + } + + /** + * Turn magic function calls into non-magic function calls + * for joinUsing syntax + * + * @param string $method + * @param array $args OPTIONAL Zend_Db_Table_Select query modifier + * @return Select + * @throws SelectException If an invalid method is called. + */ + public function __call($method, array $args) + { + $matches = array(); + + /** + * Recognize methods for Has-Many cases: + * findParent<Class>() + * findParent<Class>By<Rule>() + * Use the non-greedy pattern repeat modifier e.g. \w+? + */ + if (preg_match('/^join([a-zA-Z]*?)Using$/', $method, $matches)) { + $type = strtolower($matches[1]); + if ($type) { + $type .= ' join'; + if (!in_array($type, self::$_joinTypes)) { + throw new SelectException("Unrecognized method '$method()'"); + } + if (in_array($type, array(self::CROSS_JOIN, self::NATURAL_JOIN))) { + throw new SelectException("Cannot perform a joinUsing with method '$method()'"); + } + } else { + $type = self::INNER_JOIN; + } + array_unshift($args, $type); + return call_user_func_array(array($this, '_joinUsing'), $args); + } + + throw new SelectException("Unrecognized method '$method()'"); + } + + /** + * Implements magic method. + * + * @return string This object as a SELECT string. + */ + public function __toString() + { + try { + $sql = $this->assemble(); + } catch (DbException $e) { + trigger_error($e->getMessage(), E_USER_WARNING); + $sql = ''; + } + return (string)$sql; + } +} diff --git a/vendor/gipfl/zfdb/src/Statement.php b/vendor/gipfl/zfdb/src/Statement.php new file mode 100644 index 0000000..3288be6 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Statement.php @@ -0,0 +1,462 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb; + +use gipfl\ZfDb\Adapter\Adapter; +use gipfl\ZfDb\Profiler\ProfilerQuery; +use gipfl\ZfDb\Statement\Exception\StatementException; +use gipfl\ZfDb\Statement\StatementInterface; + +/** + * Abstract class to emulate a PDOStatement for native database adapters. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +abstract class Statement implements StatementInterface +{ + /** + * @var resource|object The driver level statement object/resource + */ + protected $_stmt = null; + + /** + * @var Adapter + */ + protected $_adapter = null; + + /** + * The current fetch mode. + * + * @var integer + */ + protected $_fetchMode = Db::FETCH_ASSOC; + + /** + * Attributes. + * + * @var array + */ + protected $_attribute = array(); + + /** + * Column result bindings. + * + * @var array + */ + protected $_bindColumn = array(); + + /** + * Query parameter bindings; covers bindParam() and bindValue(). + * + * @var array + */ + protected $_bindParam = array(); + + /** + * SQL string split into an array at placeholders. + * + * @var array + */ + protected $_sqlSplit = array(); + + /** + * Parameter placeholders in the SQL string by position in the split array. + * + * @var array + */ + protected $_sqlParam = array(); + + /** + * @var ProfilerQuery + */ + protected $_queryId = null; + + /** + * Constructor for a statement. + * + * @param Adapter $adapter + * @param mixed $sql Either a string or Zend_Db_Select. + */ + public function __construct($adapter, $sql) + { + $this->_adapter = $adapter; + if ($sql instanceof Select) { + $sql = $sql->assemble(); + } + $this->_parseParameters($sql); + $this->_prepare($sql); + + $this->_queryId = $this->_adapter->getProfiler()->queryStart($sql); + } + + /** + * Internal method called by abstract statment constructor to setup + * the driver level statement + * + * @return void + */ + protected function _prepare($sql) + { + return; + } + + /** + * @param string $sql + * @return void + */ + protected function _parseParameters($sql) + { + $sql = $this->_stripQuoted($sql); + + // split into text and params + $this->_sqlSplit = preg_split( + '/(\?|:[a-zA-Z0-9_]+)/', + $sql, + -1, + PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY + ); + + // map params + $this->_sqlParam = array(); + foreach ($this->_sqlSplit as $key => $val) { + if ($val == '?') { + if ($this->_adapter->supportsParameters('positional') === false) { + throw new StatementException("Invalid bind-variable position '$val'"); + } + } elseif ($val[0] == ':') { + if ($this->_adapter->supportsParameters('named') === false) { + throw new StatementException("Invalid bind-variable name '$val'"); + } + } + $this->_sqlParam[] = $val; + } + + // set up for binding + $this->_bindParam = array(); + } + + /** + * Remove parts of a SQL string that contain quoted strings + * of values or identifiers. + * + * @param string $sql + * @return string + */ + protected function _stripQuoted($sql) + { + // get the character for value quoting + // this should be ' + $q = $this->_adapter->quote('a'); + $q = $q[0]; + // get the value used as an escaped quote, + // e.g. \' or '' + $qe = $this->_adapter->quote($q); + $qe = substr($qe, 1, 2); + $qe = preg_quote($qe); + $escapeChar = substr($qe, 0, 1); + // remove 'foo\'bar' + if (!empty($q)) { + $escapeChar = preg_quote($escapeChar); + // this segfaults only after 65,000 characters instead of 9,000 + $sql = preg_replace("/$q([^$q{$escapeChar}]*|($qe)*)*$q/s", '', $sql); + } + + // get a version of the SQL statement with all quoted + // values and delimited identifiers stripped out + // remove "foo\"bar" + $sql = preg_replace("/\"(\\\\\"|[^\"])*\"/Us", '', $sql); + + // get the character for delimited id quotes, + // this is usually " but in MySQL is ` + $d = $this->_adapter->quoteIdentifier('a'); + $d = $d[0]; + // get the value used as an escaped delimited id quote, + // e.g. \" or "" or \` + $de = $this->_adapter->quoteIdentifier($d); + $de = substr($de, 1, 2); + $de = preg_quote($de); + // Note: $de and $d where never used..., now they are: + $sql = preg_replace("/$d($de|\\\\{2}|[^$d])*$d/Us", '', $sql); + return $sql; + } + + /** + * Bind a column of the statement result set to a PHP variable. + * + * @param string $column Name the column in the result set, either by + * position or by name. + * @param mixed $param Reference to the PHP variable containing the value. + * @param mixed $type OPTIONAL + * @return bool + */ + public function bindColumn($column, &$param, $type = null) + { + $this->_bindColumn[$column] =& $param; + return true; + } + + /** + * Binds a parameter to the specified variable name. + * + * @param mixed $parameter Name the parameter, either integer or string. + * @param mixed $variable Reference to PHP variable containing the value. + * @param mixed $type OPTIONAL Datatype of SQL parameter. + * @param mixed $length OPTIONAL Length of SQL parameter. + * @param mixed $options OPTIONAL Other options. + * @return bool + */ + public function bindParam($parameter, &$variable, $type = null, $length = null, $options = null) + { + if (!is_int($parameter) && !is_string($parameter)) { + throw new StatementException('Invalid bind-variable position'); + } + + $position = null; + if (($intval = (int) $parameter) > 0 && $this->_adapter->supportsParameters('positional')) { + if ($intval >= 1 || $intval <= count($this->_sqlParam)) { + $position = $intval; + } + } elseif ($this->_adapter->supportsParameters('named')) { + if ($parameter[0] != ':') { + $parameter = ':' . $parameter; + } + if (in_array($parameter, $this->_sqlParam) !== false) { + $position = $parameter; + } + } + + if ($position === null) { + throw new StatementException("Invalid bind-variable position '$parameter'"); + } + + // Finally we are assured that $position is valid + $this->_bindParam[$position] =& $variable; + return $this->_bindParam($position, $variable, $type, $length, $options); + } + + /** + * Binds a value to a parameter. + * + * @param mixed $parameter Name the parameter, either integer or string. + * @param mixed $value Scalar value to bind to the parameter. + * @param mixed $type OPTIONAL Datatype of the parameter. + * @return bool + */ + public function bindValue($parameter, $value, $type = null) + { + return $this->bindParam($parameter, $value, $type); + } + + /** + * Executes a prepared statement. + * + * @param array $params OPTIONAL Values to bind to parameter placeholders. + * @return bool + */ + public function execute(array $params = null) + { + /* + * Simple case - no query profiler to manage. + */ + if ($this->_queryId === null) { + return $this->_execute($params); + } + + /* + * Do the same thing, but with query profiler + * management before and after the execute. + */ + $prof = $this->_adapter->getProfiler(); + $qp = $prof->getQueryProfile($this->_queryId); + if ($qp->hasEnded()) { + $this->_queryId = $prof->queryClone($qp); + $qp = $prof->getQueryProfile($this->_queryId); + } + if ($params !== null) { + $qp->bindParams($params); + } else { + $qp->bindParams($this->_bindParam); + } + $qp->start($this->_queryId); + + $retval = $this->_execute($params); + + $prof->queryEnd($this->_queryId); + + return $retval; + } + + /** + * Returns an array containing all of the result set rows. + * + * @param int $style OPTIONAL Fetch mode. + * @param int $col OPTIONAL Column number, if fetch mode is by column. + * @return array Collection of rows, each in a format by the fetch mode. + */ + public function fetchAll($style = null, $col = null) + { + $data = array(); + if ($style === Db::FETCH_COLUMN && $col === null) { + $col = 0; + } + if ($col === null) { + while ($row = $this->fetch($style)) { + $data[] = $row; + } + } else { + while (false !== ($val = $this->fetchColumn($col))) { + $data[] = $val; + } + } + return $data; + } + + /** + * Returns a single column from the next row of a result set. + * + * @param int $col OPTIONAL Position of the column to fetch. + * @return string One value from the next row of result set, or false. + */ + public function fetchColumn($col = 0) + { + $data = array(); + $col = (int) $col; + $row = $this->fetch(Db::FETCH_NUM); + if (!is_array($row)) { + return false; + } + return $row[$col]; + } + + /** + * Fetches the next row and returns it as an object. + * + * @param string $class OPTIONAL Name of the class to create. + * @param array $config OPTIONAL Constructor arguments for the class. + * @return mixed One object instance of the specified class, or false. + */ + public function fetchObject($class = 'stdClass', array $config = array()) + { + $obj = new $class($config); + $row = $this->fetch(Db::FETCH_ASSOC); + if (!is_array($row)) { + return false; + } + foreach ($row as $key => $val) { + $obj->$key = $val; + } + return $obj; + } + + /** + * Retrieve a statement attribute. + * + * @param string $key Attribute name. + * @return mixed Attribute value. + */ + public function getAttribute($key) + { + if (array_key_exists($key, $this->_attribute)) { + return $this->_attribute[$key]; + } + + return null; + } + + /** + * Set a statement attribute. + * + * @param string $key Attribute name. + * @param mixed $val Attribute value. + */ + public function setAttribute($key, $val) + { + $this->_attribute[$key] = $val; + } + + /** + * Set the default fetch mode for this statement. + * + * @param int $mode The fetch mode. + * @return bool + * @throws StatementException + */ + public function setFetchMode($mode) + { + switch ($mode) { + case Db::FETCH_NUM: + case Db::FETCH_ASSOC: + case Db::FETCH_BOTH: + case Db::FETCH_OBJ: + $this->_fetchMode = $mode; + break; + case Db::FETCH_BOUND: + default: + $this->closeCursor(); + throw new StatementException('invalid fetch mode'); + } + + return true; + } + + /** + * Helper function to map retrieved row + * to bound column variables + * + * @param array $row + * @return bool True + */ + public function _fetchBound($row) + { + foreach ($row as $key => $value) { + // bindColumn() takes 1-based integer positions + // but fetch() returns 0-based integer indexes + if (is_int($key)) { + $key++; + } + // set results only to variables that were bound previously + if (isset($this->_bindColumn[$key])) { + $this->_bindColumn[$key] = $value; + } + } + return true; + } + + /** + * Gets the Zend_Db_Adapter_Abstract for this + * particular Zend_Db_Statement object. + * + * @return Adapter + */ + public function getAdapter() + { + return $this->_adapter; + } + + /** + * Gets the resource or object setup by the + * _parse + * @return mixed + */ + public function getDriverStatement() + { + return $this->_stmt; + } +} diff --git a/vendor/gipfl/zfdb/src/Statement/Db2Statement.php b/vendor/gipfl/zfdb/src/Statement/Db2Statement.php new file mode 100644 index 0000000..ebee20f --- /dev/null +++ b/vendor/gipfl/zfdb/src/Statement/Db2Statement.php @@ -0,0 +1,334 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Statement; + +use gipfl\ZfDb\Db; +use gipfl\ZfDb\Statement; +use gipfl\ZfDb\Statement\Exception\StatementExceptionDb2; + +/** + * Extends for DB2 native adapter. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Db2Statement extends Statement +{ + /** + * Column names. + */ + protected $_keys; + + /** + * Fetched result values. + */ + protected $_values; + + /** + * Prepare a statement handle. + * + * @param string $sql + * @return void + * @throws StatementExceptionDb2 + */ + public function _prepare($sql) + { + $connection = $this->_adapter->getConnection(); + + // db2_prepare on i5 emits errors, these need to be + // suppressed so that proper exceptions can be thrown + $this->_stmt = @db2_prepare($connection, $sql); + + if (!$this->_stmt) { + throw new StatementExceptionDb2( + db2_stmt_errormsg(), + db2_stmt_error() + ); + } + } + + /** + * Binds a parameter to the specified variable name. + * + * @param mixed $parameter Name the parameter, either integer or string. + * @param mixed $variable Reference to PHP variable containing the value. + * @param mixed $type OPTIONAL Datatype of SQL parameter. + * @param mixed $length OPTIONAL Length of SQL parameter. + * @param mixed $options OPTIONAL Other options. + * @return bool + * @throws StatementExceptionDb2 + */ + public function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null) + { + if ($type === null) { + $type = DB2_PARAM_IN; + } + + if (isset($options['data-type'])) { + $datatype = $options['data-type']; + } else { + $datatype = DB2_CHAR; + } + + if (!db2_bind_param($this->_stmt, $parameter, "variable", $type, $datatype)) { + throw new StatementExceptionDb2( + db2_stmt_errormsg(), + db2_stmt_error() + ); + } + + return true; + } + + /** + * Closes the cursor, allowing the statement to be executed again. + * + * @return bool + */ + public function closeCursor() + { + if (!$this->_stmt) { + return false; + } + db2_free_stmt($this->_stmt); + $this->_stmt = false; + return true; + } + + + /** + * Returns the number of columns in the result set. + * Returns null if the statement has no result set metadata. + * + * @return int The number of columns. + */ + public function columnCount() + { + if (!$this->_stmt) { + return false; + } + return db2_num_fields($this->_stmt); + } + + /** + * Retrieves the error code, if any, associated with the last operation on + * the statement handle. + * + * @return string error code. + */ + public function errorCode() + { + if (!$this->_stmt) { + return false; + } + + $error = db2_stmt_error(); + if ($error === '') { + return false; + } + + return $error; + } + + /** + * Retrieves an array of error information, if any, associated with the + * last operation on the statement handle. + * + * @return array|false + */ + public function errorInfo() + { + $error = $this->errorCode(); + if ($error === false) { + return false; + } + + /* + * Return three-valued array like PDO. But DB2 does not distinguish + * between SQLCODE and native RDBMS error code, so repeat the SQLCODE. + */ + return array( + $error, + $error, + db2_stmt_errormsg() + ); + } + + /** + * Executes a prepared statement. + * + * @param array $params OPTIONAL Values to bind to parameter placeholders. + * @return bool + * @throws StatementExceptionDb2 + */ + public function _execute(array $params = null) + { + if (!$this->_stmt) { + return false; + } + + $retval = true; + if ($params !== null) { + $retval = @db2_execute($this->_stmt, $params); + } else { + $retval = @db2_execute($this->_stmt); + } + + if ($retval === false) { + throw new StatementExceptionDb2( + db2_stmt_errormsg(), + db2_stmt_error() + ); + } + + $this->_keys = array(); + if ($field_num = $this->columnCount()) { + for ($i = 0; $i < $field_num; $i++) { + $name = db2_field_name($this->_stmt, $i); + $this->_keys[] = $name; + } + } + + $this->_values = array(); + if ($this->_keys) { + $this->_values = array_fill(0, count($this->_keys), null); + } + + return $retval; + } + + /** + * Fetches a row from the result set. + * + * @param int $style OPTIONAL Fetch mode for this fetch operation. + * @param int $cursor OPTIONAL Absolute, relative, or other. + * @param int $offset OPTIONAL Number for absolute or relative cursors. + * @return mixed Array, object, or scalar depending on fetch mode. + * @throws StatementExceptionDb2 + */ + public function fetch($style = null, $cursor = null, $offset = null) + { + if (!$this->_stmt) { + return false; + } + + if ($style === null) { + $style = $this->_fetchMode; + } + + switch ($style) { + case Db::FETCH_NUM: + $row = db2_fetch_array($this->_stmt); + break; + case Db::FETCH_ASSOC: + $row = db2_fetch_assoc($this->_stmt); + break; + case Db::FETCH_BOTH: + $row = db2_fetch_both($this->_stmt); + break; + case Db::FETCH_OBJ: + $row = db2_fetch_object($this->_stmt); + break; + case Db::FETCH_BOUND: + $row = db2_fetch_both($this->_stmt); + if ($row !== false) { + return $this->_fetchBound($row); + } + break; + default: + throw new StatementExceptionDb2("Invalid fetch mode '$style' specified"); + } + + return $row; + } + + /** + * Fetches the next row and returns it as an object. + * + * @param string $class OPTIONAL Name of the class to create. + * @param array $config OPTIONAL Constructor arguments for the class. + * @return mixed One object instance of the specified class. + */ + public function fetchObject($class = 'stdClass', array $config = array()) + { + $obj = $this->fetch(Db::FETCH_OBJ); + return $obj; + } + + /** + * Retrieves the next rowset (result set) for a SQL statement that has + * multiple result sets. An example is a stored procedure that returns + * the results of multiple queries. + * + * @return bool + * @throws StatementExceptionDb2 + */ + public function nextRowset() + { + throw new StatementExceptionDb2(__FUNCTION__ . '() is not implemented'); + } + + /** + * Returns the number of rows affected by the execution of the + * last INSERT, DELETE, or UPDATE statement executed by this + * statement object. + * + * @return int The number of rows affected. + */ + public function rowCount() + { + if (!$this->_stmt) { + return false; + } + + $num = @db2_num_rows($this->_stmt); + + if ($num === false) { + return 0; + } + + return $num; + } + + /** + * Returns an array containing all of the result set rows. + * + * @param int $style OPTIONAL Fetch mode. + * @param int $col OPTIONAL Column number, if fetch mode is by column. + * @return array Collection of rows, each in a format by the fetch mode. + * + * Behaves like parent, but if limit() + * is used, the final result removes the extra column + * 'zend_db_rownum' + */ + public function fetchAll($style = null, $col = null) + { + $data = parent::fetchAll($style, $col); + $results = array(); + $remove = $this->_adapter->foldCase('ZEND_DB_ROWNUM'); + + foreach ($data as $row) { + if (is_array($row) && array_key_exists($remove, $row)) { + unset($row[$remove]); + } + $results[] = $row; + } + return $results; + } +} diff --git a/vendor/gipfl/zfdb/src/Statement/Exception/StatementException.php b/vendor/gipfl/zfdb/src/Statement/Exception/StatementException.php new file mode 100644 index 0000000..48450fc --- /dev/null +++ b/vendor/gipfl/zfdb/src/Statement/Exception/StatementException.php @@ -0,0 +1,48 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Statement\Exception; + +use gipfl\ZfDb\Exception\DbException; + +/** + * Zend_Db_Statement_Exception + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class StatementException extends DbException +{ + /** + * Check if this general exception has a specific database driver specific exception nested inside. + * + * @return bool + */ + public function hasChainedException() + { + return ($this->getPrevious() !== null); + } + + /** + * @return \Exception|null + */ + public function getChainedException() + { + return $this->getPrevious(); + } +} diff --git a/vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionDb2.php b/vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionDb2.php new file mode 100644 index 0000000..23de2d7 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionDb2.php @@ -0,0 +1,50 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Statement\Exception; + +use gipfl\ZfDb\Statement\Exception\StatementException; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ + +class StatementExceptionDb2 extends StatementException +{ + /** + * @var string + */ + protected $code = '00000'; + + /** + * @var string + */ + protected $message = 'unknown exception'; + + /** + * @param string $msg + * @param string $state + */ + public function __construct($msg = 'unknown exception', $state = '00000') + { + $this->message = $msg; + $this->code = $state; + parent::__construct($this->message, $this->code); + } +} diff --git a/vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionMysqli.php b/vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionMysqli.php new file mode 100644 index 0000000..9adbbc3 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionMysqli.php @@ -0,0 +1,27 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Statement\Exception; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class StatementExceptionMysqli extends StatementException +{ +} diff --git a/vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionOracle.php b/vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionOracle.php new file mode 100644 index 0000000..64e0de7 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionOracle.php @@ -0,0 +1,49 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Statement\Exception; + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ + +class StatementExceptionOracle extends StatementException +{ + protected $message = 'Unknown exception'; + protected $code = 0; + + public function __construct($error = null, $code = 0) + { + if (is_array($error)) { + if (!isset($error['offset'])) { + $this->message = $error['code']." ".$error['message']; + } else { + $this->message = $error['code']." ".$error['message']." "; + $this->message .= substr($error['sqltext'], 0, $error['offset']); + $this->message .= "*"; + $this->message .= substr($error['sqltext'], $error['offset']); + } + $this->code = $error['code']; + } + if (!$this->code && $code) { + $this->code = $code; + } + parent::__construct($this->message, $this->code); + } +} diff --git a/vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionSqlsrv.php b/vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionSqlsrv.php new file mode 100644 index 0000000..a2bcd16 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Statement/Exception/StatementExceptionSqlsrv.php @@ -0,0 +1,55 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Statement\Exception; + +/** + * @see StatementException + */ + +/** + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class StatementExceptionSqlsrv extends StatementException +{ + /** + * Constructor + * + * If $message is an array, the assumption is that the return value of + * sqlsrv_errors() was provided. If so, it then retrieves the most recent + * error from that stack, and sets the message and code based on it. + * + * @param null|array|string $message + * @param null|int $code + */ + public function __construct($message = null, $code = 0) + { + if (is_array($message)) { + // Error should be array of errors + // We only need first one (?) + if (isset($message[0])) { + $message = $message[0]; + } + + $code = (int) $message['code']; + $message = (string) $message['message']; + } + parent::__construct($message, $code); + } +} diff --git a/vendor/gipfl/zfdb/src/Statement/MysqliStatement.php b/vendor/gipfl/zfdb/src/Statement/MysqliStatement.php new file mode 100644 index 0000000..4aa62d2 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Statement/MysqliStatement.php @@ -0,0 +1,345 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Statement; + +use gipfl\ZfDb\Db; +use gipfl\ZfDb\Statement; +use gipfl\ZfDb\Statement\Exception\StatementExceptionMysqli; + +/** + * Extends for Mysqli + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class MysqliStatement extends Statement +{ + /** + * Column names. + * + * @var array + */ + protected $_keys; + + /** + * Fetched result values. + * + * @var array + */ + protected $_values; + + /** + * @var array + */ + protected $_meta = null; + + /** + * @param string $sql + * @return void + * @throws StatementExceptionMysqli + */ + public function _prepare($sql) + { + $mysqli = $this->_adapter->getConnection(); + + $this->_stmt = $mysqli->prepare($sql); + + if ($this->_stmt === false || $mysqli->errno) { + /** + * @see StatementExceptionMysqli + */ + throw new StatementExceptionMysqli("Mysqli prepare error: " . $mysqli->error, $mysqli->errno); + } + } + + /** + * Binds a parameter to the specified variable name. + * + * @param mixed $parameter Name the parameter, either integer or string. + * @param mixed $variable Reference to PHP variable containing the value. + * @param mixed $type OPTIONAL Datatype of SQL parameter. + * @param mixed $length OPTIONAL Length of SQL parameter. + * @param mixed $options OPTIONAL Other options. + * @return bool + * @throws StatementExceptionMysqli + */ + protected function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null) + { + return true; + } + + /** + * Closes the cursor and the statement. + * + * @return bool + */ + public function close() + { + if ($this->_stmt) { + $r = $this->_stmt->close(); + $this->_stmt = null; + return $r; + } + return false; + } + + /** + * Closes the cursor, allowing the statement to be executed again. + * + * @return bool + */ + public function closeCursor() + { + if ($stmt = $this->_stmt) { + $mysqli = $this->_adapter->getConnection(); + while ($mysqli->more_results()) { + $mysqli->next_result(); + } + $this->_stmt->free_result(); + return $this->_stmt->reset(); + } + return false; + } + + /** + * Returns the number of columns in the result set. + * Returns null if the statement has no result set metadata. + * + * @return int The number of columns. + */ + public function columnCount() + { + if (isset($this->_meta) && $this->_meta) { + return $this->_meta->field_count; + } + return 0; + } + + /** + * Retrieves the error code, if any, associated with the last operation on + * the statement handle. + * + * @return string error code. + */ + public function errorCode() + { + if (!$this->_stmt) { + return false; + } + return substr($this->_stmt->sqlstate, 0, 5); + } + + /** + * Retrieves an array of error information, if any, associated with the + * last operation on the statement handle. + * + * @return array|bool + */ + public function errorInfo() + { + if (!$this->_stmt) { + return false; + } + return array( + substr($this->_stmt->sqlstate, 0, 5), + $this->_stmt->errno, + $this->_stmt->error, + ); + } + + /** + * Executes a prepared statement. + * + * @param array $params OPTIONAL Values to bind to parameter placeholders. + * @return bool + * @throws StatementExceptionMysqli + */ + public function _execute(array $params = null) + { + if (!$this->_stmt) { + return false; + } + + // if no params were given as an argument to execute(), + // then default to the _bindParam array + if ($params === null) { + $params = $this->_bindParam; + } + // send $params as input parameters to the statement + if ($params) { + array_unshift($params, str_repeat('s', count($params))); + $stmtParams = array(); + foreach ($params as $k => &$value) { + $stmtParams[$k] = &$value; + } + call_user_func_array( + array($this->_stmt, 'bind_param'), + $stmtParams + ); + } + + // execute the statement + $retval = $this->_stmt->execute(); + if ($retval === false) { + throw new StatementExceptionMysqli( + "Mysqli statement execute error : " . $this->_stmt->error, + $this->_stmt->errno + ); + } + + + // retain metadata + if ($this->_meta === null) { + $this->_meta = $this->_stmt->result_metadata(); + if ($this->_stmt->errno) { + /** + * @see StatementExceptionMysqli + */ + throw new StatementExceptionMysqli( + "Mysqli statement metadata error: " . $this->_stmt->error, + $this->_stmt->errno + ); + } + } + + // statements that have no result set do not return metadata + if ($this->_meta !== false) { + // get the column names that will result + $this->_keys = array(); + foreach ($this->_meta->fetch_fields() as $col) { + $this->_keys[] = $this->_adapter->foldCase($col->name); + } + + // set up a binding space for result variables + $this->_values = array_fill(0, count($this->_keys), null); + + // set up references to the result binding space. + // just passing $this->_values in the call_user_func_array() + // below won't work, you need references. + $refs = array(); + foreach ($this->_values as $i => &$f) { + $refs[$i] = &$f; + } + + $this->_stmt->store_result(); + // bind to the result variables + call_user_func_array( + array($this->_stmt, 'bind_result'), + $this->_values + ); + } + return $retval; + } + + + /** + * Fetches a row from the result set. + * + * @param int $style OPTIONAL Fetch mode for this fetch operation. + * @param int $cursor OPTIONAL Absolute, relative, or other. + * @param int $offset OPTIONAL Number for absolute or relative cursors. + * @return mixed Array, object, or scalar depending on fetch mode. + * @throws StatementExceptionMysqli + */ + public function fetch($style = null, $cursor = null, $offset = null) + { + if (!$this->_stmt) { + return false; + } + // fetch the next result + $retval = $this->_stmt->fetch(); + switch ($retval) { + case null: // end of data + case false: // error occurred + $this->_stmt->reset(); + return false; + default: + // fallthrough + } + + // make sure we have a fetch mode + if ($style === null) { + $style = $this->_fetchMode; + } + + // dereference the result values, otherwise things like fetchAll() + // return the same values for every entry (because of the reference). + $values = array(); + foreach ($this->_values as $key => $val) { + $values[] = $val; + } + + $row = false; + switch ($style) { + case Db::FETCH_NUM: + $row = $values; + break; + case Db::FETCH_ASSOC: + $row = array_combine($this->_keys, $values); + break; + case Db::FETCH_BOTH: + $assoc = array_combine($this->_keys, $values); + $row = array_merge($values, $assoc); + break; + case Db::FETCH_OBJ: + $row = (object) array_combine($this->_keys, $values); + break; + case Db::FETCH_BOUND: + $assoc = array_combine($this->_keys, $values); + $row = array_merge($values, $assoc); + return $this->_fetchBound($row); + default: + throw new StatementExceptionMysqli("Invalid fetch mode '$style' specified"); + } + + return $row; + } + + /** + * Retrieves the next rowset (result set) for a SQL statement that has + * multiple result sets. An example is a stored procedure that returns + * the results of multiple queries. + * + * @return bool + * @throws StatementExceptionMysqli + */ + public function nextRowset() + { + /** + * @see StatementExceptionMysqli + */ + throw new StatementExceptionMysqli(__FUNCTION__.'() is not implemented'); + } + + /** + * Returns the number of rows affected by the execution of the + * last INSERT, DELETE, or UPDATE statement executed by this + * statement object. + * + * @return int The number of rows affected. + */ + public function rowCount() + { + if (!$this->_adapter) { + return false; + } + $mysqli = $this->_adapter->getConnection(); + return $mysqli->affected_rows; + } +} diff --git a/vendor/gipfl/zfdb/src/Statement/OracleStatement.php b/vendor/gipfl/zfdb/src/Statement/OracleStatement.php new file mode 100644 index 0000000..532b227 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Statement/OracleStatement.php @@ -0,0 +1,519 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Statement; + +use gipfl\ZfDb\Db; +use gipfl\ZfDb\Statement; +use gipfl\ZfDb\Statement\Exception\StatementException; +use gipfl\ZfDb\Statement\Exception\StatementExceptionOracle; + +/** + * Extends for Oracle. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class OracleStatement extends Statement +{ + /** + * Column names. + */ + protected $_keys; + + /** + * Fetched result values. + */ + protected $_values; + + /** + * Check if LOB field are returned as string + * instead of OCI-Lob object + * + * @var boolean + */ + protected $_lobAsString = false; + + /** + * Activate/deactivate return of LOB as string + * + * @param string $lob_as_string + * @return OracleStatement + */ + public function setLobAsString($lob_as_string) + { + $this->_lobAsString = (bool) $lob_as_string; + return $this; + } + + /** + * Return whether or not LOB are returned as string + * + * @return boolean + */ + public function getLobAsString() + { + return $this->_lobAsString; + } + + /** + * Prepares statement handle + * + * @param string $sql + * @return void + * @throws StatementExceptionOracle + */ + protected function _prepare($sql) + { + $connection = $this->_adapter->getConnection(); + $this->_stmt = @oci_parse($connection, $sql); + if (!$this->_stmt) { + throw new StatementExceptionOracle(oci_error($connection)); + } + } + + /** + * Binds a parameter to the specified variable name. + * + * @param mixed $parameter Name the parameter, either integer or string. + * @param mixed $variable Reference to PHP variable containing the value. + * @param mixed $type OPTIONAL Datatype of SQL parameter. + * @param mixed $length OPTIONAL Length of SQL parameter. + * @param mixed $options OPTIONAL Other options. + * @return bool + * @throws StatementException + */ + protected function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null) + { + // default value + if ($type === null) { + $type = SQLT_CHR; + } + + // default value + if ($length === null) { + $length = -1; + } + + $retval = @oci_bind_by_name($this->_stmt, $parameter, $variable, $length, $type); + if ($retval === false) { + throw new StatementExceptionOracle(oci_error($this->_stmt)); + } + + return true; + } + + /** + * Closes the cursor, allowing the statement to be executed again. + * + * @return bool + */ + public function closeCursor() + { + if (!$this->_stmt) { + return false; + } + + oci_free_statement($this->_stmt); + $this->_stmt = false; + return true; + } + + /** + * Returns the number of columns in the result set. + * Returns null if the statement has no result set metadata. + * + * @return int The number of columns. + */ + public function columnCount() + { + if (!$this->_stmt) { + return false; + } + + return oci_num_fields($this->_stmt); + } + + + /** + * Retrieves the error code, if any, associated with the last operation on + * the statement handle. + * + * @return string error code. + */ + public function errorCode() + { + if (!$this->_stmt) { + return false; + } + + $error = oci_error($this->_stmt); + + if (!$error) { + return false; + } + + return $error['code']; + } + + + /** + * Retrieves an array of error information, if any, associated with the + * last operation on the statement handle. + * + * @return array|bool + */ + public function errorInfo() + { + if (!$this->_stmt) { + return false; + } + + $error = oci_error($this->_stmt); + if (!$error) { + return false; + } + + if (isset($error['sqltext'])) { + return array( + $error['code'], + $error['message'], + $error['offset'], + $error['sqltext'], + ); + } else { + return array( + $error['code'], + $error['message'], + ); + } + } + + + /** + * Executes a prepared statement. + * + * @param array $params OPTIONAL Values to bind to parameter placeholders. + * @return bool + * @throws StatementException + */ + public function _execute(array $params = null) + { + $connection = $this->_adapter->getConnection(); + + if (!$this->_stmt) { + return false; + } + + if ($params !== null) { + if (!is_array($params)) { + $params = array($params); + } + $error = false; + foreach (array_keys($params) as $name) { + if (!$this->bindParam($name, $params[$name], null, -1)) { + $error = true; + break; + } + } + if ($error) { + throw new StatementExceptionOracle(oci_error($this->_stmt)); + } + } + + $retval = @oci_execute($this->_stmt, $this->_adapter->_getExecuteMode()); + if ($retval === false) { + throw new StatementExceptionOracle(oci_error($this->_stmt)); + } + + $this->_keys = array(); + if ($field_num = oci_num_fields($this->_stmt)) { + for ($i = 1; $i <= $field_num; $i++) { + $name = oci_field_name($this->_stmt, $i); + $this->_keys[] = $name; + } + } + + $this->_values = array(); + if ($this->_keys) { + $this->_values = array_fill(0, count($this->_keys), null); + } + + return $retval; + } + + /** + * Fetches a row from the result set. + * + * @param int $style OPTIONAL Fetch mode for this fetch operation. + * @param int $cursor OPTIONAL Absolute, relative, or other. + * @param int $offset OPTIONAL Number for absolute or relative cursors. + * @return mixed array, object, or scalar depending on fetch mode. + * @throws StatementException + */ + public function fetch($style = null, $cursor = null, $offset = null) + { + if (!$this->_stmt) { + return false; + } + + if ($style === null) { + $style = $this->_fetchMode; + } + + $lob_as_string = $this->getLobAsString() ? OCI_RETURN_LOBS : 0; + + switch ($style) { + case Db::FETCH_NUM: + $row = oci_fetch_array($this->_stmt, OCI_NUM | OCI_RETURN_NULLS | $lob_as_string); + break; + case Db::FETCH_ASSOC: + $row = oci_fetch_array($this->_stmt, OCI_ASSOC | OCI_RETURN_NULLS | $lob_as_string); + break; + case Db::FETCH_BOTH: + $row = oci_fetch_array($this->_stmt, OCI_BOTH | OCI_RETURN_NULLS | $lob_as_string); + break; + case Db::FETCH_OBJ: + $row = oci_fetch_object($this->_stmt); + break; + case Db::FETCH_BOUND: + $row = oci_fetch_array($this->_stmt, OCI_BOTH | OCI_RETURN_NULLS | $lob_as_string); + if ($row !== false) { + return $this->_fetchBound($row); + } + break; + default: + /** + * @see StatementExceptionOracle + */ + throw new StatementExceptionOracle( + array( + 'code' => 'HYC00', + 'message' => "Invalid fetch mode '$style' specified" + ) + ); + break; + } + + if (! $row && $error = oci_error($this->_stmt)) { + throw new StatementExceptionOracle($error); + } + + if (is_array($row) && array_key_exists('zend_db_rownum', $row)) { + unset($row['zend_db_rownum']); + } + + return $row; + } + + /** + * Returns an array containing all of the result set rows. + * + * @param int $style OPTIONAL Fetch mode. + * @param int $col OPTIONAL Column number, if fetch mode is by column. + * @return array|false Collection of rows, each in a format by the fetch mode. + * @throws StatementException + */ + public function fetchAll($style = null, $col = 0) + { + if (!$this->_stmt) { + return false; + } + + // make sure we have a fetch mode + if ($style === null) { + $style = $this->_fetchMode; + } + + $flags = OCI_FETCHSTATEMENT_BY_ROW; + + switch ($style) { + case Db::FETCH_BOTH: + /** + * @see StatementExceptionOracle + */ + throw new StatementExceptionOracle( + array( + 'code' => 'HYC00', + 'message' => "OCI8 driver does not support fetchAll(FETCH_BOTH), use fetch() in a loop instead" + ) + ); + // notreached + $flags |= OCI_NUM; + $flags |= OCI_ASSOC; + break; + case Db::FETCH_NUM: + $flags |= OCI_NUM; + break; + case Db::FETCH_ASSOC: + $flags |= OCI_ASSOC; + break; + case Db::FETCH_OBJ: + break; + case Db::FETCH_COLUMN: + $flags = $flags &~ OCI_FETCHSTATEMENT_BY_ROW; + $flags |= OCI_FETCHSTATEMENT_BY_COLUMN; + $flags |= OCI_NUM; + break; + default: + throw new StatementExceptionOracle( + array( + 'code' => 'HYC00', + 'message' => "Invalid fetch mode '$style' specified" + ) + ); + break; + } + + $result = array(); + if ($flags != OCI_FETCHSTATEMENT_BY_ROW) { /* not Zend_Db::FETCH_OBJ */ + if (! ($rows = oci_fetch_all($this->_stmt, $result, 0, -1, $flags) )) { + if ($error = oci_error($this->_stmt)) { + throw new StatementExceptionOracle($error); + } + if (!$rows) { + return array(); + } + } + if ($style == Db::FETCH_COLUMN) { + $result = $result[$col]; + } + foreach ($result as &$row) { + if (is_array($row) && array_key_exists('zend_db_rownum', $row)) { + unset($row['zend_db_rownum']); + } + } + } else { + while (($row = oci_fetch_object($this->_stmt)) !== false) { + $result [] = $row; + } + if ($error = oci_error($this->_stmt)) { + throw new StatementExceptionOracle($error); + } + } + + return $result; + } + + + /** + * Returns a single column from the next row of a result set. + * + * @param int $col OPTIONAL Position of the column to fetch. + * @return string + * @throws StatementException + */ + public function fetchColumn($col = 0) + { + if (!$this->_stmt) { + return false; + } + + if (!oci_fetch($this->_stmt)) { + // if no error, there is simply no record + if (!$error = oci_error($this->_stmt)) { + return false; + } + throw new StatementExceptionOracle($error); + } + + $data = oci_result($this->_stmt, $col+1); //1-based + if ($data === false) { + throw new StatementExceptionOracle(oci_error($this->_stmt)); + } + + if ($this->getLobAsString()) { + // instanceof doesn't allow '-', we must use a temporary string + $type = 'OCI-Lob'; + if ($data instanceof $type) { + $data = $data->read($data->size()); + } + } + + return $data; + } + + /** + * Fetches the next row and returns it as an object. + * + * @param string $class OPTIONAL Name of the class to create. + * @param array $config OPTIONAL Constructor arguments for the class. + * @return mixed One object instance of the specified class. + * @throws StatementException + */ + public function fetchObject($class = 'stdClass', array $config = array()) + { + if (!$this->_stmt) { + return false; + } + + $obj = oci_fetch_object($this->_stmt); + + if ($error = oci_error($this->_stmt)) { + throw new StatementExceptionOracle($error); + } + + /* @todo XXX handle parameters */ + + return $obj; + } + + /** + * Retrieves the next rowset (result set) for a SQL statement that has + * multiple result sets. An example is a stored procedure that returns + * the results of multiple queries. + * + * @return bool + * @throws StatementException + */ + public function nextRowset() + { + /** + * @see StatementExceptionOracle + */ + throw new StatementExceptionOracle( + array( + 'code' => 'HYC00', + 'message' => 'Optional feature not implemented' + ) + ); + } + + /** + * Returns the number of rows affected by the execution of the + * last INSERT, DELETE, or UPDATE statement executed by this + * statement object. + * + * @return int The number of rows affected. + * @throws StatementException + */ + public function rowCount() + { + if (!$this->_stmt) { + return false; + } + + $num_rows = oci_num_rows($this->_stmt); + + if ($num_rows === false) { + throw new StatementExceptionOracle(oci_error($this->_stmt)); + } + + return $num_rows; + } +} diff --git a/vendor/gipfl/zfdb/src/Statement/Pdo/IbmPdoStatement.php b/vendor/gipfl/zfdb/src/Statement/Pdo/IbmPdoStatement.php new file mode 100644 index 0000000..6e6d43a --- /dev/null +++ b/vendor/gipfl/zfdb/src/Statement/Pdo/IbmPdoStatement.php @@ -0,0 +1,86 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Statement\Pdo; + +use gipfl\ZfDb\Statement\PdoStatement; +use gipfl\ZfDb\Statement\Exception\StatementException; +use PDOException; + +/** + * Proxy class to wrap a PDOStatement object for IBM Databases. + * Matches the interface of PDOStatement. All methods simply proxy to the + * matching method in PDOStatement. PDOExceptions thrown by PDOStatement + * are re-thrown as Zend_Db_Statement_Exception. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class IbmPdoStatement extends PdoStatement +{ + /** + * Returns an array containing all of the result set rows. + * + * Behaves like parent, but if limit() + * is used, the final result removes the extra column + * 'zend_db_rownum' + * + * @param int $style OPTIONAL Fetch mode. + * @param int $col OPTIONAL Column number, if fetch mode is by column. + * @return array Collection of rows, each in a format by the fetch mode. + * @throws StatementException + */ + public function fetchAll($style = null, $col = null) + { + $data = parent::fetchAll($style, $col); + $results = array(); + $remove = $this->_adapter->foldCase('ZEND_DB_ROWNUM'); + + foreach ($data as $row) { + if (is_array($row) && array_key_exists($remove, $row)) { + unset($row[$remove]); + } + $results[] = $row; + } + return $results; + } + + /** + * Binds a parameter to the specified variable name. + * + * @param mixed $parameter Name the parameter, either integer or string. + * @param mixed $variable Reference to PHP variable containing the value. + * @param mixed $type OPTIONAL Datatype of SQL parameter. + * @param mixed $length OPTIONAL Length of SQL parameter. + * @param mixed $options OPTIONAL Other options. + * @return bool + * @throws StatementException + */ + public function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null) + { + try { + if (($type === null) && ($length === null) && ($options === null)) { + return $this->_stmt->bindParam($parameter, $variable); + } else { + return $this->_stmt->bindParam($parameter, $variable, $type, $length, $options); + } + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } +} diff --git a/vendor/gipfl/zfdb/src/Statement/Pdo/OciPdoStatement.php b/vendor/gipfl/zfdb/src/Statement/Pdo/OciPdoStatement.php new file mode 100644 index 0000000..ed9b98c --- /dev/null +++ b/vendor/gipfl/zfdb/src/Statement/Pdo/OciPdoStatement.php @@ -0,0 +1,83 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Statement\Pdo; + +use gipfl\ZfDb\Statement\PdoStatement; +use gipfl\ZfDb\Statement\Exception\StatementException; + +/** + * Proxy class to wrap a PDOStatement object for IBM Databases. + * Matches the interface of PDOStatement. All methods simply proxy to the + * matching method in PDOStatement. PDOExceptions thrown by PDOStatement + * are re-thrown as Zend_Db_Statement_Exception. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class OciPdoStatement extends PdoStatement +{ + /** + * Returns an array containing all of the result set rows. + * + * Behaves like parent, but if limit() + * is used, the final result removes the extra column + * 'zend_db_rownum' + * + * @param int $style OPTIONAL Fetch mode. + * @param int $col OPTIONAL Column number, if fetch mode is by column. + * @return array Collection of rows, each in a format by the fetch mode. + * @throws StatementException + */ + public function fetchAll($style = null, $col = null) + { + $data = parent::fetchAll($style, $col); + $results = array(); + $remove = $this->_adapter->foldCase('zend_db_rownum'); + + foreach ($data as $row) { + if (is_array($row) && array_key_exists($remove, $row)) { + unset($row[$remove]); + } + $results[] = $row; + } + + return $results; + } + + /** + * Fetches a row from the result set. + * + * @param int $style OPTIONAL Fetch mode for this fetch operation. + * @param int $cursor OPTIONAL Absolute, relative, or other. + * @param int $offset OPTIONAL Number for absolute or relative cursors. + * @return mixed Array, object, or scalar depending on fetch mode. + * @throws StatementException + */ + public function fetch($style = null, $cursor = null, $offset = null) + { + $row = parent::fetch($style, $cursor, $offset); + + $remove = $this->_adapter->foldCase('zend_db_rownum'); + if (is_array($row) && array_key_exists($remove, $row)) { + unset($row[$remove]); + } + + return $row; + } +} diff --git a/vendor/gipfl/zfdb/src/Statement/PdoStatement.php b/vendor/gipfl/zfdb/src/Statement/PdoStatement.php new file mode 100644 index 0000000..ac49eb6 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Statement/PdoStatement.php @@ -0,0 +1,417 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Statement; + +use gipfl\ZfDb\Statement; +use gipfl\ZfDb\Statement\Exception\StatementException; +use IteratorAggregate; +use IteratorIterator; +use PDO; +use PDOException; + +/** + * Proxy class to wrap a PDOStatement object. + * Matches the interface of PDOStatement. All methods simply proxy to the + * matching method in PDOStatement. PDOExceptions thrown by PDOStatement + * are re-thrown as Zend_Db_Statement_Exception. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class PdoStatement extends Statement implements IteratorAggregate +{ + /** + * @var int + */ + protected $_fetchMode = PDO::FETCH_ASSOC; + + /** + * Prepare a string SQL statement and create a statement object. + * + * @param string $sql + * @return void + * @throws StatementException + */ + protected function _prepare($sql) + { + try { + $this->_stmt = $this->_adapter->getConnection()->prepare($sql); + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Bind a column of the statement result set to a PHP variable. + * + * @param string $column Name the column in the result set, either by + * position or by name. + * @param mixed $param Reference to the PHP variable containing the value. + * @param mixed $type OPTIONAL + * @return bool + * @throws StatementException + */ + public function bindColumn($column, &$param, $type = null) + { + try { + if ($type === null) { + return $this->_stmt->bindColumn($column, $param); + } else { + return $this->_stmt->bindColumn($column, $param, $type); + } + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Binds a parameter to the specified variable name. + * + * @param mixed $parameter Name the parameter, either integer or string. + * @param mixed $variable Reference to PHP variable containing the value. + * @param mixed $type OPTIONAL Datatype of SQL parameter. + * @param mixed $length OPTIONAL Length of SQL parameter. + * @param mixed $options OPTIONAL Other options. + * @return bool + * @throws StatementException + */ + protected function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null) + { + try { + if ($type === null) { + if (is_bool($variable)) { + $type = PDO::PARAM_BOOL; + } elseif ($variable === null) { + $type = PDO::PARAM_NULL; + } elseif (is_integer($variable)) { + $type = PDO::PARAM_INT; + } else { + $type = PDO::PARAM_STR; + } + } + return $this->_stmt->bindParam($parameter, $variable, $type, $length, $options); + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Binds a value to a parameter. + * + * @param mixed $parameter Name the parameter, either integer or string. + * @param mixed $value Scalar value to bind to the parameter. + * @param mixed $type OPTIONAL Datatype of the parameter. + * @return bool + * @throws StatementException + */ + public function bindValue($parameter, $value, $type = null) + { + if (is_string($parameter) && $parameter[0] != ':') { + $parameter = ":$parameter"; + } + + $this->_bindParam[$parameter] = $value; + + try { + if ($type === null) { + return $this->_stmt->bindValue($parameter, $value); + } else { + return $this->_stmt->bindValue($parameter, $value, $type); + } + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Closes the cursor, allowing the statement to be executed again. + * + * @return bool + * @throws StatementException + */ + public function closeCursor() + { + try { + return $this->_stmt->closeCursor(); + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Returns the number of columns in the result set. + * Returns null if the statement has no result set metadata. + * + * @return int The number of columns. + * @throws StatementException + */ + public function columnCount() + { + try { + return $this->_stmt->columnCount(); + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Retrieves the error code, if any, associated with the last operation on + * the statement handle. + * + * @return string error code. + * @throws StatementException + */ + public function errorCode() + { + try { + return $this->_stmt->errorCode(); + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Retrieves an array of error information, if any, associated with the + * last operation on the statement handle. + * + * @return array + * @throws StatementException + */ + public function errorInfo() + { + try { + return $this->_stmt->errorInfo(); + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Executes a prepared statement. + * + * @param array $params OPTIONAL Values to bind to parameter placeholders. + * @return bool + * @throws StatementException + */ + public function _execute(array $params = null) + { + try { + if ($params !== null) { + return $this->_stmt->execute($params); + } else { + return $this->_stmt->execute(); + } + } catch (PDOException $e) { + $message = sprintf('%s, query was: %s', $e->getMessage(), $this->_stmt->queryString); + throw new StatementException($message, (int) $e->getCode(), $e); + } + } + + /** + * Fetches a row from the result set. + * + * @param int $style OPTIONAL Fetch mode for this fetch operation. + * @param int $cursor OPTIONAL Absolute, relative, or other. + * @param int $offset OPTIONAL Number for absolute or relative cursors. + * @return mixed Array, object, or scalar depending on fetch mode. + * @throws StatementException + */ + public function fetch($style = null, $cursor = PDO::FETCH_ORI_NEXT, $offset = 0) + { + if ($style === null) { + $style = $this->_fetchMode; + } + try { + return $this->_stmt->fetch($style, $cursor, $offset); + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Required by IteratorAggregate interface + * + * @return IteratorIterator + */ + #[\ReturnTypeWillChange] + public function getIterator() + { + return new IteratorIterator($this->_stmt); + } + + /** + * Returns an array containing all of the result set rows. + * + * @param int $style OPTIONAL Fetch mode. + * @param int $col OPTIONAL Column number, if fetch mode is by column. + * @return array Collection of rows, each in a format by the fetch mode. + * @throws StatementException + */ + public function fetchAll($style = null, $col = null) + { + if ($style === null) { + $style = $this->_fetchMode; + } + try { + if ($style == PDO::FETCH_COLUMN) { + if ($col === null) { + $col = 0; + } + return $this->_stmt->fetchAll($style, $col); + } else { + return $this->_stmt->fetchAll($style); + } + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Returns a single column from the next row of a result set. + * + * @param int $col OPTIONAL Position of the column to fetch. + * @return string + * @throws StatementException + */ + public function fetchColumn($col = 0) + { + try { + return $this->_stmt->fetchColumn($col); + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Fetches the next row and returns it as an object. + * + * @param string $class OPTIONAL Name of the class to create. + * @param array $config OPTIONAL Constructor arguments for the class. + * @return mixed One object instance of the specified class. + * @throws StatementException + */ + public function fetchObject($class = 'stdClass', array $config = array()) + { + try { + return $this->_stmt->fetchObject($class, $config); + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Retrieve a statement attribute. + * + * @param integer $key Attribute name. + * @return mixed Attribute value. + * @throws StatementException + */ + public function getAttribute($key) + { + try { + return $this->_stmt->getAttribute($key); + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Returns metadata for a column in a result set. + * + * @param int $column + * @return mixed + * @throws StatementException + */ + public function getColumnMeta($column) + { + try { + return $this->_stmt->getColumnMeta($column); + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Retrieves the next rowset (result set) for a SQL statement that has + * multiple result sets. An example is a stored procedure that returns + * the results of multiple queries. + * + * @return bool + * @throws StatementException + */ + public function nextRowset() + { + try { + return $this->_stmt->nextRowset(); + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Returns the number of rows affected by the execution of the + * last INSERT, DELETE, or UPDATE statement executed by this + * statement object. + * + * @return int The number of rows affected. + * @throws StatementException + */ + public function rowCount() + { + try { + return $this->_stmt->rowCount(); + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Set a statement attribute. + * + * @param string $key Attribute name. + * @param mixed $val Attribute value. + * @return bool + * @throws StatementException + */ + public function setAttribute($key, $val) + { + try { + return $this->_stmt->setAttribute($key, $val); + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Set the default fetch mode for this statement. + * + * @param int $mode The fetch mode. + * @return bool + * @throws StatementException + */ + public function setFetchMode($mode) + { + $this->_fetchMode = $mode; + try { + return $this->_stmt->setFetchMode($mode); + } catch (PDOException $e) { + throw new StatementException($e->getMessage(), $e->getCode(), $e); + } + } +} diff --git a/vendor/gipfl/zfdb/src/Statement/SqlsrvStatement.php b/vendor/gipfl/zfdb/src/Statement/SqlsrvStatement.php new file mode 100644 index 0000000..d5fe23a --- /dev/null +++ b/vendor/gipfl/zfdb/src/Statement/SqlsrvStatement.php @@ -0,0 +1,423 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Statement; + +use gipfl\ZfDb\Db; +use gipfl\ZfDb\Statement\Exception\StatementException; +use gipfl\ZfDb\Statement\Exception\StatementExceptionSqlsrv; +use gipfl\ZfDb\Statement; + +/** + * Extends for Microsoft SQL Server Driver for PHP + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class SqlsrvStatement extends Statement +{ + /** + * The connection_stmt object original string. + */ + protected $_originalSQL; + + /** + * Column names. + */ + protected $_keys; + + /** + * Query executed + */ + protected $_executed = false; + + /** + * Prepares statement handle + * + * @param string $sql + * @return void + * @throws StatementExceptionSqlsrv + */ + protected function _prepare($sql) + { + $connection = $this->_adapter->getConnection(); + + $this->_stmt = sqlsrv_prepare($connection, $sql); + + if (!$this->_stmt) { + throw new StatementExceptionSqlsrv(sqlsrv_errors()); + } + + $this->_originalSQL = $sql; + } + + /** + * Binds a parameter to the specified variable name. + * + * @param mixed $parameter Name the parameter, either integer or string. + * @param mixed $variable Reference to PHP variable containing the value. + * @param mixed $type OPTIONAL Datatype of SQL parameter. + * @param mixed $length OPTIONAL Length of SQL parameter. + * @param mixed $options OPTIONAL Other options. + * @return bool + * @throws StatementException + */ + protected function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null) + { + //Sql server doesn't support bind by name + return true; + } + + /** + * Closes the cursor, allowing the statement to be executed again. + * + * @return bool + */ + public function closeCursor() + { + if (!$this->_stmt) { + return false; + } + + sqlsrv_free_stmt($this->_stmt); + $this->_stmt = false; + return true; + } + + /** + * Returns the number of columns in the result set. + * Returns null if the statement has no result set metadata. + * + * @return int The number of columns. + */ + public function columnCount() + { + if ($this->_stmt && $this->_executed) { + return sqlsrv_num_fields($this->_stmt); + } + + return 0; + } + + + /** + * Retrieves the error code, if any, associated with the last operation on + * the statement handle. + * + * @return string error code. + */ + public function errorCode() + { + if (!$this->_stmt) { + return false; + } + + $error = sqlsrv_errors(); + if (!$error) { + return false; + } + + return $error[0]['code']; + } + + /** + * Retrieves an array of error information, if any, associated with the + * last operation on the statement handle. + * + * @return array|bool + */ + public function errorInfo() + { + if (!$this->_stmt) { + return false; + } + + $error = sqlsrv_errors(); + if (!$error) { + return false; + } + + return array( + $error[0]['code'], + $error[0]['message'], + ); + } + + + /** + * Executes a prepared statement. + * + * @param array $params OPTIONAL Values to bind to parameter placeholders. + * @return bool + * @throws StatementException + */ + public function _execute(array $params = null) + { + $connection = $this->_adapter->getConnection(); + if (!$this->_stmt) { + return false; + } + + if ($params !== null) { + if (!is_array($params)) { + $params = array($params); + } + $error = false; + + // make all params passed by reference + $params_ = array(); + $temp = array(); + $i = 1; + foreach ($params as $param) { + $temp[$i] = $param; + $params_[] = &$temp[$i]; + $i++; + } + $params = $params_; + } + + $this->_stmt = sqlsrv_query($connection, $this->_originalSQL, $params); + + if (!$this->_stmt) { + throw new StatementExceptionSqlsrv(sqlsrv_errors()); + } + + $this->_executed = true; + + return (!$this->_stmt); + } + + /** + * Fetches a row from the result set. + * + * @param int $style OPTIONAL Fetch mode for this fetch operation. + * @param int $cursor OPTIONAL Absolute, relative, or other. + * @param int $offset OPTIONAL Number for absolute or relative cursors. + * @return mixed Array, object, or scalar depending on fetch mode. + * @throws StatementException + */ + public function fetch($style = null, $cursor = null, $offset = null) + { + if (!$this->_stmt) { + return false; + } + + if (null === $style) { + $style = $this->_fetchMode; + } + + $values = sqlsrv_fetch_array($this->_stmt, SQLSRV_FETCH_ASSOC); + + if (!$values && (null !== $error = sqlsrv_errors())) { + throw new StatementExceptionSqlsrv($error); + } + + if (null === $values) { + return null; + } + + if (!$this->_keys) { + foreach ($values as $key => $value) { + $this->_keys[] = $this->_adapter->foldCase($key); + } + } + + $values = array_values($values); + + $row = false; + switch ($style) { + case Db::FETCH_NUM: + $row = $values; + break; + case Db::FETCH_ASSOC: + $row = array_combine($this->_keys, $values); + break; + case Db::FETCH_BOTH: + $assoc = array_combine($this->_keys, $values); + $row = array_merge($values, $assoc); + break; + case Db::FETCH_OBJ: + $row = (object) array_combine($this->_keys, $values); + break; + case Db::FETCH_BOUND: + $assoc = array_combine($this->_keys, $values); + $row = array_merge($values, $assoc); + $row = $this->_fetchBound($row); + break; + default: + throw new StatementExceptionSqlsrv("Invalid fetch mode '$style' specified"); + } + + return $row; + } + + /** + * Returns a single column from the next row of a result set. + * + * @param int $col OPTIONAL Position of the column to fetch. + * @return string + * @throws StatementException + */ + public function fetchColumn($col = 0) + { + if (!$this->_stmt) { + return false; + } + + if (!sqlsrv_fetch($this->_stmt)) { + if (null !== $error = sqlsrv_errors()) { + throw new StatementExceptionSqlsrv($error); + } + + // If no error, there is simply no record + return false; + } + + $data = sqlsrv_get_field($this->_stmt, $col); //0-based + if ($data === false) { + throw new StatementExceptionSqlsrv(sqlsrv_errors()); + } + + return $data; + } + + /** + * Fetches the next row and returns it as an object. + * + * @param string $class OPTIONAL Name of the class to create. + * @param array $config OPTIONAL Constructor arguments for the class. + * @return mixed One object instance of the specified class. + * @throws StatementException + */ + public function fetchObject($class = 'stdClass', array $config = array()) + { + if (!$this->_stmt) { + return false; + } + + $obj = sqlsrv_fetch_object($this->_stmt); + + if ($error = sqlsrv_errors()) { + throw new StatementExceptionSqlsrv($error); + } + + /* @todo XXX handle parameters */ + + if (null === $obj) { + return false; + } + + return $obj; + } + + /** + * Returns metadata for a column in a result set. + * + * @param int $column + * @return mixed + * @throws StatementExceptionSqlsrv + */ + public function getColumnMeta($column) + { + $fields = sqlsrv_field_metadata($this->_stmt); + + if (!$fields) { + throw new StatementExceptionSqlsrv('Column metadata can not be fetched'); + } + + if (!isset($fields[$column])) { + throw new StatementExceptionSqlsrv('Column index does not exist in statement'); + } + + return $fields[$column]; + } + + /** + * Retrieves the next rowset (result set) for a SQL statement that has + * multiple result sets. An example is a stored procedure that returns + * the results of multiple queries. + * + * @return bool + * @throws StatementException + */ + public function nextRowset() + { + if (sqlsrv_next_result($this->_stmt) === false) { + throw new StatementExceptionSqlsrv(sqlsrv_errors()); + } + + // reset column keys + $this->_keys = null; + + return true; + } + + /** + * Returns the number of rows affected by the execution of the + * last INSERT, DELETE, or UPDATE statement executed by this + * statement object. + * + * @return int The number of rows affected. + * @throws StatementException + */ + public function rowCount() + { + if (!$this->_stmt) { + return false; + } + + if (!$this->_executed) { + return 0; + } + + $num_rows = sqlsrv_rows_affected($this->_stmt); + + // Strict check is necessary; 0 is a valid return value + if ($num_rows === false) { + throw new StatementExceptionSqlsrv(sqlsrv_errors()); + } + + return $num_rows; + } + + /** + * Returns an array containing all of the result set rows. + * + * @param int $style OPTIONAL Fetch mode. + * @param int $col OPTIONAL Column number, if fetch mode is by column. + * @return array Collection of rows, each in a format by the fetch mode. + * + * Behaves like parent, but if limit() + * is used, the final result removes the extra column + * 'zend_db_rownum' + */ + public function fetchAll($style = null, $col = null) + { + $data = parent::fetchAll($style, $col); + $results = array(); + $remove = $this->_adapter->foldCase('ZEND_DB_ROWNUM'); + + foreach ($data as $row) { + if (is_array($row) && array_key_exists($remove, $row)) { + unset($row[$remove]); + } + $results[] = $row; + } + return $results; + } +} diff --git a/vendor/gipfl/zfdb/src/Statement/StatementInterface.php b/vendor/gipfl/zfdb/src/Statement/StatementInterface.php new file mode 100644 index 0000000..3b2a0b4 --- /dev/null +++ b/vendor/gipfl/zfdb/src/Statement/StatementInterface.php @@ -0,0 +1,198 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +namespace gipfl\ZfDb\Statement; + +use gipfl\ZfDb\Statement\Exception\StatementException; + +/** + * Emulates a PDOStatement for native database adapters. + * + * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +interface StatementInterface +{ + /** + * Bind a column of the statement result set to a PHP variable. + * + * @param string $column Name the column in the result set, either by + * position or by name. + * @param mixed $param Reference to the PHP variable containing the value. + * @param mixed $type OPTIONAL + * @return bool + * @throws StatementException + */ + public function bindColumn($column, &$param, $type = null); + + /** + * Binds a parameter to the specified variable name. + * + * @param mixed $parameter Name the parameter, either integer or string. + * @param mixed $variable Reference to PHP variable containing the value. + * @param mixed $type OPTIONAL Datatype of SQL parameter. + * @param mixed $length OPTIONAL Length of SQL parameter. + * @param mixed $options OPTIONAL Other options. + * @return bool + * @throws StatementException + */ + public function bindParam($parameter, &$variable, $type = null, $length = null, $options = null); + + /** + * Binds a value to a parameter. + * + * @param mixed $parameter Name the parameter, either integer or string. + * @param mixed $value Scalar value to bind to the parameter. + * @param mixed $type OPTIONAL Datatype of the parameter. + * @return bool + * @throws StatementException + */ + public function bindValue($parameter, $value, $type = null); + + /** + * Closes the cursor, allowing the statement to be executed again. + * + * @return bool + * @throws StatementException + */ + public function closeCursor(); + + /** + * Returns the number of columns in the result set. + * Returns null if the statement has no result set metadata. + * + * @return int The number of columns. + * @throws StatementException + */ + public function columnCount(); + + /** + * Retrieves the error code, if any, associated with the last operation on + * the statement handle. + * + * @return string error code. + * @throws StatementException + */ + public function errorCode(); + + /** + * Retrieves an array of error information, if any, associated with the + * last operation on the statement handle. + * + * @return array + * @throws StatementException + */ + public function errorInfo(); + + /** + * Executes a prepared statement. + * + * @param array $params OPTIONAL Values to bind to parameter placeholders. + * @return bool + * @throws StatementException + */ + public function execute(array $params = array()); + + /** + * Fetches a row from the result set. + * + * @param int $style OPTIONAL Fetch mode for this fetch operation. + * @param int $cursor OPTIONAL Absolute, relative, or other. + * @param int $offset OPTIONAL Number for absolute or relative cursors. + * @return mixed Array, object, or scalar depending on fetch mode. + * @throws StatementException + */ + public function fetch($style = null, $cursor = null, $offset = null); + + /** + * Returns an array containing all of the result set rows. + * + * @param int $style OPTIONAL Fetch mode. + * @param int $col OPTIONAL Column number, if fetch mode is by column. + * @return array Collection of rows, each in a format by the fetch mode. + * @throws StatementException + */ + public function fetchAll($style = null, $col = null); + + /** + * Returns a single column from the next row of a result set. + * + * @param int $col OPTIONAL Position of the column to fetch. + * @return string + * @throws StatementException + */ + public function fetchColumn($col = 0); + + /** + * Fetches the next row and returns it as an object. + * + * @param string $class OPTIONAL Name of the class to create. + * @param array $config OPTIONAL Constructor arguments for the class. + * @return mixed One object instance of the specified class. + * @throws StatementException + */ + public function fetchObject($class = 'stdClass', array $config = array()); + + /** + * Retrieve a statement attribute. + * + * @param string $key Attribute name. + * @return mixed Attribute value. + * @throws StatementException + */ + public function getAttribute($key); + + /** + * Retrieves the next rowset (result set) for a SQL statement that has + * multiple result sets. An example is a stored procedure that returns + * the results of multiple queries. + * + * @return bool + * @throws StatementException + */ + public function nextRowset(); + + /** + * Returns the number of rows affected by the execution of the + * last INSERT, DELETE, or UPDATE statement executed by this + * statement object. + * + * @return int The number of rows affected. + * @throws StatementException + */ + public function rowCount(); + + /** + * Set a statement attribute. + * + * @param string $key Attribute name. + * @param mixed $val Attribute value. + * @return bool + * @throws StatementException + */ + public function setAttribute($key, $val); + + /** + * Set the default fetch mode for this statement. + * + * @param int $mode The fetch mode. + * @return bool + * @throws StatementException + */ + public function setFetchMode($mode); +} |