* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
abstract class Horde_Db_Adapter_Base_Schema
{
/**
* A Horde_Db_Adapter instance.
*
* @var Horde_Db_Adapter_Base
*/
protected $_adapter = null;
/**
* List of public methods supported by the attached adapter.
*
* Method names are in the keys.
*
* @var array
*/
protected $_adapterMethods = array();
/*##########################################################################
# Construct/Destruct
##########################################################################*/
/**
* Constructor.
*
* @param Horde_Db_Adapter_Base $adapter A Horde_Db_Adapter instance.
*/
public function __construct(Horde_Db_Adapter $adapter)
{
$this->setAdapter($adapter);
}
/**
* Setter for a Horde_Db_Adapter instance.
*
* @param Horde_Db_Adapter $adapter A Horde_Db_Adapter instance.
*/
public function setAdapter(Horde_Db_Adapter $adapter)
{
$this->_adapter = $adapter;
$this->_adapterMethods = array_flip(get_class_methods($adapter));
}
/*##########################################################################
# Object factories
##########################################################################*/
/**
* Factory for Column objects.
*
* @param string $name The column's name, such as "supplier_id" in
* "supplier_id int(11)".
* @param string $default The type-casted default value, such as "new" in
* "sales_stage varchar(20) default 'new'".
* @param string $sqlType Used to extract the column's type, length and
* signed status, if necessary. For example
* "varchar" and "60" in "company_name varchar(60)"
* or "unsigned => true" in "int(10) UNSIGNED".
* @param boolean $null Whether this column allows NULL values.
*
* @return Horde_Db_Adapter_Base_Column A column object.
*/
public function makeColumn($name, $default, $sqlType = null, $null = true)
{
return new Horde_Db_Adapter_Base_Column($name, $default, $sqlType, $null);
}
/**
* Factory for ColumnDefinition objects.
*
* @return Horde_Db_Adapter_Base_ColumnDefinition A column definition
* object.
*/
public function makeColumnDefinition(
$base, $name, $type, $limit = null, $precision = null, $scale = null,
$unsigned = null, $default = null, $null = null, $autoincrement = null)
{
return new Horde_Db_Adapter_Base_ColumnDefinition(
$base, $name, $type, $limit, $precision, $scale, $unsigned,
$default, $null, $autoincrement);
}
/**
* Factory for Index objects.
*
* @param string $table The table the index is on.
* @param string $name The index's name.
* @param boolean $primary Is this a primary key?
* @param boolean $unique Is this a unique index?
* @param array $columns The columns this index covers.
*
* @return Horde_Db_Adapter_Base_Index An index object.
*/
public function makeIndex($table, $name, $primary, $unique, $columns)
{
return new Horde_Db_Adapter_Base_Index($table, $name, $primary, $unique, $columns);
}
/**
* Factory for Table objects.
*
* @return Horde_Db_Adapter_Base_Table A table object.
*/
public function makeTable($name, $primaryKey, $columns, $indexes)
{
return new Horde_Db_Adapter_Base_Table($name, $primaryKey, $columns, $indexes);
}
/**
* Factory for TableDefinition objects.
*
* @return Horde_Db_Adapter_Base_TableDefinition A table definition object.
*/
public function makeTableDefinition($name, $base, $options = array())
{
return new Horde_Db_Adapter_Base_TableDefinition($name, $base, $options);
}
/*##########################################################################
# Object composition
##########################################################################*/
/**
* Delegates calls to the adapter object.
*
* @param string $method A method name.
* @param array $args Method parameters.
*
* @return mixed The method call result.
* @throws BadMethodCallException if method doesn't exist in the adapter.
*/
public function __call($method, $args)
{
if (isset($this->_adapterMethods[$method])) {
return call_user_func_array(array($this->_adapter, $method), $args);
}
throw new BadMethodCallException('Call to undeclared method "' . $method . '"');
}
/**
* Delegates access to $_cache and $_logger to the adapter object.
*
* @param string $key Property name. Only '_cache' and '_logger' are
* supported.
*
* @return object The request property object.
*/
public function __get($key)
{
if ($key == '_cache' || $key == '_logger') {
$getter = 'get' . Horde_String::ucfirst(substr($key, 1));
return $this->_adapter->$getter();
}
}
/*##########################################################################
# Quoting
##########################################################################*/
/**
* Quotes the column value to help prevent SQL injection attacks.
*
* This method makes educated guesses on the scalar type based on the
* passed value. Make sure to correctly cast the value and/or pass the
* $column parameter to get the best results.
*
* @param mixed $value The scalar value to quote, a Horde_Db_Value,
* Horde_Date, or DateTime instance, or an object
* implementing quotedId().
* @param object $column An object implementing getType().
*
* @return string The correctly quoted value.
*/
public function quote($value, $column = null)
{
if (is_object($value) && is_callable(array($value, 'quotedId'))) {
return $value->quotedId();
}
if ($value instanceof Horde_Db_Value) {
return $value->quote($this->_adapter);
}
$type = isset($column) ? $column->getType() : null;
if (is_null($value)) {
return 'NULL';
} elseif ($value === true) {
return $type == 'integer' ? '1' : $this->quoteTrue();
} elseif ($value === false) {
return $type == 'integer' ? '0' : $this->quoteFalse();
} elseif (is_float($value)) {
return sprintf('%F', $value);
} elseif (is_int($value)) {
return $value;
} elseif ($value instanceof DateTime || $value instanceof Horde_Date) {
return $this->_adapter->quoteString($type == 'integer'
? $value->format('U')
: $value->format('Y-m-d H:i:s'));
} elseif ($type == 'integer') {
return (int)$value;
} elseif ($type == 'float') {
return sprintf('%F', $value);
} else {
return $this->_adapter->quoteString($value);
}
}
/**
* Quotes a string, escaping any ' (single quote) and \ (backslash)
* characters.
*
* @param string $string A string to escape.
*
* @return string The escaped and quoted string.
*/
public function quoteString($string)
{
return "'" . str_replace(array('\\', '\''), array('\\\\', '\\\''), $string) . "'";
}
/**
* Returns a quoted form of the column name.
*
* @param string $name A column name.
*
* @return string The quoted column name.
*/
public function quoteColumnName($name)
{
return '"' . str_replace('"', '""', $name) . '"';
}
/**
* Returns a quoted form of the table name.
*
* Defaults to column name quoting.
*
* @param string $name A table name.
*
* @return string The quoted table name.
*/
public function quoteTableName($name)
{
return $this->quoteColumnName($name);
}
/**
* Returns a quoted boolean true.
*
* @return string The quoted boolean true.
*/
public function quoteTrue()
{
return '1';
}
/**
* Returns a quoted boolean false.
*
* @return string The quoted boolean false.
*/
public function quoteFalse()
{
return '0';
}
/**
* Returns a quoted date value.
*
* @param mixed A date value that can be casted to string.
*
* @return string The quoted date value.
*/
public function quoteDate($value)
{
return $this->quoteString((string)$value);
}
/**
* Returns a quoted binary value.
*
* @param mixed A binary value.
*
* @return string The quoted binary value.
*/
public function quoteBinary($value)
{
return $this->quoteString($value);
}
/*##########################################################################
# Schema Statements
##########################################################################*/
/**
* Returns a hash of mappings from the abstract data types to the native
* database types.
*
* See TableDefinition::column() for details on the recognized abstract
* data types.
*
* @see TableDefinition::column()
*
* @return array A database type map.
*/
abstract public function nativeDatabaseTypes();
/**
* Returns the maximum length a table alias can have.
*
* @return integer The maximum table alias length.
*/
public function tableAliasLength()
{
return 255;
}
/**
* Converts a table name into a suitable table alias.
*
* @param string $tableName A table name.
*
* @return string A possible alias name for the table.
*/
public function tableAliasFor($tableName)
{
$alias = substr($tableName, 0, $this->tableAliasLength());
return str_replace('.', '_', $alias);
}
/**
* Returns a list of all tables of the current database.
*
* @return array A table list.
*/
abstract public function tables();
/**
* Returns a Horde_Db_Adapter_Base_Table object for a table.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return Horde_Db_Adapter_Base_Table A table object.
*/
public function table($tableName, $name = null)
{
return $this->makeTable(
$tableName,
$this->primaryKey($tableName),
$this->columns($tableName, $name),
$this->indexes($tableName, $name)
);
}
/**
* Returns a table's primary key.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return Horde_Db_Adapter_Base_Index The primary key index object.
*/
abstract public function primaryKey($tableName, $name = null);
/**
* Returns a list of tables indexes.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return array A list of Horde_Db_Adapter_Base_Index objects.
*/
abstract public function indexes($tableName, $name = null);
/**
* Returns a list of table columns.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return array A list of Horde_Db_Adapter_Base_Column objects.
*/
abstract public function columns($tableName, $name = null);
/**
* Returns a table column.
*
* @since Horde_Db 2.1.0
*
* @param string $tableName A table name.
* @param string $columnName A column name.
*
* @throws Horde_Db_Exception if column not found.
* @return Horde_Db_Adapter_Base_Column A column object.
*/
public function column($tableName, $columnName)
{
foreach ($this->columns($tableName) as $column) {
if ($column->getName() == $columnName) {
return $column;
}
}
throw new Horde_Db_Exception("$tableName does not have a column '$columnName'");
}
/**
* Creates a new table.
*
* The $options hash can include the following keys:
* - autoincrementKey (string|array):
* The name of the autoincrementing primary key, if one is to be added
* automatically. Defaults to "id".
* - options (array):
* Any extra options you want appended to the table definition.
* - temporary (boolean):
* Make a temporary table.
* - force (boolean):
* Set to true or false to drop the table before creating it.
* Defaults to false.
*
* Examples:
*
* // Add a backend specific option to the generated SQL (MySQL)
* $schema->createTable('suppliers', array('options' => 'ENGINE=InnoDB DEFAULT CHARSET=utf8')));
*
* generates:
*
* CREATE TABLE suppliers (
* id int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8
*
*
*
* // Rename the primary key column
* $table = $schema->createTable('objects', array('autoincrementKey' => 'guid'));
* $table->column('name', 'string', array('limit' => 80));
* $table->end();
*
* generates:
*
* CREATE TABLE objects (
* guid int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
* name varchar(80)
* )
*
*
*
* // Do not add a primary key column, use fluent interface, use type
* // method.
* $schema->createTable('categories_suppliers', array('autoincrementKey' => false))
* ->column('category_id', 'integer')
* ->integer('supplier_id')
* ->end();
*
* generates:
*
* CREATE TABLE categories_suppliers (
* category_id int(11),
* supplier_id int(11)
* )
*
*
* See also Horde_Db_Adapter_Base_TableDefinition::column() for details on
* how to create columns.
*
* @param string $name A table name.
* @param array $options A list of table options, see the method
* description.
*
* @return Horde_Db_Adapter_Base_TableDefinition The definition of the
* created table.
*/
public function createTable($name, $options = array())
{
$tableDefinition = $this->makeTableDefinition($name, $this, $options);
if (isset($options['autoincrementKey'])) {
if ($options['autoincrementKey'] === true ||
$options['autoincrementKey'] === 'true' ||
$options['autoincrementKey'] === 't' ||
$options['autoincrementKey'] === 1 ||
$options['autoincrementKey'] === '1') {
$pk = 'id';
} elseif ($options['autoincrementKey'] === false ||
$options['autoincrementKey'] === 'false' ||
$options['autoincrementKey'] === 'f' ||
$options['autoincrementKey'] === 0 ||
$options['autoincrementKey'] === '0') {
$pk = false;
} else {
$pk = $options['autoincrementKey'];
}
} else {
$pk = 'id';
}
if ($pk != false) {
$tableDefinition->primaryKey($pk);
}
return $tableDefinition;
}
/**
* Finishes and executes table creation.
*
* @param string|Horde_Db_Adapter_Base_TableDefinition $name
* A table name or object.
* @param array $options
* A list of options. See createTable().
*/
public function endTable($name, $options = array())
{
if ($name instanceof Horde_Db_Adapter_Base_TableDefinition) {
$tableDefinition = $name;
$options = array_merge($tableDefinition->getOptions(), $options);
} else {
$tableDefinition = $this->createTable($name, $options);
}
// Drop previous table.
if (isset($options['force'])) {
$this->dropTable($tableDefinition->getName(), $options);
}
$temp = !empty($options['temporary']) ? 'TEMPORARY' : null;
$opts = !empty($options['options']) ? $options['options'] : null;
$sql = sprintf("CREATE %s TABLE %s (\n%s\n) %s",
$temp,
$this->quoteTableName($tableDefinition->getName()),
$tableDefinition->toSql(),
$opts);
$this->execute($sql);
}
/**
* Renames a table.
*
* @param string $name A table name.
* @param string $newName The new table name.
*/
abstract public function renameTable($name, $newName);
/**
* Drops a table from the database.
*
* @param string $name A table name.
*/
public function dropTable($name)
{
$this->_clearTableCache($name);
return $this->execute('DROP TABLE ' . $this->quoteTableName($name));
}
/**
* Adds a new column to a table.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param string $type A data type.
* @param array $options Column options. See
* Horde_Db_Adapter_Base_TableDefinition#column()
* for details.
*/
public function addColumn($tableName, $columnName, $type,
$options = array())
{
$this->_clearTableCache($tableName);
$options = array_merge(
array('limit' => null,
'precision' => null,
'scale' => null,
'unsigned' => null),
$options);
$sql = sprintf('ALTER TABLE %s ADD %s %s',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName),
$this->typeToSql($type,
$options['limit'],
$options['precision'],
$options['scale'],
$options['unsigned']));
$sql = $this->addColumnOptions($sql, $options);
return $this->execute($sql);
}
/**
* Removes a column from a table.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
*/
public function removeColumn($tableName, $columnName)
{
$this->_clearTableCache($tableName);
$sql = sprintf('ALTER TABLE %s DROP %s',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName));
return $this->execute($sql);
}
/**
* Changes an existing column's definition.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param string $type A data type.
* @param array $options Column options. See
* Horde_Db_Adapter_Base_TableDefinition#column()
* for details.
*/
abstract public function changeColumn($tableName, $columnName, $type, $options = array());
/**
* Sets a new default value for a column.
*
* If you want to set the default value to NULL, you are out of luck. You
* need to execute the apppropriate SQL statement yourself.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param mixed $default The new default value.
*/
abstract public function changeColumnDefault($tableName, $columnName, $default);
/**
* Renames a column.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param string $newColumnName The new column name.
*/
abstract public function renameColumn($tableName, $columnName, $newColumnName);
/**
* Adds a primary key to a table.
*
* @param string $tableName A table name.
* @param string|array $columnName One or more column names.
*
* @throws Horde_Db_Exception
*/
public function addPrimaryKey($tableName, $columns)
{
$this->_clearTableCache($tableName);
$columns = (array)$columns;
$sql = sprintf('ALTER TABLE %s ADD PRIMARY KEY (%s)',
$this->quoteTableName($tableName),
implode(', ', $columns));
return $this->execute($sql);
}
/**
* Removes a primary key from a table.
*
* @param string $tableName A table name.
*
* @throws Horde_Db_Exception
*/
abstract public function removePrimaryKey($tableName);
/**
* Adds a new index to a table.
*
* The index will be named after the table and the first column names,
* unless you pass 'name' as an option.
*
* When creating an index on multiple columns, the first column is used as
* a name for the index. For example, when you specify an index on two
* columns 'first' and 'last', the DBMS creates an index for both columns
* as well as an index for the first colum 'first'. Using just the first
* name for this index makes sense, because you will never have to create a
* singular index with this name.
*
* Examples:
*
* Creating a simple index
*
* $schema->addIndex('suppliers', 'name');
*
* generates
*
* CREATE INDEX suppliers_name_index ON suppliers(name)
*
*
* Creating a unique index
*
* $schema->addIndex('accounts',
* array('branch_id', 'party_id'),
* array('unique' => true));
*
* generates
*
* CREATE UNIQUE INDEX accounts_branch_id_index ON accounts(branch_id, party_id)
*
*
* Creating a named index
*
* $schema->addIndex('accounts',
* array('branch_id', 'party_id'),
* array('unique' => true, 'name' => 'by_branch_party'));
*
* generates
*
* CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
*
*
* @param string $tableName A table name.
* @param string|array $columnName One or more column names.
* @param array $options Index options:
* - name: (string) the index name.
* - unique: (boolean) create a unique
* index?
*
* @return string The index name. @since Horde_Db 2.1.0
*/
public function addIndex($tableName, $columnName, $options = array())
{
$this->_clearTableCache($tableName);
$columnNames = (array)$columnName;
$indexName = empty($options['name'])
? $this->indexName($tableName, array('column' => $columnNames))
: $this->indexName($tableName, $options);
foreach ($columnNames as &$colName) {
$colName = $this->quoteColumnName($colName);
}
$sql = sprintf('CREATE %s INDEX %s ON %s (%s)',
empty($options['unique']) ? null : 'UNIQUE',
$this->quoteColumnName($indexName),
$this->quoteTableName($tableName),
implode(', ', $columnNames));
$this->execute($sql);
return $indexName;
}
/**
* Removes an index from a table.
*
* Examples:
*
* Remove the suppliers_name_index in the suppliers table:
*
* $schema->removeIndex('suppliers', 'name');
*
*
* Remove the index named accounts_branch_id in the accounts table:
*
* $schema->removeIndex('accounts', array('column' => 'branch_id'));
*
*
* Remove the index named by_branch_party in the accounts table:
*
* $schema->removeIndex('accounts', array('name' => 'by_branch_party'));
*
*
* You can remove an index on multiple columns by specifying the first
* column:
*
* $schema->addIndex('accounts', array('username', 'password'))
* $schema->removeIndex('accounts', 'username');
*
*
* @param string $tableName A table name.
* @param string|array $options Either a column name or index options:
* - name: (string) the index name.
* - column: (string|array) column name(s).
*/
public function removeIndex($tableName, $options = array())
{
$this->_clearTableCache($tableName);
$index = $this->indexName($tableName, $options);
$sql = sprintf('DROP INDEX %s ON %s',
$this->quoteColumnName($index),
$this->quoteTableName($tableName));
return $this->execute($sql);
}
/**
* Builds the name for an index.
*
* @param string $tableName A table name.
* @param string|array $options Either a column name or index options:
* - column: (string|array) column name(s).
* - name: (string) the index name to fall
* back to if no column names specified.
*/
public function indexName($tableName, $options = array())
{
if (!is_array($options)) {
$options = array('column' => $options);
}
if (isset($options['column'])) {
$columns = (array)$options['column'];
return "index_{$tableName}_on_" . implode('_and_', $columns);
}
if (isset($options['name'])) {
return $options['name'];
}
throw new Horde_Db_Exception('You must specify the index name');
}
/**
* Recreates, i.e. drops then creates a database.
*
* @param string $name A database name.
*/
public function recreateDatabase($name)
{
$this->dropDatabase($name);
return $this->createDatabase($name);
}
/**
* Creates a database.
*
* @param string $name A database name.
* @param array $options Database options.
*/
abstract public function createDatabase($name, $options = array());
/**
* Drops a database.
*
* @param string $name A database name.
*/
abstract public function dropDatabase($name);
/**
* Returns the name of the currently selected database.
*
* @return string The database name.
*/
abstract public function currentDatabase();
/**
* Generates the SQL definition for a column type.
*
* @param string $type A column type.
* @param integer $limit Maximum column length (non decimal type only)
* @param integer $precision The number precision (decimal type only).
* @param integer $scale The number scaling (decimal columns only).
* @param boolean $unsigned Whether the column is an unsigned number
* (non decimal columns only).
*
* @return string The SQL definition. If $type is not one of the
* internally supported types, $type is returned unchanged.
*/
public function typeToSql($type, $limit = null, $precision = null,
$scale = null, $unsigned = null)
{
$natives = $this->nativeDatabaseTypes();
$native = isset($natives[$type]) ? $natives[$type] : null;
if (empty($native)) {
return $type;
}
$sql = is_array($native) ? $native['name'] : $native;
if ($type == 'decimal' ||
is_array($native) && (isset($native['precision']) || isset($native['scale'])) ||
isset($precision) || isset($scale)) {
$nativePrec = isset($native['precision']) ? $native['precision'] : null;
$nativeScale = isset($native['scale']) ? $native['scale'] : null;
$precision = !empty($precision) ? $precision : $nativePrec;
$scale = !empty($scale) ? $scale : $nativeScale;
if ($precision) {
$sql .= $scale ? "($precision, $scale)" : "($precision)";
}
} else {
$nativeLimit = is_array($native) ? $native['limit'] : null;
// If there is no explicit limit, adjust $nativeLimit for unsigned
// integers.
if (!empty($unsigned) && empty($limit) && is_integer($nativeLimit)) {
$nativeLimit--;
}
if ($limit = !empty($limit) ? $limit : $nativeLimit) {
$sql .= "($limit)";
}
}
return $sql;
}
/**
* Adds default/null options to column SQL definitions.
*
* @param string $sql Existing SQL definition for a column.
* @param array $options Column options:
* - column: (Horde_Db_Adapter_Base_ColumnDefinition
* The column definition class.
* - null: (boolean) Whether to allow NULL values.
* - default: (mixed) Default column value.
* - autoincrement: (boolean) Whether the column is
* an autoincrement column. Driver depedendent.
*
* @return string The manipulated SQL definition.
*/
public function addColumnOptions($sql, $options)
{
/* 'autoincrement' is not handled here - it varies too much between
* DBs. Do autoincrement-specific handling in the driver. */
if (isset($options['null']) && $options['null'] === false) {
$sql .= ' NOT NULL';
}
if (isset($options['default'])) {
$default = $options['default'];
$column = isset($options['column']) ? $options['column'] : null;
$sql .= ' DEFAULT ' . $this->quote($default, $column);
}
return $sql;
}
/**
* Generates a DISTINCT clause for SELECT queries.
*
*
* $connection->distinct('posts.id', 'posts.created_at DESC')
*
*
* @param string $columns A column list.
* @param string $orderBy An ORDER clause.
*
* @return string The generated DISTINCT clause.
*/
public function distinct($columns, $orderBy = null)
{
return 'DISTINCT ' . $columns;
}
/**
* Adds an ORDER BY clause to an existing query.
*
* @param string $sql An SQL query to manipulate.
* @param array $options Options:
* - order: Order column an direction.
*
* @return string The manipulated SQL query.
*/
public function addOrderByForAssocLimiting($sql, $options)
{
return $sql . 'ORDER BY ' . $options['order'];
}
/**
* Generates an INTERVAL clause for SELECT queries.
*
* @deprecated since version 1.2.0. This function does not work with SQLite
* as a backend so you should avoid using it. Use "modifyDate()" instead.
*
* @param string $interval The interval.
* @param string $precision The precision.
*
* @return string The generated INTERVAL clause.
*/
public function interval($interval, $precision)
{
return 'INTERVAL ' . $precision . ' ' . $interval;
}
/**
* Generates a modified date for SELECT queries.
*
* @param string $reference The reference date - this is a column
* referenced in the SELECT.
* @param string $operator Add or subtract time? (+/-)
* @param integer $amount The shift amount (number of days if $interval
* is DAY, etc).
* @param string $interval The interval (SECOND, MINUTE, HOUR, DAY,
* MONTH, YEAR).
*
* @return string The generated INTERVAL clause.
*/
public function modifyDate($reference, $operator, $amount, $interval)
{
if (!is_int($amount)) {
throw new InvalidArgumentException('$amount parameter must be an integer');
}
return sprintf('%s %s INTERVAL \'%s\' %s',
$reference,
$operator,
$amount,
$interval);
}
/**
* Returns an expression using the specified operator.
*
* @param string $lhs The column or expression to test.
* @param string $op The operator.
* @param string $rhs The comparison value.
* @param boolean $bind If true, the method returns the query and a list
* of values suitable for binding as an array.
* @param array $params Any additional parameters for the operator.
*
* @return string|array The SQL test fragment, or an array containing the
* query and a list of values if $bind is true.
*/
public function buildClause($lhs, $op, $rhs, $bind = false,
$params = array())
{
$lhs = $this->_escapePrepare($lhs);
switch ($op) {
case '|':
case '&':
if ($bind) {
return array($lhs . ' ' . $op . ' ?',
array((int)$rhs));
}
return $lhs . ' ' . $op . ' ' . (int)$rhs;
case '~':
if ($bind) {
return array($lhs . ' ' . $op . ' ?', array($rhs));
}
return $lhs . ' ' . $op . ' ' . $rhs;
case 'IN':
if ($bind) {
if (is_array($rhs)) {
return array($lhs . ' IN (?' . str_repeat(', ?', count($rhs) - 1) . ')', $rhs);
}
/* We need to bind each member of the IN clause separately to
* ensure proper quoting. */
if (substr($rhs, 0, 1) == '(') {
$rhs = substr($rhs, 1);
}
if (substr($rhs, -1) == ')') {
$rhs = substr($rhs, 0, -1);
}
$ids = preg_split('/\s*,\s*/', $rhs);
return array($lhs . ' IN (?' . str_repeat(', ?', count($ids) - 1) . ')', $ids);
}
if (is_array($rhs)) {
return $lhs . ' IN ' . implode(', ', $rhs);
}
return $lhs . ' IN ' . $rhs;
case 'LIKE':
$query = 'LOWER(%s) LIKE LOWER(%s)';
if ($bind) {
if (empty($params['begin'])) {
return array(sprintf($query, $lhs, '?'),
array('%' . $rhs . '%'));
}
return array(sprintf('(' . $query . ' OR ' . $query . ')',
$lhs, '?', $lhs, '?'),
array($rhs . '%', '% ' . $rhs . '%'));
}
if (empty($params['begin'])) {
return sprintf($query,
$lhs,
$this->_escapePrepare($this->quote('%' . $rhs . '%')));
}
return sprintf('(' . $query . ' OR ' . $query . ')',
$lhs,
$this->_escapePrepare($this->quote($rhs . '%')),
$lhs,
$this->_escapePrepare($this->quote('% ' . $rhs . '%')));
default:
if ($bind) {
return array($lhs . ' ' . $this->_escapePrepare($op) . ' ?', array($rhs));
}
return $lhs . ' ' . $this->_escapePrepare($op . ' ' . $this->quote($rhs));
}
}
/**
* Escapes all characters in a string that are placeholders for
* prepare/execute methods.
*
* @param string $query A string to escape.
*
* @return string The correctly escaped string.
*/
protected function _escapePrepare($query)
{
return preg_replace('/[?!&]/', '\\\\$0', $query);
}
/*##########################################################################
# Protected
##########################################################################*/
/**
* Clears the cache for tables when altering them.
*
* @param string $tableName A table name.
*/
protected function _clearTableCache($tableName)
{
$this->cacheWrite('tables/columns/' . $tableName, '');
$this->cacheWrite('tables/indexes/' . $tableName, '');
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Base/Table.php 0000664 0001750 0001750 00000010777 13054774261 017442 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Base_Table implements ArrayAccess, IteratorAggregate
{
/**
* The table's name.
*
* @var string
*/
protected $_name;
protected $_primaryKey;
protected $_columns;
protected $_indexes;
/*##########################################################################
# Construct/Destruct
##########################################################################*/
/**
* Constructor.
*
* @param string $name The table's name.
*/
public function __construct($name, $primaryKey, $columns, $indexes)
{
$this->_name = $name;
$this->_primaryKey = $primaryKey;
$this->_columns = $columns;
$this->_indexes = $indexes;
}
/*##########################################################################
# Accessor
##########################################################################*/
/**
* @return string
*/
public function getName()
{
return $this->_name;
}
/**
* @return mixed
*/
public function getPrimaryKey()
{
return $this->_primaryKey;
}
/**
* @return array
*/
public function getColumns()
{
return $this->_columns;
}
/**
* @return Horde_Db_Adapter_Base_Column
*/
public function getColumn($column)
{
return isset($this->_columns[$column]) ? $this->_columns[$column] : null;
}
/**
* @return array
*/
public function getColumnNames()
{
$names = array();
foreach ($this->_columns as $column) {
$names[] = $column->getName();
}
return $names;
}
/**
* @return array
*/
public function getIndexes()
{
return $this->_indexes;
}
/**
* @return array
*/
public function getIndexNames()
{
$names = array();
foreach ($this->_indexes as $index) {
$names[] = $index->getName();
}
return $names;
}
/*##########################################################################
# Object composition
##########################################################################*/
public function __get($key)
{
return $this->getColumn($key);
}
public function __isset($key)
{
return isset($this->_columns[$key]);
}
/*##########################################################################
# ArrayAccess
##########################################################################*/
/**
* ArrayAccess: Check if the given offset exists
*
* @param int $offset
* @return boolean
*/
public function offsetExists($offset)
{
return isset($this->_columns[$offset]);
}
/**
* ArrayAccess: Return the value for the given offset.
*
* @param int $offset
* @return object {@link {@Horde_Db_Adapter_Base_ColumnDefinition}
*/
public function offsetGet($offset)
{
return $this->getColumn($offset);
}
/**
* ArrayAccess: Set value for given offset
*
* @param int $offset
* @param mixed $value
*/
public function offsetSet($offset, $value)
{
}
/**
* ArrayAccess: remove element
*
* @param int $offset
*/
public function offsetUnset($offset)
{
}
/*##########################################################################
# IteratorAggregate
##########################################################################*/
public function getIterator()
{
return new ArrayIterator($this->_columns);
}
/*##########################################################################
# Protected
##########################################################################*/
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Base/TableDefinition.php 0000664 0001750 0001750 00000022711 13054774261 021442 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Base_TableDefinition implements ArrayAccess, IteratorAggregate
{
protected $_name = null;
protected $_base = null;
protected $_options = null;
protected $_columns = null;
protected $_primaryKey = null;
protected $_columntypes = array('string', 'text', 'integer', 'float',
'datetime', 'timestamp', 'time', 'date', 'binary', 'boolean');
/**
* Constructor.
*
* @param string $name
* @param Horde_Db_Adapter_Base_Schema $base
* @param array $options
*/
public function __construct($name, $base, $options = array())
{
$this->_name = $name;
$this->_base = $base;
$this->_options = $options;
$this->_columns = array();
}
/**
* @return string
*/
public function getName()
{
return $this->_name;
}
/**
* @return array
*/
public function getOptions()
{
return $this->_options;
}
/**
* @param string $name
*/
public function primaryKey($name)
{
if (is_scalar($name) && $name !== false) {
$this->column($name, 'autoincrementKey');
}
$this->_primaryKey = $name;
}
/**
* Adds a new column to the table definition.
*
* Examples:
*
* // Assuming $def is an instance of Horde_Db_Adapter_Base_TableDefinition
*
* $def->column('granted', 'boolean');
* // => granted BOOLEAN
*
* $def->column('picture', 'binary', 'limit' => 4096);
* // => picture BLOB(4096)
*
* $def->column('sales_stage', 'string', array('limit' => 20, 'default' => 'new', 'null' => false));
* // => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
*
*
* @param string $type Column type, one of:
* autoincrementKey, string, text, integer, float,
* datetime, timestamp, time, date, binary, boolean.
* @param array $options Column options:
* - autoincrement: (boolean) Whether the column is
* an autoincrement column. Restrictions are
* RDMS specific.
* - default: (mixed) The column's default value.
* You cannot explicitly set the default value to
* NULL. Simply leave off this option if you want
* a NULL default value.
* - limit: (integer) Maximum column length (string,
* text, binary or integer columns only)
* - null: (boolean) Whether NULL values are allowed
* in the column.
* - precision: (integer) The number precision
* (float columns only).
* - scale: (integer) The number scaling (float
* columns only).
* - unsigned: (boolean) Whether the column is an
* unsigned number (integer columns only).
*
* @return Horde_Db_Adapter_Base_TableDefinition This object.
*/
public function column($name, $type, $options = array())
{
if ($name == $this->_primaryKey) {
throw new LogicException($name . ' has already been added as a primary key');
}
$options = array_merge(
array('limit' => null,
'precision' => null,
'scale' => null,
'unsigned' => null,
'default' => null,
'null' => null,
'autoincrement' => null),
$options);
$column = $this->_base->makeColumnDefinition(
$this->_base,
$name,
$type,
$options['limit'],
$options['precision'],
$options['scale'],
$options['unsigned'],
$options['default'],
$options['null'],
$options['autoincrement']
);
$this[$name] ? $this[$name] = $column : $this->_columns[] = $column;
return $this;
}
/**
* Adds created_at and updated_at columns to the table.
*/
public function timestamps()
{
return $this->column('created_at', 'datetime')
->column('updated_at', 'datetime');
}
/**
* Add one or several references to foreign keys
*
* This method returns self.
*/
public function belongsTo($columns)
{
if (!is_array($columns)) { $columns = array($columns); }
foreach ($columns as $col) {
$this->column($col . '_id', 'integer');
}
return $this;
}
/**
* Alias for the belongsTo() method
*
* This method returns self.
*/
public function references($columns)
{
return $this->belongsTo($columns);
}
/**
* Use __call to provide shorthand column creation ($this->integer(), etc.)
*/
public function __call($method, $arguments)
{
if (!in_array($method, $this->_columntypes)) {
throw new BadMethodCallException('Call to undeclared method "' . $method . '"');
}
if (count($arguments) > 0 && count($arguments) < 3) {
return $this->column($arguments[0], $method,
isset($arguments[1]) ? $arguments[1] : array());
}
throw new BadMethodCallException('Method "'.$method.'" takes two arguments');
}
/**
* Wrap up table creation block & create the table
*/
public function end()
{
$this->_base->endTable($this);
}
/**
* Returns a String whose contents are the column definitions
* concatenated together. This string can then be pre and appended to
* to generate the final SQL to create the table.
*
* @return string
*/
public function toSql()
{
$cols = array();
foreach ($this->_columns as $col) {
$cols[] = $col->toSql();
}
$sql = ' ' . implode(", \n ", $cols);
// Specify composite primary keys as well
if (is_array($this->_primaryKey)) {
$pk = array();
foreach ($this->_primaryKey as $pkColumn) {
$pk[] = $this->_base->quoteColumnName($pkColumn);
}
$sql .= ", \n PRIMARY KEY(" . implode(', ', $pk) . ')';
}
return $sql;
}
public function __toString()
{
return $this->toSql();
}
/*##########################################################################
# ArrayAccess
##########################################################################*/
/**
* ArrayAccess: Check if the given offset exists
*
* @param int $offset
* @return boolean
*/
public function offsetExists($offset)
{
foreach ($this->_columns as $column) {
if ($column->getName() == $offset) return true;
}
return false;
}
/**
* ArrayAccess: Return the value for the given offset.
*
* @param int $offset
* @return object {@link {@Horde_Db_Adapter_Base_ColumnDefinition}
*/
public function offsetGet($offset)
{
if (!$this->offsetExists($offset)) {
return null;
}
foreach ($this->_columns as $column) {
if ($column->getName() == $offset) {
return $column;
}
}
}
/**
* ArrayAccess: Set value for given offset
*
* @param int $offset
* @param mixed $value
*/
public function offsetSet($offset, $value)
{
foreach ($this->_columns as $key=>$column) {
if ($column->getName() == $offset) {
$this->_columns[$key] = $value;
}
}
}
/**
* ArrayAccess: remove element
*
* @param int $offset
*/
public function offsetUnset($offset)
{
foreach ($this->_columns as $key=>$column) {
if ($column->getName() == $offset) {
unset($this->_columns[$key]);
}
}
}
/*##########################################################################
# IteratorAggregate
##########################################################################*/
public function getIterator()
{
return new ArrayIterator($this->_columns);
}
/*##########################################################################
# Protected
##########################################################################*/
/**
* Get the types
*/
protected function _native()
{
return $this->_base->nativeDatabaseTypes();
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Mysql/Column.php 0000664 0001750 0001750 00000004524 13054774261 020074 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Mysql_Column extends Horde_Db_Adapter_Base_Column
{
/**
* @var array
*/
protected $_hasEmptyStringDefault = array('binary', 'string', 'text');
/**
* @var string
*/
protected $_originalDefault = null;
/**
* Construct
* @param string $name
* @param string $default
* @param string $sqlType
* @param boolean $null
*/
public function __construct($name, $default, $sqlType=null, $null=true)
{
$this->_originalDefault = $default;
parent::__construct($name, $default, $sqlType, $null);
if ($this->_isMissingDefaultForgedAsEmptyString()) {
$this->_default = null;
}
}
/**
*/
protected function _setSimplifiedType()
{
if (strpos(Horde_String::lower($this->_sqlType), 'tinyint(1)') !== false) {
$this->_type = 'boolean';
return;
} elseif (preg_match('/enum/i', $this->_sqlType)) {
$this->_type = 'string';
return;
}
parent::_setSimplifiedType();
}
/**
* MySQL misreports NOT NULL column default when none is given.
* We can't detect this for columns which may have a legitimate ''
* default (string, text, binary) but we can for others (integer,
* datetime, boolean, and the rest).
*
* Test whether the column has default '', is not null, and is not
* a type allowing default ''.
*
* @return boolean
*/
protected function _isMissingDefaultForgedAsEmptyString()
{
return !$this->_null && $this->_originalDefault == '' &&
!in_array($this->_type, $this->_hasEmptyStringDefault);
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Mysql/Result.php 0000664 0001750 0001750 00000003361 13054774261 020113 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* This class represents the result set of a SELECT query from the MySQL
* driver.
*
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Mysql_Result extends Horde_Db_Adapter_Base_Result
{
/**
* Maps Horde_Db fetch mode constant to the extension constants.
*
* @var array
*/
protected $_map = array(
Horde_Db::FETCH_ASSOC => MYSQL_ASSOC,
Horde_Db::FETCH_NUM => MYSQL_NUM,
Horde_Db::FETCH_BOTH => MYSQL_BOTH
);
/**
* Returns a row from a resultset.
*
* @return array|boolean The next row in the resultset or false if there
* are no more results.
*/
protected function _fetchArray()
{
return mysql_fetch_array(
$this->_result,
$this->_map[$this->_fetchMode]
);
}
/**
* Returns the number of columns in the result set.
*
* @return integer Number of columns.
*/
protected function _columnCount()
{
return mysql_num_fields($this->_result);
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Mysql/Schema.php 0000664 0001750 0001750 00000052215 13054774261 020037 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* Class for MySQL-specific managing of database schemes and handling of SQL
* dialects and quoting.
*
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Mysql_Schema extends Horde_Db_Adapter_Base_Schema
{
/*##########################################################################
# Object factories
##########################################################################*/
/**
* Factory for Column objects.
*
* @param string $name The column's name, such as "supplier_id" in
* "supplier_id int(11)".
* @param string $default The type-casted default value, such as "new" in
* "sales_stage varchar(20) default 'new'".
* @param string $sqlType Used to extract the column's type, length and
* signed status, if necessary. For example
* "varchar" and "60" in "company_name varchar(60)"
* or "unsigned => true" in "int(10) UNSIGNED".
* @param boolean $null Whether this column allows NULL values.
*
* @return Horde_Db_Adapter_Mysql_Column A column object.
*/
public function makeColumn($name, $default, $sqlType = null, $null = true)
{
return new Horde_Db_Adapter_Mysql_Column($name, $default, $sqlType, $null);
}
/*##########################################################################
# Quoting
##########################################################################*/
/**
* Returns a quoted form of the column name.
*
* @param string $name A column name.
*
* @return string The quoted column name.
*/
public function quoteColumnName($name)
{
return '`' . str_replace('`', '``', $name) . '`';
}
/**
* Returns a quoted form of the table name.
*
* Defaults to column name quoting.
*
* @param string $name A table name.
*
* @return string The quoted table name.
*/
public function quoteTableName($name)
{
return str_replace('.', '`.`', $this->quoteColumnName($name));
}
/*##########################################################################
# Schema Statements
##########################################################################*/
/**
* Returns a hash of mappings from the abstract data types to the native
* database types.
*
* See TableDefinition::column() for details on the recognized abstract
* data types.
*
* @see TableDefinition::column()
*
* @return array A database type map.
*/
public function nativeDatabaseTypes()
{
return array(
'autoincrementKey' => 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
'string' => array('name' => 'varchar', 'limit' => 255),
'text' => array('name' => 'text', 'limit' => null),
'mediumtext' => array('name' => 'mediumtext', 'limit' => null),
'longtext' => array('name' => 'longtext', 'limit' => null),
'integer' => array('name' => 'int', 'limit' => 11),
'float' => array('name' => 'float', 'limit' => null),
'decimal' => array('name' => 'decimal', 'limit' => null),
'datetime' => array('name' => 'datetime', 'limit' => null),
'timestamp' => array('name' => 'datetime', 'limit' => null),
'time' => array('name' => 'time', 'limit' => null),
'date' => array('name' => 'date', 'limit' => null),
'binary' => array('name' => 'longblob', 'limit' => null),
'boolean' => array('name' => 'tinyint', 'limit' => 1),
);
}
/**
* Returns a list of all tables of the current database.
*
* @return array A table list.
*/
public function tables()
{
return $this->selectValues('SHOW TABLES');
}
/**
* Returns a table's primary key.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return Horde_Db_Adapter_Base_Index The primary key index object.
*/
public function primaryKey($tableName, $name = null)
{
// Share the column cache with the columns() method
$rows = @unserialize($this->cacheRead("tables/columns/$tableName"));
if (!$rows) {
$rows = $this->selectAll(
'SHOW FIELDS FROM ' . $this->quoteTableName($tableName),
$name);
$this->cacheWrite("tables/columns/$tableName", serialize($rows));
}
$pk = $this->makeIndex($tableName, 'PRIMARY', true, true, array());
foreach ($rows as $row) {
if ($row['Key'] == 'PRI') {
$pk->columns[] = $row['Field'];
}
}
return $pk;
}
/**
* Returns a list of tables indexes.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return array A list of Horde_Db_Adapter_Base_Index objects.
*/
public function indexes($tableName, $name=null)
{
$indexes = @unserialize($this->cacheRead("tables/indexes/$tableName"));
if (!$indexes) {
$indexes = array();
$currentIndex = null;
foreach ($this->select('SHOW KEYS FROM ' . $this->quoteTableName($tableName)) as $row) {
if ($currentIndex != $row['Key_name']) {
if ($row['Key_name'] == 'PRIMARY') {
continue;
}
$currentIndex = $row['Key_name'];
$indexes[] = $this->makeIndex(
$tableName, $row['Key_name'], false, $row['Non_unique'] == '0', array());
}
$indexes[count($indexes) - 1]->columns[] = $row['Column_name'];
}
$this->cacheWrite("tables/indexes/$tableName", serialize($indexes));
}
return $indexes;
}
/**
* Returns a list of table columns.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return array A list of Horde_Db_Adapter_Base_Column objects.
*/
public function columns($tableName, $name=null)
{
$rows = @unserialize($this->cacheRead("tables/columns/$tableName"));
if (!$rows) {
$rows = $this->selectAll('SHOW FIELDS FROM ' . $this->quoteTableName($tableName), $name);
$this->cacheWrite("tables/columns/$tableName", serialize($rows));
}
// Create columns from rows.
$columns = array();
foreach ($rows as $row) {
$columns[$row['Field']] = $this->makeColumn(
$row['Field'], $row['Default'], $row['Type'], $row['Null'] == 'YES');
}
return $columns;
}
/**
* Finishes and executes table creation.
*
* @param string|Horde_Db_Adapter_Base_TableDefinition $name
* A table name or object.
* @param array $options
* A list of options. See createTable().
*/
public function endTable($name, $options = array())
{
if ($name instanceof Horde_Db_Adapter_Base_TableDefinition) {
$options = array_merge($name->getOptions(), $options);
}
if (isset($options['options'])) {
$opts = $options['options'];
} else {
if (empty($options['charset'])) {
$options['charset'] = $this->getCharset();
}
$opts = 'ENGINE=InnoDB DEFAULT CHARSET=' . $options['charset'];
}
return parent::endTable($name, array_merge(array('options' => $opts), $options));
}
/**
* Renames a table.
*
* @param string $name A table name.
* @param string $newName The new table name.
*/
public function renameTable($name, $newName)
{
$this->_clearTableCache($name);
$sql = sprintf('ALTER TABLE %s RENAME %s',
$this->quoteTableName($name),
$this->quoteTableName($newName));
return $this->execute($sql);
}
/**
* Changes an existing column's definition.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param string $type A data type.
* @param array $options Column options. See
* Horde_Db_Adapter_Base_TableDefinition#column()
* for details.
*/
public function changeColumn($tableName, $columnName, $type,
$options = array())
{
$this->_clearTableCache($tableName);
$quotedTableName = $this->quoteTableName($tableName);
$quotedColumnName = $this->quoteColumnName($columnName);
$options = array_merge(
array('limit' => null,
'precision' => null,
'scale' => null,
'unsigned' => null),
$options);
$sql = sprintf('SHOW COLUMNS FROM %s LIKE %s',
$quotedTableName,
$this->quoteString($columnName));
$row = $this->selectOne($sql);
if (!array_key_exists('default', $options)) {
$options['default'] = $row['Default'];
$options['column'] = $this->makeColumn($columnName,
$row['Default'],
$row['Type'],
$row['Null'] == 'YES');
}
$typeSql = $this->typeToSql($type, $options['limit'],
$options['precision'], $options['scale'],
$options['unsigned']);
$dropPk = ($type == 'autoincrementKey' && $row['Key'] == 'PRI')
? 'DROP PRIMARY KEY,'
: '';
$sql = sprintf('ALTER TABLE %s %s CHANGE %s %s %s',
$quotedTableName,
$dropPk,
$quotedColumnName,
$quotedColumnName,
$typeSql);
if ($type != 'autoincrementKey') {
$sql = $this->addColumnOptions($sql, $options);
}
$this->execute($sql);
}
/**
* Sets a new default value for a column.
*
* If you want to set the default value to NULL, you are out of luck. You
* need to execute the apppropriate SQL statement yourself.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param mixed $default The new default value.
*/
public function changeColumnDefault($tableName, $columnName, $default)
{
$this->_clearTableCache($tableName);
$quotedTableName = $this->quoteTableName($tableName);
$quotedColumnName = $this->quoteColumnName($columnName);
$sql = sprintf('SHOW COLUMNS FROM %s LIKE %s',
$quotedTableName,
$this->quoteString($columnName));
$res = $this->selectOne($sql);
$column = $this->makeColumn($columnName, $res['Default'], $res['Type'], $res['Null'] == 'YES');
$default = $this->quote($default, $column);
$sql = sprintf('ALTER TABLE %s CHANGE %s %s %s DEFAULT %s',
$quotedTableName,
$quotedColumnName,
$quotedColumnName,
$res['Type'],
$default);
return $this->execute($sql);
}
/**
* Renames a column.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param string $newColumnName The new column name.
*/
public function renameColumn($tableName, $columnName, $newColumnName)
{
$this->_clearTableCache($tableName);
$quotedTableName = $this->quoteTableName($tableName);
$quotedColumnName = $this->quoteColumnName($columnName);
$sql = sprintf('SHOW COLUMNS FROM %s LIKE %s',
$quotedTableName,
$this->quoteString($columnName));
$res = $this->selectOne($sql);
$currentType = $res['Type'];
$sql = sprintf('ALTER TABLE %s CHANGE %s %s %s',
$quotedTableName,
$quotedColumnName,
$this->quoteColumnName($newColumnName),
$currentType);
return $this->execute($sql);
}
/**
* Removes a primary key from a table.
*
* @param string $tableName A table name.
*
* @throws Horde_Db_Exception
*/
public function removePrimaryKey($tableName)
{
$this->_clearTableCache($tableName);
$sql = sprintf('ALTER TABLE %s DROP PRIMARY KEY',
$this->quoteTableName($tableName));
return $this->execute($sql);
}
/**
* Builds the name for an index.
*
* Cuts the index name to the maximum length of 64 characters limited by
* MySQL.
*
* @param string $tableName A table name.
* @param string|array $options Either a column name or index options:
* - column: (string|array) column name(s).
* - name: (string) the index name to fall
* back to if no column names specified.
*/
public function indexName($tableName, $options = array())
{
return substr(parent::indexName($tableName, $options), 0, 64);
}
/**
* Creates a database.
*
* @param string $name A database name.
* @param array $options Database options.
*/
public function createDatabase($name, $options = array())
{
return $this->execute("CREATE DATABASE `$name`");
}
/**
* Drops a database.
*
* @param string $name A database name.
*/
public function dropDatabase($name)
{
return $this->execute("DROP DATABASE IF EXISTS `$name`");
}
/**
* Returns the name of the currently selected database.
*
* @return string The database name.
*/
public function currentDatabase()
{
return $this->selectValue('SELECT DATABASE() AS db');
}
/**
* Generates the SQL definition for a column type.
*
* @param string $type A column type.
* @param integer $limit Maximum column length (non decimal type only)
* @param integer $precision The number precision (decimal type only).
* @param integer $scale The number scaling (decimal columns only).
* @param boolean $unsigned Whether the column is an unsigned number
* (non decimal columns only).
*
* @return string The SQL definition. If $type is not one of the
* internally supported types, $type is returned unchanged.
*/
public function typeToSql($type, $limit = null, $precision = null,
$scale = null, $unsigned = null)
{
// If there is no explicit limit, adjust $nativeLimit for unsigned
// integers.
if ($type == 'integer' && !empty($unsigned) && empty($limit)) {
$natives = $this->nativeDatabaseTypes();
$native = isset($natives[$type]) ? $natives[$type] : null;
if (empty($native)) {
return $type;
}
$nativeLimit = is_array($native) ? $native['limit'] : null;
if (is_integer($nativeLimit)) {
$limit = $nativeLimit - 1;
}
}
$sql = parent::typeToSql($type, $limit, $precision, $scale, $unsigned);
if (!empty($unsigned)) {
$sql .= ' UNSIGNED';
}
return $sql;
}
/**
* Adds default/null options to column SQL definitions.
*
* @param string $sql Existing SQL definition for a column.
* @param array $options Column options:
* - null: (boolean) Whether to allow NULL values.
* - default: (mixed) Default column value.
* - autoincrement: (boolean) Whether the column is
* an autoincrement column. Driver depedendent.
* - after: (string) Insert column after this one.
* MySQL specific.
*
* @return string The manipulated SQL definition.
*/
public function addColumnOptions($sql, $options)
{
$sql = parent::addColumnOptions($sql, $options);
if (isset($options['after'])) {
$sql .= ' AFTER ' . $this->quoteColumnName($options['after']);
}
if (!empty($options['autoincrement'])) {
$sql .= ' AUTO_INCREMENT';
}
return $sql;
}
/**
* Returns an expression using the specified operator.
*
* @param string $lhs The column or expression to test.
* @param string $op The operator.
* @param string $rhs The comparison value.
* @param boolean $bind If true, the method returns the query and a list
* of values suitable for binding as an array.
* @param array $params Any additional parameters for the operator.
*
* @return string|array The SQL test fragment, or an array containing the
* query and a list of values if $bind is true.
*/
public function buildClause($lhs, $op, $rhs, $bind = false,
$params = array())
{
switch ($op) {
case '~':
if ($bind) {
return array($lhs . ' REGEXP ?', array($rhs));
} else {
return $lhs . ' REGEXP ' . $rhs;
}
}
return parent::buildClause($lhs, $op, $rhs, $bind, $params);
}
/*##########################################################################
# MySQL specific methods
##########################################################################*/
/**
* Returns the character set of query results.
*
* @return string The result's charset.
*/
public function getCharset()
{
return $this->showVariable('character_set_results');
}
/**
* Sets the client and result charset.
*
* @param string $charset The character set to use for client queries and
* results.
*/
public function setCharset($charset)
{
$charset = $this->_mysqlCharsetName($charset);
$this->execute('SET NAMES ' . $this->quoteString($charset));
}
/**
* Returns the MySQL name of a character set.
*
* @param string $charset A charset name.
*
* @return string MySQL-normalized charset.
*/
public function _mysqlCharsetName($charset)
{
$charset = preg_replace(array('/[^a-z0-9]/', '/iso8859(\d)/'),
array('', 'latin$1'),
Horde_String::lower($charset));
$validCharsets = $this->selectValues('SHOW CHARACTER SET');
if (!in_array($charset, $validCharsets)) {
throw new Horde_Db_Exception($charset . ' is not supported by MySQL (' . implode(', ', $validCharsets) . ')');
}
return $charset;
}
/**
* Returns the database collation strategy.
*
* @return string Database collation.
*/
public function getCollation()
{
return $this->showVariable('collation_database');
}
/**
* Returns a database variable.
*
* Convenience wrapper around "SHOW VARIABLES LIKE 'name'".
*
* @param string $name A variable name.
*
* @return string The variable value.
* @throws Horde_Db_Exception
*/
public function showVariable($name)
{
$value = $this->selectOne('SHOW VARIABLES LIKE ' . $this->quoteString($name));
if ($value['Variable_name'] == $name) {
return $value['Value'];
} else {
throw new Horde_Db_Exception($name . ' is not a recognized variable');
}
}
/**
*/
public function caseSensitiveEqualityOperator()
{
return '= BINARY';
}
/**
*/
public function limitedUpdateConditions($whereSql, $quotedTableName,
$quotedPrimaryKey)
{
return $whereSql;
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Mysqli/Result.php 0000664 0001750 0001750 00000003311 13054774261 020257 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* This class represents the result set of a SELECT query from the MySQLi
* driver.
*
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Mysqli_Result extends Horde_Db_Adapter_Base_Result
{
/**
* Maps Horde_Db fetch mode constant to the extension constants.
*
* @var array
*/
protected $_map = array(
Horde_Db::FETCH_ASSOC => MYSQLI_ASSOC,
Horde_Db::FETCH_NUM => MYSQLI_NUM,
Horde_Db::FETCH_BOTH => MYSQLI_BOTH
);
/**
* Returns a row from a resultset.
*
* @return array|boolean The next row in the resultset or false if there
* are no more results.
*/
protected function _fetchArray()
{
return $this->_result->fetch_array($this->_map[$this->_fetchMode]);
}
/**
* Returns the number of columns in the result set.
*
* @return integer Number of columns.
*/
protected function _columnCount()
{
return $this->_result->field_count;
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Oracle/Column.php 0000664 0001750 0001750 00000005751 13054774261 020177 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* @since Horde_Db 2.1.0
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Oracle_Column extends Horde_Db_Adapter_Base_Column
{
/*##########################################################################
# Construct/Destruct
##########################################################################*/
/**
* Constructor.
*
* @param string $name Column name, such as "supplier_id" in
* "supplier_id int(11)".
* @param string $default Type-casted default value, such as "new"
* in "sales_stage varchar(20) default 'new'".
* @param string $sqlType Column type.
* @param boolean $null Whether this column allows NULL values.
* @param integer $length Column width.
* @param integer $precision Precision for NUMBER and FLOAT columns.
* @param integer $scale Number of digits to the right of the decimal
* point in a number.
*/
public function __construct($name, $default, $sqlType = null, $null = true,
$length = null, $precision = null,
$scale = null)
{
$this->_name = $name;
$this->_sqlType = Horde_String::lower($sqlType);
$this->_null = $null;
$this->_limit = $length;
$this->_precision = $precision;
$this->_scale = $scale;
$this->_setSimplifiedType();
$this->_isText = $this->_type == 'text' || $this->_type == 'string';
$this->_isNumber = $this->_type == 'float' || $this->_type == 'integer' || $this->_type == 'decimal';
$this->_default = $this->typeCast($default);
}
/*##########################################################################
# Type Juggling
##########################################################################*/
/**
* Used to convert from BLOBs to Strings
*
* @return string
*/
public function binaryToString($value)
{
if (is_a($value, 'OCI-Lob')) {
return $value->load();
}
return parent::binaryToString($value);
}
/*##########################################################################
# Protected
##########################################################################*/
/**
*/
protected function _setSimplifiedType()
{
if (Horde_String::lower($this->_sqlType) == 'number' &&
$this->_precision == 1) {
$this->_type = 'boolean';
return;
}
parent::_setSimplifiedType();
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Oracle/Result.php 0000664 0001750 0001750 00000002710 13054774261 020210 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* @since Horde_Db 2.1.0
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Oracle_Result extends Horde_Db_Adapter_Base_Result
{
/**
* Maps Horde_Db fetch mode constant to the extension constants.
*
* @var array
*/
protected $_map = array(
Horde_Db::FETCH_ASSOC => OCI_ASSOC,
Horde_Db::FETCH_NUM => OCI_NUM,
Horde_Db::FETCH_BOTH => OCI_BOTH
);
/**
* Returns a row from a resultset.
*
* @return array|boolean The next row in the resultset or false if there
* are no more results.
*/
protected function _fetchArray()
{
$array = oci_fetch_array(
$this->_result, $this->_map[$this->_fetchMode] | OCI_RETURN_NULLS
);
if ($array) {
$array = array_change_key_case($array, CASE_LOWER);
}
return $array;
}
/**
* Returns the number of columns in the result set.
*
* @return integer Number of columns.
*/
protected function _columnCount()
{
return oci_num_fields($this->_result);
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Oracle/Schema.php 0000664 0001750 0001750 00000100261 13054774261 020132 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* Class for Oracle-specific managing of database schemes and handling of SQL
* dialects and quoting.
*
* @since Horde_Db 2.1.0
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Oracle_Schema extends Horde_Db_Adapter_Base_Schema
{
/*##########################################################################
# Object factories
##########################################################################*/
/**
* Factory for Column objects.
*
* @param string $name Column name, such as "supplier_id" in
* "supplier_id int(11)".
* @param string $default Type-casted default value, such as "new"
* in "sales_stage varchar(20) default 'new'".
* @param string $sqlType Column type.
* @param boolean $null Whether this column allows NULL values.
* @param integer $length Column width.
* @param integer $precision Precision for NUMBER and FLOAT columns.
* @param integer $scale Number of digits to the right of the decimal
* point in a number.
*
* @return Horde_Db_Adapter_Base_Column A column object.
*/
public function makeColumn($name, $default, $sqlType = null, $null = true,
$length = null, $precision = null, $scale = null)
{
return new Horde_Db_Adapter_Oracle_Column(
$name, $default, $sqlType, $null,
$length, $precision, $scale
);
}
/**
* Factory for TableDefinition objects.
*
* @return Horde_Db_Adapter_Base_TableDefinition A table definition object.
*/
public function makeTableDefinition($name, $base, $options = array())
{
return new Horde_Db_Adapter_Oracle_TableDefinition($name, $base, $options);
}
/*##########################################################################
# Quoting
##########################################################################*/
/**
* Returns a quoted form of the column name.
*
* With Oracle, if using quoted identifiers, you need to use them
* everywhere. 'SELECT * FROM "tablename"' is NOT the same as 'SELECT *
* FROM tablename'. Thus we cannot blindly quote table or column names,
* unless we know that in subsequent queries they will be used too.
*
* @param string $name A column name.
*
* @return string The quoted column name.
*/
public function quoteColumnName($name)
{
return $name;
}
/**
* Returns a quoted binary value.
*
* @param mixed A binary value.
*
* @return string The quoted binary value.
*/
public function quoteBinary($value)
{
return "'" . bin2hex($value) . "'";
}
/*##########################################################################
# Schema Statements
##########################################################################*/
/**
* Returns a hash of mappings from the abstract data types to the native
* database types.
*
* See TableDefinition::column() for details on the recognized abstract
* data types.
*
* @see TableDefinition::column()
*
* @return array A database type map.
*/
public function nativeDatabaseTypes()
{
return array(
'autoincrementKey' => array('name' => 'number NOT NULL PRIMARY KEY',
'limit' => null,
'null' => null),
'string' => array('name' => 'varchar2',
'limit' => 255),
'text' => array('name' => 'clob',
'limit' => null),
'mediumtext' => array('name' => 'clob',
'limit' => null),
'longtext' => array('name' => 'clob',
'limit' => null),
'integer' => array('name' => 'number',
'limit' => null),
'bigint' => array('name' => 'number',
'limit' => null),
'float' => array('name' => 'float',
'limit' => null),
'decimal' => array('name' => 'number',
'limit' => null),
'datetime' => array('name' => 'date',
'limit' => null),
'timestamp' => array('name' => 'date',
'limit' => null),
'time' => array('name' => 'varchar2',
'limit' => 8),
'date' => array('name' => 'date',
'limit' => null),
'binary' => array('name' => 'blob',
'limit' => null),
'boolean' => array('name' => 'number',
'precision' => 1,
'scale' => 0),
);
}
/**
* Returns the maximum length a table alias can have.
*
* @return integer The maximum table alias length.
*/
public function tableAliasLength()
{
return 30;
}
/**
* Converts a table name into a suitable table alias.
*
* @param string $tableName A table name.
*
* @return string A possible alias name for the table.
*/
public function tableAliasFor($tableName)
{
return parent::tableAliasFor($this->_truncate($tableName));
}
/**
* Returns a list of all tables of the current database.
*
* @return array A table list.
*/
public function tables()
{
return array_map(
array('Horde_String', 'lower'),
$this->selectValues('SELECT table_name FROM USER_TABLES')
);
}
/**
* Returns a table's primary key.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return Horde_Db_Adapter_Base_Index The primary key index object.
*/
public function primaryKey($tableName, $name = null)
{
$pk = $this->makeIndex(
$tableName,
'PRIMARY',
true,
true,
array()
);
$rows = @unserialize($this->cacheRead("tables/primarykeys/$tableName"));
if (!$rows) {
$constraint = $this->selectOne(
'SELECT CONSTRAINT_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME = ? AND CONSTRAINT_TYPE = \'P\'',
array(Horde_String::upper($tableName)),
$name
);
if ($constraint['constraint_name']) {
$pk->name = $constraint['constraint_name'];
$rows = $this->selectValues(
'SELECT DISTINCT COLUMN_NAME FROM USER_CONS_COLUMNS WHERE CONSTRAINT_NAME = ?',
array($constraint['constraint_name'])
);
$rows = array_map(array('Horde_String', 'lower'), $rows);
$this->cacheWrite("tables/primarykeys/$tableName", serialize($rows));
} else {
$rows = array();
}
}
$pk->columns = $rows;
return $pk;
}
/**
* Returns a list of tables indexes.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return array A list of Horde_Db_Adapter_Base_Index objects.
*/
public function indexes($tableName, $name = null)
{
$rows = @unserialize($this->cacheRead("tables/indexes/$tableName"));
if (!$rows) {
$rows = $this->selectAll(
'SELECT INDEX_NAME, UNIQUENESS FROM USER_INDEXES WHERE TABLE_NAME = ? AND INDEX_NAME NOT IN (SELECT INDEX_NAME FROM USER_LOBS)',
array(Horde_String::upper($tableName)),
$name
);
$this->cacheWrite("tables/indexes/$tableName", serialize($rows));
}
$indexes = array();
$primary = $this->primaryKey($tableName);
foreach ($rows as $row) {
if ($row['index_name'] == $primary->name) {
continue;
}
$columns = $this->selectValues(
'SELECT DISTINCT COLUMN_NAME FROM USER_IND_COLUMNS WHERE INDEX_NAME = ?',
array($row['index_name'])
);
$indexes[] = $this->makeIndex(
$tableName,
Horde_String::lower($row['index_name']),
false,
$row['uniqueness'] == 'UNIQUE',
array_map(array('Horde_String', 'lower'), $columns)
);
}
return $indexes;
}
/**
* Returns a list of table columns.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return array A list of Horde_Db_Adapter_Base_Column objects.
*/
public function columns($tableName, $name = null)
{
$rows = @unserialize($this->cacheRead("tables/columns/$tableName"));
if (!$rows) {
$rows = $this->selectAll(
'SELECT COLUMN_NAME, DATA_DEFAULT, DATA_TYPE, NULLABLE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE FROM USER_TAB_COLUMNS WHERE TABLE_NAME = ?',
array(Horde_String::upper($tableName)),
$name
);
$this->cacheWrite("tables/columns/$tableName", serialize($rows));
}
// Create columns from rows.
$columns = array();
foreach ($rows as $row) {
$column = Horde_String::lower($row['column_name']);
$columns[$column] = $this->makeColumn(
$column,
$row['data_default'],
$row['data_type'],
$row['nullable'] != 'N',
$row['data_length'],
$row['data_precision'],
$row['data_scale']
);
}
return $columns;
}
/**
* Renames a table.
*
* @param string $name A table name.
* @param string $newName The new table name.
*/
public function renameTable($name, $newName)
{
$this->_clearTableCache($name);
return $this->execute(
sprintf(
'ALTER TABLE %s RENAME TO %s',
$this->quoteTableName($name),
$this->quoteTableName($newName)
)
);
}
/**
* Drops a table from the database.
*
* @param string $name A table name.
*/
public function dropTable($name)
{
$this->removeAutoincrementTrigger($name);
parent::dropTable($name);
}
/**
* Adds a new column to a table.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param string $type A data type.
* @param array $options Column options. See
* Horde_Db_Adapter_Base_TableDefinition#column()
* for details.
*/
public function addColumn($tableName, $columnName, $type,
$options = array())
{
$this->_clearTableCache($tableName);
$options = array_merge(
array('limit' => null,
'precision' => null,
'scale' => null,
'unsigned' => null),
$options);
$sql = $this->quoteColumnName($columnName)
. ' '
. $this->typeToSql(
$type,
$options['limit'],
$options['precision'],
$options['scale'],
$options['unsigned']
);
$sql = $this->addColumnOptions($sql, $options);
$sql = sprintf(
'ALTER TABLE %s ADD (%s)',
$this->quoteTableName($tableName),
$sql
);
$this->execute($sql);
if ($type == 'autoincrementKey') {
$this->createAutoincrementTrigger($tableName, $columnName);
}
}
/**
* Removes a column from a table.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
*/
public function removeColumn($tableName, $columnName)
{
$this->_clearTableCache($tableName);
$sql = sprintf('ALTER TABLE %s DROP COLUMN %s',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName));
$this->removeAutoincrementTrigger($tableName, $columnName);
return $this->execute($sql);
}
/**
* Changes an existing column's definition.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param string $type A data type.
* @param array $options Column options. See
* Horde_Db_Adapter_Base_TableDefinition#column()
* for details.
*/
public function changeColumn($tableName, $columnName, $type, $options = array())
{
$options = array_merge(
array(
'limit' => null,
'precision' => null,
'scale' => null,
'unsigned' => null
),
$options
);
$column = $this->column($tableName, $columnName);
$isNull = $column->isNull();
if ($type == 'binary' && $column->getType() == 'binary') {
return;
}
$columnOptions = array(
'limit' => $column->getLimit(),
'default' => $column->getDefault(),
);
if (!$column->isNull()) {
$columnOptions['null'] = false;
}
$old = $this->addColumnOptions(
$this->typeToSql(
$column->getType(),
$column->getType() == 'integer' || is_null($options['limit'])
? null
: $column->getLimit(),
is_null($options['precision']) ? null : $column->precision(),
is_null($options['scale']) ? null : $column->scale(),
is_null($options['unsigned']) ? null : $column->isUnsigned()
),
$columnOptions
);
$new = $this->typeToSql(
$type,
$options['limit'],
$options['precision'],
$options['scale'],
$options['unsigned']
);
if ($old == $this->addColumnOptions($new, $options)) {
return;
}
if ($type == 'autoincrementKey') {
try {
$this->removeAutoincrementTrigger($tableName);
$this->removePrimaryKey($tableName);
} catch (Horde_Db_Exception $e) {
}
if (!$isNull) {
/* Manually set to NULL, because MODIFY fails if it contains a
* NOT NULL constraint and the column already is NOT NULL. */
$sql = $this->quoteColumnName($columnName)
. ' '
. $this->typeToSql(
$column->getType(),
$column->getType() == 'integer' ? null : $column->getLimit(),
$column->precision(),
$column->scale(),
$column->isUnsigned()
);
$sql = $this->addColumnOptions($sql, array('null' => true));
$sql = sprintf(
'ALTER TABLE %s MODIFY (%s)',
$this->quoteTableName($tableName),
$sql
);
$this->execute($sql);
}
} else {
/* Jump through some more hoops because MODIFY fails if it contains
* a NOT NULL constraint and the column already is NOT NULL. */
if (isset($options['null']) && $isNull == $options['null']) {
unset($options['null']);
} elseif (!isset($options['null']) && !$isNull) {
$options['null'] = true;
}
}
$this->_clearTableCache($tableName);
if ($type == 'binary' && $column->getType() != 'binary') {
$this->beginDbTransaction();
$this->addColumn($tableName, $columnName . '_tmp', $type, $options);
$this->execute('
CREATE OR REPLACE FUNCTION CLOB_TO_BLOB (p_clob CLOB) RETURN BLOB
AS
l_blob BLOB;
l_dest_offset INTEGER := 1;
l_source_offset INTEGER := 1;
l_lang_context INTEGER := DBMS_LOB.DEFAULT_LANG_CTX;
l_warning INTEGER := DBMS_LOB.WARN_INCONVERTIBLE_CHAR;
BEGIN
DBMS_LOB.CREATETEMPORARY(l_blob, TRUE);
DBMS_LOB.CONVERTTOBLOB
(
dest_lob => l_blob,
src_clob => p_clob,
amount => DBMS_LOB.LOBMAXSIZE,
dest_offset => l_dest_offset,
src_offset => l_source_offset,
blob_csid => DBMS_LOB.DEFAULT_CSID,
lang_context => l_lang_context,
warning => l_warning
);
RETURN l_blob;
END;
');
$this->update(sprintf(
'UPDATE %s SET %s = CLOB_TO_BLOB(%s) WHERE %s IS NOT NULL',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName . '_tmp'),
$this->quoteColumnName($columnName),
$this->quoteColumnName($columnName)
));
$this->update(sprintf(
'UPDATE %s SET %s = NULL WHERE %s IS NULL',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName . '_tmp'),
$this->quoteColumnName($columnName)
));
$this->removeColumn($tableName, $columnName);
$this->renameColumn($tableName, $columnName . '_tmp', $columnName);
$this->commitDbTransaction();
return;
}
if ($type != 'binary' && $column->getType() == 'binary') {
$this->beginDbTransaction();
$this->addColumn($tableName, $columnName . '_tmp', $type, $options);
$this->update(sprintf(
'UPDATE %s SET %s = UTL_RAW.CAST_TO_VARCHAR2(%s)',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName . '_tmp'),
$this->quoteColumnName($columnName)
));
$this->removeColumn($tableName, $columnName);
$this->renameColumn($tableName, $columnName . '_tmp', $columnName);
$this->commitDbTransaction();
return;
}
if ($type == 'text' && $column->getType() != 'text') {
$this->beginDbTransaction();
$this->addColumn($tableName, $columnName . '_tmp', $type, $options);
$this->update(sprintf(
'UPDATE %s SET %s = TO_CLOB(%s)',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName . '_tmp'),
$this->quoteColumnName($columnName)
));
$this->removeColumn($tableName, $columnName);
$this->renameColumn($tableName, $columnName . '_tmp', $columnName);
$this->commitDbTransaction();
return;
}
if ($type != 'text' && $column->getType() == 'text') {
$this->beginDbTransaction();
$this->addColumn($tableName, $columnName . '_tmp', $type, $options);
$this->update(sprintf(
'UPDATE %s SET %s = DBMS_LOB.SUBSTR(%s, 4000)',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName . '_tmp'),
$this->quoteColumnName($columnName)
));
$this->removeColumn($tableName, $columnName);
$this->renameColumn($tableName, $columnName . '_tmp', $columnName);
$this->commitDbTransaction();
return;
}
$sql = $this->quoteColumnName($columnName) . ' ' . $new;
$sql = $this->addColumnOptions($sql, $options);
$sql = sprintf(
'ALTER TABLE %s MODIFY (%s)',
$this->quoteTableName($tableName),
$sql
);
$this->execute($sql);
if ($type == 'autoincrementKey') {
$this->createAutoincrementTrigger($tableName, $columnName);
}
}
/**
* Creates sequences and triggers for an autoincrementKey column.
*
* @since Horde_Db 2.1.0
*
* @param string $tableName A table name.
* @param string $columnName A column name.
*/
public function createAutoincrementTrigger($tableName, $columnName)
{
// Build the table that holds the last autoincremented value. Used for
// example for returning the ID from last INSERT.
$id = $tableName . '_' . $columnName;
if (!$this->selectValue('SELECT 1 FROM USER_TABLES WHERE TABLE_NAME = \'HORDE_DB_AUTOINCREMENT\'')) {
$this->execute('CREATE TABLE horde_db_autoincrement (id INTEGER)');
$this->execute('INSERT INTO horde_db_autoincrement (id) VALUES (0)');
}
// Create a sequence that automatically increments when queried with
// .NEXTVAL.
$sequence = $this->_truncate($id . '_seq');
$sql = sprintf(
'CREATE SEQUENCE %s',
$sequence
);
// See if the column already has values, to start the sequence at a
// higher value.
$max = $this->selectValue(
sprintf(
'SELECT MAX(%s) FROM %s',
$this->quoteColumnName($columnName),
$tableName
)
);
if ($max) {
$sql .= ' MINVALUE ' . ($max + 1);
}
$this->execute($sql);
// Create the actual trigger that inserts the next value from the
// sequence into the autoincrementKey column when inserting a row.
$this->execute(sprintf(
'CREATE OR REPLACE TRIGGER %s BEFORE INSERT ON %s FOR EACH ROW DECLARE increment INTEGER; BEGIN SELECT %s.NEXTVAL INTO :NEW.%s FROM dual; SELECT %s.CURRVAL INTO increment FROM dual; UPDATE horde_db_autoincrement SET id = increment; END;',
$this->_truncate($id . '_trig'),
$tableName,
$sequence,
$columnName,
$sequence
));
}
/**
* Drops sequences and triggers for an autoincrementKey column.
*
* If $columnName is specified, the sequences and triggers are only dropped
* if $columnName is actually an autoincrementKey column.
*
* @since Horde_Db 2.1.0
*
* @param string $tableName A table name.
* @param string $columnName A column name.
*/
public function removeAutoincrementTrigger($tableName, $columnName = null)
{
$pk = $this->primaryKey($tableName);
if (count($pk->columns) == 1 &&
(!$columnName || $pk->columns[0] == $columnName)) {
$prefix = $tableName . '_' . $pk->columns[0];
try {
$this->execute(sprintf(
'DROP SEQUENCE %s',
$this->quoteColumnName($this->_truncate($prefix . '_seq'))
));
} catch (Horde_Db_Exception $e) {
}
try {
$this->execute(sprintf(
'DROP TRIGGER %s',
$this->quoteColumnName($this->_truncate($prefix . '_trig'))
));
} catch (Horde_Db_Exception $e) {
}
}
}
/**
* Sets a new default value for a column.
*
* If you want to set the default value to NULL, you are out of luck. You
* need to execute the apppropriate SQL statement yourself.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param mixed $default The new default value.
*/
public function changeColumnDefault($tableName, $columnName, $default)
{
$this->_clearTableCache($tableName);
$sql = sprintf('ALTER TABLE %s MODIFY (%s DEFAULT %s)',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName),
$this->quote($default));
return $this->execute($sql);
}
/**
* Renames a column.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param string $newColumnName The new column name.
*/
public function renameColumn($tableName, $columnName, $newColumnName)
{
$this->_clearTableCache($tableName);
$sql = sprintf('ALTER TABLE %s RENAME COLUMN %s TO %s',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName),
$this->quoteColumnName($newColumnName));
return $this->execute($sql);
}
/**
* Removes a primary key from a table.
*
* @param string $tableName A table name.
*
* @throws Horde_Db_Exception
*/
public function removePrimaryKey($tableName)
{
$this->_clearTableCache($tableName);
$sql = sprintf('ALTER TABLE %s DROP PRIMARY KEY',
$this->quoteTableName($tableName));
return $this->execute($sql);
}
/**
* Removes an index from a table.
*
* @param string $tableName A table name.
* @param string|array $options Either a column name or index options:
* - name: (string) the index name.
* - column: (string|array) column name(s).
*/
public function removeIndex($tableName, $options = array())
{
$this->_clearTableCache($tableName);
$index = $this->indexName($tableName, $options);
$sql = sprintf('DROP INDEX %s',
$this->quoteColumnName($index));
return $this->execute($sql);
}
/**
* Builds the name for an index.
*
* Cuts the index name to the maximum length of 30 characters limited by
* Oracle.
*
* @param string $tableName A table name.
* @param string|array $options Either a column name or index options:
* - column: (string|array) column name(s).
* - name: (string) the index name to fall
* back to if no column names specified.
*/
public function indexName($tableName, $options = array())
{
$index = parent::indexName($tableName, $options);
if (strlen($index) <= 30) {
return $index;
}
if (isset($options['name']) && $index == $options['name']) {
return $this->_truncate($index);
}
return substr('ind_' . $this->_truncate($tableName, 15) . '_' . hash('crc32', $index), 0, 30);
}
/**
* Creates a database.
*
* @param string $name A database name.
* @param array $options Database options.
*/
public function createDatabase($name, $options = array())
{
return $this->execute(sprintf('CREATE DATABASE %s', $this->quoteTableName($name)));
}
/**
* Drops a database.
*
* @param string $name A database name.
*/
public function dropDatabase($name)
{
if ($this->currentDatabase() != $name) {
throw new Horde_Db_Exception('Oracle can only drop the current database');
}
return $this->execute('DROP DATABASE');
}
/**
* Returns the name of the currently selected database.
*
* @return string The database name.
*/
public function currentDatabase()
{
return $this->selectValue("SELECT SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') FROM DUAL");
}
/**
* Adds default/null options to column SQL definitions.
*
* @param string $sql Existing SQL definition for a column.
* @param array $options Column options:
* - column: (Horde_Db_Adapter_Base_ColumnDefinition
* The column definition class.
* - null: (boolean) Whether to allow NULL values.
* - default: (mixed) Default column value.
* - autoincrement: (boolean) Whether the column is
* an autoincrement column. Driver depedendent.
*
* @return string The manipulated SQL definition.
*/
public function addColumnOptions($sql, $options)
{
/* 'autoincrement' is not handled here - it varies too much between
* DBs. Do autoincrement-specific handling in the driver. */
if (isset($options['default'])) {
$default = $options['default'];
$column = isset($options['column']) ? $options['column'] : null;
$sql .= ' DEFAULT ' . $this->quote($default, $column);
}
if (isset($options['null']) &&
(!isset($options['column']) ||
($options['column']->getType() != 'text' &&
$options['column']->getType() != 'binary'))) {
if ($options['null']) {
$sql .= ' NULL';
} else {
$sql .= ' NOT NULL';
}
}
return $sql;
}
/**
* Returns an expression using the specified operator.
*
* @param string $lhs The column or expression to test.
* @param string $op The operator.
* @param string $rhs The comparison value.
* @param boolean $bind If true, the method returns the query and a list
* of values suitable for binding as an array.
* @param array $params Any additional parameters for the operator.
*
* @return string|array The SQL test fragment, or an array containing the
* query and a list of values if $bind is true.
*/
public function buildClause($lhs, $op, $rhs, $bind = false,
$params = array())
{
$lhs = $this->_escapePrepare($lhs);
switch ($op) {
case '|':
if ($bind) {
return array($lhs . ' + ? - BITAND(' . $lhs . ', ?)',
array((int)$rhs, (int)$rhs));
}
return $lhs . ' + ' . (int)$rhs . ' - BITAND(' . $lhs . ', ' . (int)$rhs . ')';
case '&':
if ($bind) {
return array('BITAND(' . $lhs . ', ?)',
array((int)$rhs));
}
return 'BITAND(' . $lhs . ', ' . (int)$rhs . ')';
}
return parent::buildClause($lhs, $op, $rhs, $bind, $params);
}
/*##########################################################################
# Protected
##########################################################################*/
/**
* Clears the cache for tables when altering them.
*
* @param string $tableName A table name.
*/
protected function _clearTableCache($tableName)
{
parent::_clearTableCache($tableName);
$this->cacheWrite('tables/primarykeys/' . $tableName, '');
}
/**
* Truncates an indentifier to a certain length.
*
* To avoid collisions, the identifier is split up by underscores and the
* parts truncated to 3 characters first.
*
* @param string $name An identifier.
* @param integer $length The maximum length.
*
* @return string The truncated identifier.
*/
protected function _truncate($name, $length = 30)
{
if (strlen($name) > $length) {
$name = implode(
'_',
array_map(
function($t)
{
return substr($t, 0, 3);
},
explode('_', $name)
)
);
}
return substr($name, 0, $length);
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Oracle/TableDefinition.php 0000664 0001750 0001750 00000005120 13054774261 021770 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* @since Horde_Db 2.1.0
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Oracle_TableDefinition extends Horde_Db_Adapter_Base_TableDefinition
{
protected $_createTrigger = false;
/**
* Adds a new column to the table definition.
*
* @param string $type Column type, one of:
* autoincrementKey, string, text, integer, float,
* datetime, timestamp, time, date, binary, boolean.
* @param array $options Column options:
* - limit: (integer) Maximum column length (string,
* text, binary or integer columns only)
* - default: (mixed) The column's default value.
* You cannot explicitly set the default value to
* NULL. Simply leave off this option if you want
* a NULL default value.
* - null: (boolean) Whether NULL values are allowed
* in the column.
* - precision: (integer) The number precision
* (float columns only).
* - scale: (integer) The number scaling (float
* columns only).
* - unsigned: (boolean) Whether the column is an
* unsigned number (integer columns only).
* - autoincrement: (boolean) Whether the column is
* an autoincrement column. Restrictions are
* RDMS specific.
*
* @return Horde_Db_Adapter_Base_TableDefinition This object.
*/
public function column($name, $type, $options = array())
{
parent::column($name, $type, $options);
if ($type == 'autoincrementKey') {
$this->_createTrigger = $name;
}
return $this;
}
/**
* Wrap up table creation block & create the table
*/
public function end()
{
parent::end();
if ($this->_createTrigger) {
$this->_base->createAutoincrementTrigger($this->_name, $this->_createTrigger);
}
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Pdo/Base.php 0000664 0001750 0001750 00000043007 13054774261 017125 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
abstract class Horde_Db_Adapter_Pdo_Base extends Horde_Db_Adapter_Base
{
/*##########################################################################
# Connection Management
##########################################################################*/
/**
* Connect to the db
*/
public function connect()
{
if ($this->_active) {
return;
}
list($dsn, $user, $pass) = $this->_parseConfig();
try {
$pdo = @new PDO($dsn, $user, $pass);
} catch (PDOException $e) {
$msg = 'Could not instantiate PDO. PDOException: '
. $e->getMessage();
$this->_logError($msg, '');
$e2 = new Horde_Db_Exception($msg);
$e2->logged = true;
throw $e2;
}
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$this->_connection = $pdo;
$this->_active = true;
}
/**
* Check if the connection is active
*
* @return boolean
*/
public function isActive()
{
$this->_lastQuery = $sql = 'SELECT 1';
try {
return isset($this->_connection) &&
$this->_connection->query($sql);
} catch (PDOException $e) {
throw new Horde_Db_Exception($e);
}
}
/*##########################################################################
# Database Statements
##########################################################################*/
/**
* Returns an array of records with the column names as keys, and
* column values as values.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return Horde_Db_Adapter_Pdo_Result
* @throws Horde_Db_Exception
*/
public function select($sql, $arg1 = null, $arg2 = null)
{
return new Horde_Db_Adapter_Pdo_Result($this, $sql, $arg1, $arg2);
}
/**
* Returns an array of record hashes with the column names as keys and
* column values as values.
*
* @param string $sql
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
*/
public function selectAll($sql, $arg1=null, $arg2=null)
{
$stmt = $this->execute($sql, $arg1, $arg2);
if (!$stmt) {
return array();
}
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Required to really close the connection.
$stmt = null;
return $result;
}
/**
* Returns a record hash with the column names as keys and column values as
* values.
*
* @param string $sql A query.
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
*
* @return array|boolean A record hash or false if no record found.
*/
public function selectOne($sql, $arg1 = null, $arg2 = null)
{
$stmt = $this->execute($sql, $arg1, $arg2);
if (!$stmt) {
return array();
}
$result = $stmt->fetch(PDO::FETCH_ASSOC);
// Required to really close the connection.
$stmt = null;
return $result;
}
/**
* Returns a single value from a record
*
* @param string $sql
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
* @return string
*/
public function selectValue($sql, $arg1=null, $arg2=null)
{
$stmt = $this->execute($sql, $arg1, $arg2);
if (!$stmt) {
return null;
}
$result = $stmt->fetchColumn(0);
// Required to really close the connection.
$stmt = null;
return $result;
}
/**
* Returns an array of the values of the first column in a select:
* selectValues("SELECT id FROM companies LIMIT 3") => [1,2,3]
*
* @param string $sql
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
*/
public function selectValues($sql, $arg1=null, $arg2=null)
{
$stmt = $this->execute($sql, $arg1, $arg2);
if (!$stmt) {
return null;
}
$result = $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
// Required to really close the connection.
$stmt = null;
return $result;
}
/**
* Returns an array where the keys are the first column of a select, and the
* values are the second column:
*
* selectAssoc("SELECT id, name FROM companies LIMIT 3") => [1 => 'Ford', 2 => 'GM', 3 => 'Chrysler']
*
* @param string $sql
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
*/
public function selectAssoc($sql, $arg1=null, $arg2=null)
{
$stmt = $this->execute($sql, $arg1, $arg2);
if (!$stmt) {
return null;
}
$result = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
// Required to really close the connection.
$stmt = null;
return $result;
}
/**
* Executes the SQL statement in the context of this connection.
*
* @deprecated Deprecated for external usage. Use select() instead.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return PDOStatement
* @throws Horde_Db_Exception
*/
public function execute($sql, $arg1 = null, $arg2 = null)
{
if (is_array($arg1)) {
$query = $this->_replaceParameters($sql, $arg1);
$name = $arg2;
} else {
$name = $arg1;
$query = $sql;
$arg1 = array();
}
$t = new Horde_Support_Timer;
$t->push();
try {
$this->_lastQuery = $query;
$stmt = $this->_connection->query($query);
} catch (PDOException $e) {
$this->_logInfo($sql, $arg1, $name);
$this->_logError($query, 'QUERY FAILED: ' . $e->getMessage());
throw new Horde_Db_Exception($e);
}
$this->_logInfo($sql, $arg1, $name, $t->pop());
$this->_rowCount = $stmt ? $stmt->rowCount() : 0;
return $stmt;
}
/**
* Use a PDO prepared statement to execute a query. Used when passing
* values to insert/update as a stream resource.
*
* @param string $sql The SQL statement. Includes '?' placeholder
* for binding non-stream values. Stream values are bound using a
* placeholders named like ':binary0', ':binary1' etc...
*
* @param array $values An array of non-stream values.
* @param array $binary_values An array of stream resources.
*
* @throws Horde_Db_Exception
*/
protected function _executePrepared($sql, $values, $binary_values)
{
$query = $this->_replaceParameters($sql, $values);
try {
$stmt = $this->_connection->prepare($query);
foreach ($binary_values as $key => $bvalue) {
rewind($bvalue);
$stmt->bindParam(':binary' . $key, $bvalue, PDO::PARAM_LOB);
}
} catch (PDOException $e) {
$this->_logInfo($sql, $values, null);
$this->_logError($sql, 'QUERY FAILED: ' . $e->getMessage());
throw new Horde_Db_Exception($e);
}
$t = new Horde_Support_Timer;
$t->push();
try {
$this->_lastQuery = $sql;
$stmt->execute();
} catch (PDOException $e) {
$this->_logInfo($sql, $values, null);
$this->_logError($sql, 'QUERY FAILED: ' . $e->getMessage());
throw new Horde_Db_Exception($e);
}
$t = new Horde_Support_Timer;
$t->push();
$this->_logInfo($sql, $values, null, $t->pop());
$this->_rowCount = $stmt->rowCount();
}
/**
* Inserts a row including BLOBs into a table.
*
* @since Horde_Db 2.4.0
*
* @param string $table The table name.
* @param array $fields A hash of column names and values. BLOB/CLOB
* columns must be provided as Horde_Db_Value
* objects.
* @param string $pk The primary key column.
* @param integer $idValue The primary key value. This parameter is
* required if the primary key is inserted
* manually.
*
* @return integer Last inserted ID.
* @throws Horde_Db_Exception
*/
public function insertBlob($table, $fields, $pk = null, $idValue = null)
{
$placeholders = $values = $binary = array();
$binary_cnt = 0;
foreach ($fields as $name => $value) {
if ($value instanceof Horde_Db_Value_Binary) {
$placeholders[] = ':binary' . $binary_cnt++;
$binary[] = $value->stream;
} else {
$placeholders[] = '?';
$values[] = $value;
}
}
$query = sprintf(
'INSERT INTO %s (%s) VALUES (%s)',
$this->quoteTableName($table),
implode(', ', array_map(array($this, 'quoteColumnName'), array_keys($fields))),
implode(', ', $placeholders)
);
if ($binary_cnt > 0) {
$this->_executePrepared($query, $values, $binary);
try {
return $idValue
? $idValue
: $this->_connection->lastInsertId(null);
} catch (PDOException $e) {
throw new Horde_Db_Exception($e);
}
}
return $this->insert($query, $fields, null, $pk, $idValue);
}
/**
* Updates rows including BLOBs into a table.
*
* @since Horde_Db 2.4.0
*
* @param string $table The table name.
* @param array $fields A hash of column names and values. BLOB/CLOB
* columns must be provided as
* Horde_Db_Value objects.
* @param string|array $where A WHERE clause. Either a complete clause or
* an array containing a clause with
* placeholders and a list of values.
*
* @throws Horde_Db_Exception
*/
public function updateBlob($table, $fields, $where = null)
{
if (is_array($where)) {
$where = $this->_replaceParameters($where[0], $where[1]);
}
$values = $binary_values = $fnames = array();
$binary_cnt = 0;
foreach ($fields as $field => $value) {
if ($value instanceof Horde_Db_Value) {
$fnames[] = $this->quoteColumnName($field) . ' = :binary' . $binary_cnt++;
$binary_values[] = $value->stream;
} else {
$fnames[] = $this->quoteColumnName($field) . ' = ?';
$values[] = $value;
}
}
$query = sprintf(
'UPDATE %s SET %s%s',
$this->quoteTableName($table),
implode(', ', $fnames),
strlen($where) ? ' WHERE ' . $where : ''
);
if ($binary_cnt > 0) {
$this->_executePrepared($query, $values, $binary_values);
return $this->_rowCount;
}
return $this->update($query, $fields);
}
/**
* Inserts a row into a table.
*
* @param string $sql SQL statement.
* @param array|string $arg1 Either an array of bound parameters or a
* query name.
* @param string $arg2 If $arg1 contains bound parameters, the
* query name.
* @param string $pk The primary key column.
* @param integer $idValue The primary key value. This parameter is
* required if the primary key is inserted
* manually.
* @param string $sequenceName The sequence name.
*
* @return integer Last inserted ID.
* @throws Horde_Db_Exception
*/
public function insert($sql, $arg1 = null, $arg2 = null, $pk = null,
$idValue = null, $sequenceName = null)
{
$this->execute($sql, $arg1, $arg2);
try {
return $idValue
? $idValue
: $this->_connection->lastInsertId($sequenceName);
} catch (PDOException $e) {
throw new Horde_Db_Exception($e);
}
}
/**
* Begins the transaction (and turns off auto-committing).
*/
public function beginDbTransaction()
{
if (!$this->_transactionStarted) {
try {
$this->_connection->beginTransaction();
} catch (PDOException $e) {
throw new Horde_Db_Exception($e);
}
}
$this->_transactionStarted++;
}
/**
* Commits the transaction (and turns on auto-committing).
*/
public function commitDbTransaction()
{
$this->_transactionStarted--;
if (!$this->_transactionStarted) {
try {
$this->_connection->commit();
} catch (PDOException $e) {
throw new Horde_Db_Exception($e);
}
}
}
/**
* Rolls back the transaction (and turns on auto-committing). Must be
* done if the transaction block raises an exception or returns false.
*/
public function rollbackDbTransaction()
{
if (!$this->_transactionStarted) {
return;
}
try {
$this->_connection->rollBack();
} catch (PDOException $e) {
throw new Horde_Db_Exception($e);
}
$this->_transactionStarted = 0;
}
/*##########################################################################
# Quoting
##########################################################################*/
/**
* Quotes a string, escaping any ' (single quote) and \ (backslash)
* characters..
*
* @param string $string
* @return string
*/
public function quoteString($string)
{
try {
return $this->_connection->quote($string);
} catch (PDOException $e) {
throw new Horde_Db_Exception($e);
}
}
/*##########################################################################
# Protected
##########################################################################*/
protected function _normalizeConfig($params)
{
// Normalize config parameters to what PDO expects.
$normalize = array('database' => 'dbname',
'hostspec' => 'host');
foreach ($normalize as $from => $to) {
if (isset($params[$from])) {
$params[$to] = $params[$from];
unset($params[$from]);
}
}
return $params;
}
protected function _buildDsnString($params)
{
$dsn = $this->_config['adapter'] . ':';
foreach ($params as $k => $v) {
if (strlen($v)) {
$dsn .= "$k=$v;";
}
}
return rtrim($dsn, ';');
}
/**
* Parse configuration array into options for PDO constructor.
*
* @throws Horde_Db_Exception
* @return array [dsn, username, password]
*/
protected function _parseConfig()
{
$this->_checkRequiredConfig(array('adapter', 'username'));
// try an empty password if it's not set.
if (!isset($this->_config['password'])) {
$this->_config['password'] = '';
}
// collect options to build PDO Data Source Name (DSN) string
$dsnOpts = $this->_config;
unset(
$dsnOpts['adapter'],
$dsnOpts['username'],
$dsnOpts['password'],
$dsnOpts['protocol'],
$dsnOpts['persistent'],
$dsnOpts['charset'],
$dsnOpts['phptype'],
$dsnOpts['socket']
);
// return DSN and user/pass for connection
return array(
$this->_buildDsnString($this->_normalizeConfig($dsnOpts)),
$this->_config['username'],
$this->_config['password']);
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Pdo/Mysql.php 0000664 0001750 0001750 00000007435 13054774261 017365 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* PDO_MySQL Horde_Db_Adapter
*
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Pdo_Mysql extends Horde_Db_Adapter_Pdo_Base
{
/**
* @var string
*/
protected $_schemaClass = 'Horde_Db_Adapter_Mysql_Schema';
/**
* @return string
*/
public function adapterName()
{
return 'PDO_MySQL';
}
/**
* @return boolean
*/
public function supportsMigrations()
{
return true;
}
/*##########################################################################
# Connection Management
##########################################################################*/
/**
* Connect to the db
*/
public function connect()
{
if ($this->_active) {
return;
}
parent::connect();
// ? $this->_connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
// Set the default charset. http://dev.mysql.com/doc/refman/5.1/en/charset-connection.html
if (!empty($this->_config['charset'])) {
$this->setCharset($this->_config['charset']);
}
}
/*##########################################################################
# Protected
##########################################################################*/
/**
* Parse configuration array into options for PDO constructor.
*
* http://pecl.php.net/bugs/7234
* Setting a bogus socket does not appear to work.
*
* @throws Horde_Db_Exception
* @return array [dsn, username, password]
*/
protected function _parseConfig()
{
$this->_config['adapter'] = 'mysql';
$this->_checkRequiredConfig(array('adapter', 'username'));
if (!empty($this->_config['socket'])) {
$this->_config['unix_socket'] = $this->_config['socket'];
unset($this->_config['socket']);
}
if (!empty($this->_config['host']) &&
$this->_config['host'] == 'localhost') {
$this->_config['host'] = '127.0.0.1';
}
// Try an empty password if it's not set.
if (!isset($this->_config['password'])) {
$this->_config['password'] = '';
}
// Collect options to build PDO Data Source Name (DSN) string.
$dsnOpts = $this->_config;
unset($dsnOpts['adapter'],
$dsnOpts['username'],
$dsnOpts['password'],
$dsnOpts['charset'],
$dsnOpts['phptype']);
$dsnOpts = $this->_normalizeConfig($dsnOpts);
if (isset($dsnOpts['port'])) {
if (empty($dsnOpts['host'])) {
throw new Horde_Db_Exception('Host is required if port is specified');
}
}
if (isset($dsnOpts['unix_socket'])) {
if (!empty($dsnOpts['host']) ||
!empty($dsnOpts['port'])) {
throw new Horde_Db_Exception('Host and port must not be set if using a UNIX socket');
}
}
// Return DSN and user/pass for connection.
return array(
$this->_buildDsnString($dsnOpts),
$this->_config['username'],
$this->_config['password']);
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Pdo/Pgsql.php 0000664 0001750 0001750 00000021705 13054774261 017342 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* PDO_PostgreSQL Horde_Db_Adapter
*
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Pdo_Pgsql extends Horde_Db_Adapter_Pdo_Base
{
/**
* @var string
*/
protected $_schemaClass = 'Horde_Db_Adapter_Postgresql_Schema';
/**
* @return string
*/
public function adapterName()
{
return 'PDO_PostgreSQL';
}
/**
* @return boolean
*/
public function supportsMigrations()
{
return true;
}
/**
* Does PostgreSQL support standard conforming strings?
* @return boolean
*/
public function supportsStandardConformingStrings()
{
// Temporarily set the client message level above error to prevent unintentional
// error messages in the logs when working on a PostgreSQL database server that
// does not support standard conforming strings.
$clientMinMessagesOld = $this->getClientMinMessages();
$this->setClientMinMessages('panic');
$hasSupport = $this->selectValue('SHOW standard_conforming_strings');
$this->setClientMinMessages($clientMinMessageOld);
return $hasSupport;
}
public function supportsInsertWithReturning()
{
return true;
}
/*##########################################################################
# Connection Management
##########################################################################*/
/**
* Connect to the db.
*
* @throws Horde_Db_Exception
*/
public function connect()
{
if ($this->_active) {
return;
}
parent::connect();
$this->_lastQuery = $sql = "SET datestyle TO 'iso'";
$retval = $this->_connection->exec($sql);
if ($retval === false) {
$error = $this->_connection->errorInfo();
throw new Horde_Db_Exception($error[2]);
}
$this->_configureConnection();
}
/*##########################################################################
# Database Statements
##########################################################################*/
/**
* Inserts a row into a table.
*
* @param string $sql SQL statement.
* @param array|string $arg1 Either an array of bound parameters or a
* query name.
* @param string $arg2 If $arg1 contains bound parameters, the
* query name.
* @param string $pk The primary key column.
* @param integer $idValue The primary key value. This parameter is
* required if the primary key is inserted
* manually.
* @param string $sequenceName The sequence name.
*
* @return integer Last inserted ID.
* @throws Horde_Db_Exception
*/
public function insert($sql, $arg1 = null, $arg2 = null, $pk = null,
$idValue = null, $sequenceName = null)
{
// Extract the table from the insert sql. Yuck.
$temp = explode(' ', trim($sql), 4);
$table = str_replace('"', '', $temp[2]);
// Try an insert with 'returning id'
if (!$pk) {
list($pk, $sequenceName) = $this->pkAndSequenceFor($table);
}
if ($pk) {
$id = $this->selectValue($sql . ' RETURNING ' . $this->quoteColumnName($pk), $arg1, $arg2);
$this->resetPkSequence($table, $pk, $sequenceName);
return $id;
}
// If neither pk nor sequence name is given, look them up.
if (!($pk || $sequenceName)) {
list($pk, $sequenceName) = $this->pkAndSequenceFor($table);
}
// Otherwise, insert then grab last_insert_id.
$this->execute($sql, $arg1, $arg2);
if ($idValue) {
return $idValue;
}
// If a pk is given, fallback to default sequence name.
// Don't fetch last insert id for a table without a pk.
if ($pk &&
($sequenceName ||
$sequenceName = $this->defaultSequenceName($table, $pk))) {
$this->resetPkSequence($table, $pk, $sequenceName);
return $this->_lastInsertId($table, $sequenceName);
}
}
/**
* Appends LIMIT and OFFSET options to a SQL statement.
*
* @param string $sql SQL statement.
* @param array $options Hash with 'limit' and (optional) 'offset' values.
*
* @return string
*/
public function addLimitOffset($sql, $options)
{
if (isset($options['limit']) && $limit = $options['limit']) {
$sql .= " LIMIT $limit";
}
if (isset($options['offset']) && $offset = $options['offset']) {
$sql .= " OFFSET $offset";
}
return $sql;
}
/*##########################################################################
# Protected
##########################################################################*/
/**
* Parse configuration array into options for PDO constructor.
*
* @throws Horde_Db_Exception
* @return array [dsn, username, password]
*/
protected function _parseConfig()
{
$this->_config['adapter'] = 'pgsql';
if (!empty($this->_config['socket']) && !empty($this->_config['host'])) {
throw new Horde_Db_Exception('Can only specify host or socket, not both');
}
// PDO for PostgreSQL does not accept a socket argument
// in the connection string; the location can be set via the
// "host" argument instead.
if (!empty($this->_config['socket'])) {
$this->_config['host'] = $this->_config['socket'];
unset($this->_config['socket']);
}
return parent::_parseConfig();
}
/**
* Configures the encoding, verbosity, and schema search path of the connection.
* This is called by connect() and should not be called manually.
*/
protected function _configureConnection()
{
if (!empty($this->_config['charset'])) {
$this->_lastQuery = $sql = 'SET client_encoding TO '.$this->quoteString($this->_config['charset']);
$this->execute($sql);
}
if (!empty($this->_config['client_min_messages'])) $this->setClientMinMessages($this->_config['client_min_messages']);
$this->setSchemaSearchPath(!empty($this->_config['schema_search_path']) || !empty($this->_config['schema_order']));
}
/**
* @TODO
*/
protected function _selectRaw($sql, $arg1=null, $arg2=null)
{
$result = $this->execute($sql, $arg1, $arg2);
if (!$result) return array();
$moneyFields = array();
for ($i = 0, $i_max = $result->columnCount(); $i < $i_max; $i++) {
$f = $result->getColumnMeta($i);
if (!empty($f['pgsql:oid']) && $f['pgsql:oid'] == Horde_Db_Adapter_Postgresql_Column::MONEY_COLUMN_TYPE_OID) {
$moneyFields[] = $i;
$moneyFields[] = $f['name'];
}
}
foreach ($result as $row) {
// If this is a money type column and there are any currency
// symbols, then strip them off. Indeed it would be prettier to do
// this in Horde_Db_Adapter_Postgres_Column::stringToDecimal but
// would break form input fields that call valueBeforeTypeCast.
foreach ($moneyFields as $f) {
// Because money output is formatted according to the locale, there are two
// cases to consider (note the decimal separators):
// (1) $12,345,678.12
// (2) $12.345.678,12
if (preg_match('/^-?\D+[\d,]+\.\d{2}$/', $row[$f])) { // #1
$row[$f] = preg_replace('/[^-\d\.]/', '', $row[$f]) . "\n";
} elseif (preg_match('/^-?\D+[\d\.]+,\d{2}$/', $row[$f])) { // #2
$row[$f] = str_replace(',', '.', preg_replace('/[^-\d,]/', '', $row[$f])) . "\n";
}
}
$rows[] = $row;
}
$result->closeCursor();
return $rows;
}
/**
* Returns the current ID of a table's sequence.
*
* @todo Remove unused $table parameter.
*/
protected function _lastInsertId($table, $sequenceName)
{
return (int)$this->selectValue('SELECT currval('.$this->quoteSequenceName($sequenceName).')');
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Pdo/Result.php 0000664 0001750 0001750 00000003076 13054774261 017533 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* This class represents the result set of a SELECT query from the PDO drivers.
*
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Pdo_Result extends Horde_Db_Adapter_Base_Result
{
/**
* Maps Horde_Db fetch mode constant to the extension constants.
*
* @var array
*/
protected $_map = array(
Horde_Db::FETCH_ASSOC => PDO::FETCH_ASSOC,
Horde_Db::FETCH_NUM => PDO::FETCH_NUM,
Horde_Db::FETCH_BOTH => PDO::FETCH_BOTH
);
/**
* Returns a row from a resultset.
*
* @return array|boolean The next row in the resultset or false if there
* are no more results.
*/
protected function _fetchArray()
{
try {
return $this->_result->fetch($this->_map[$this->_fetchMode]);
} catch (PDOException $e) {
throw new Horde_Db_Exception($e);
}
}
/**
* Returns the number of columns in the result set.
*
* @return integer Number of columns.
*/
protected function _columnCount()
{
try {
return $this->_result->columnCount();
} catch (PDOException $e) {
throw new Horde_Db_Exception($e);
}
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Pdo/Sqlite.php 0000664 0001750 0001750 00000014006 13054774261 017511 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* PDO_SQLite Horde_Db_Adapter
*
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Pdo_Sqlite extends Horde_Db_Adapter_Pdo_Base
{
/**
* @var string
*/
protected $_schemaClass = 'Horde_Db_Adapter_Sqlite_Schema';
/**
* SQLite version number
* @var integer
*/
protected $_sqliteVersion;
/**
* @return string
*/
public function adapterName()
{
return 'PDO_SQLite';
}
/**
* @return boolean
*/
public function supportsMigrations()
{
return true;
}
/**
* Does this adapter support using DISTINCT within COUNT? This is +true+
* for all adapters except sqlite.
*
* @return boolean
*/
public function supportsCountDistinct()
{
return $this->_sqliteVersion >= '3.2.6';
}
/**
* Does this adapter support using INTERVAL statements? This is +true+
* for all adapters except sqlite.
*
* @return boolean
*/
public function supportsInterval()
{
return false;
}
public function supportsAutoIncrement()
{
return $this->_sqliteVersion >= '3.1.0';
}
/*##########################################################################
# Connection Management
##########################################################################*/
/**
* Connect to the db.
*
* @throws Horde_Db_Exception
*/
public function connect()
{
if ($this->_active) {
return;
}
parent::connect();
$this->_connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$this->_lastQuery = $sql = 'PRAGMA full_column_names=0';
$retval = $this->_connection->exec($sql);
if ($retval === false) {
$error = $this->_connection->errorInfo();
throw new Horde_Db_Exception($error[2]);
}
$this->_lastQuery = $sql = 'PRAGMA short_column_names=1';
$retval = $this->_connection->exec($sql);
if ($retval === false) {
$error = $this->_connection->errorInfo();
throw new Horde_Db_Exception($error[2]);
}
$this->_lastQuery = $sql = 'SELECT sqlite_version(*)';
$this->_sqliteVersion = $this->selectValue($sql);
}
/*##########################################################################
# Database Statements
##########################################################################*/
/**
* Executes the SQL statement in the context of this connection.
*
* @param string $sql
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
*/
public function execute($sql, $arg1=null, $arg2=null)
{
return $this->_catchSchemaChanges('execute', array($sql, $arg1, $arg2));
}
/**
* Begins the transaction (and turns off auto-committing).
*/
public function beginDbTransaction()
{
return $this->_catchSchemaChanges('beginDbTransaction');
}
/**
* Commits the transaction (and turns on auto-committing).
*/
public function commitDbTransaction()
{
return $this->_catchSchemaChanges('commitDbTransaction');
}
/**
* Rolls back the transaction (and turns on auto-committing). Must be
* done if the transaction block raises an exception or returns false.
*/
public function rollbackDbTransaction()
{
return $this->_catchSchemaChanges('rollbackDbTransaction');
}
/**
* SELECT ... FOR UPDATE is redundant since the table is locked.
*/
public function addLock(&$sql, array $options = array())
{
}
public function emptyInsertStatement($tableName)
{
return 'INSERT INTO '.$this->quoteTableName($tableName).' VALUES(NULL)';
}
/*##########################################################################
# Protected
##########################################################################*/
protected function _catchSchemaChanges($method, $args = array())
{
try {
return call_user_func_array(array($this, "parent::$method"), $args);
} catch (Exception $e) {
if (preg_match('/database schema has changed/i', $e->getMessage())) {
$this->reconnect();
return call_user_func_array(array($this, "parent::$method"), $args);
} else {
throw $e;
}
}
}
protected function _buildDsnString($params)
{
return 'sqlite:' . $params['dbname'];
}
/**
* Parse configuration array into options for PDO constructor
*
* @throws Horde_Db_Exception
* @return array [dsn, username, password]
*/
protected function _parseConfig()
{
// check required config keys are present
if (empty($this->_config['database']) && empty($this->_config['dbname'])) {
$msg = 'Either dbname or database is required';
throw new Horde_Db_Exception($msg);
}
// collect options to build PDO Data Source Name (DSN) string
$dsnOpts = $this->_config;
unset($dsnOpts['adapter'], $dsnOpts['username'], $dsnOpts['password']);
// return DSN and dummy user/pass for connection
return array($this->_buildDsnString($this->_normalizeConfig($dsnOpts)), '', '');
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Postgresql/Column.php 0000664 0001750 0001750 00000017115 13054774261 021132 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Postgresql_Column extends Horde_Db_Adapter_Base_Column
{
/*##########################################################################
# Constants
##########################################################################*/
/**
* The internal PostgreSQL identifier of the money data type.
* @const integer
*/
const MONEY_COLUMN_TYPE_OID = 790;
/**
* @var integer
*/
public static $moneyPrecision = 19;
/**
* Construct
* @param string $name
* @param string $default
* @param string $sqlType
* @param boolean $null
*/
public function __construct($name, $default, $sqlType=null, $null=true)
{
parent::__construct($name, $this->_extractValueFromDefault($default), $sqlType, $null);
}
/**
*/
protected function _setSimplifiedType()
{
switch (true) {
case preg_match('/^(?:real|double precision)$/', $this->_sqlType):
// Numeric and monetary types
$this->_type = 'float';
return;
case preg_match('/^money$/', $this->_sqlType):
// Monetary types
$this->_type = 'decimal';
return;
case preg_match('/^(?:character varying|bpchar)(?:\(\d+\))?$/', $this->_sqlType):
// Character types
$this->_type = 'string';
return;
case preg_match('/^bytea$/', $this->_sqlType):
// Binary data types
$this->_type = 'binary';
return;
case preg_match('/^timestamp with(?:out)? time zone$/', $this->_sqlType):
// Date/time types
$this->_type = 'datetime';
return;
case preg_match('/^interval$/', $this->_sqlType):
$this->_type = 'string';
return;
case preg_match('/^(?:point|line|lseg|box|"?path"?|polygon|circle)$/', $this->_sqlType):
// Geometric types
$this->_type = 'string';
return;
case preg_match('/^(?:cidr|inet|macaddr)$/', $this->_sqlType):
// Network address types
$this->_type = 'string';
return;
case preg_match('/^bit(?: varying)?(?:\(\d+\))?$/', $this->_sqlType):
// Bit strings
$this->_type = 'string';
return;
case preg_match('/^xml$/', $this->_sqlType):
// XML type
$this->_type = 'string';
return;
case preg_match('/^\D+\[\]$/', $this->_sqlType):
// Arrays
$this->_type = 'string';
return;
case preg_match('/^oid$/', $this->_sqlType):
// Object identifier types
$this->_type = 'integer';
return;
}
// Pass through all types that are not specific to PostgreSQL.
parent::_setSimplifiedType();
}
/**
* Extracts the value from a PostgreSQL column default definition.
*/
protected function _extractValueFromDefault($default)
{
switch (true) {
case preg_match('/\A-?\d+(\.\d*)?\z/', $default):
// Numeric types
return $default;
case preg_match('/\A\'(.*)\'::(?:character varying|bpchar|text)\z/m', $default, $matches):
// Character types
return $matches[1];
case preg_match('/\AE\'(.*)\'::(?:character varying|bpchar|text)\z/m', $default, $matches):
// Character types (8.1 formatting)
/*@TODO fix preg callback*/
return preg_replace('/\\(\d\d\d)/', '$1.oct.chr', $matches[1]);
case preg_match('/\A\'(.*)\'::bytea\z/m', $default, $matches):
// Binary data types
return $matches[1];
case preg_match('/\A\'(.+)\'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/', $default, $matches):
// Date/time types
return $matches[1];
case preg_match('/\A\'(.*)\'::interval\z/', $default, $matches):
return $matches[1];
case $default == 'true':
// Boolean type
return true;
case $default == 'false':
return false;
case preg_match('/\A\'(.*)\'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/', $default, $matches):
// Geometric types
return $matches[1];
case preg_match('/\A\'(.*)\'::(?:cidr|inet|macaddr)\z/', $default, $matches):
// Network address types
return $matches[1];
case preg_match('/\AB\'(.*)\'::"?bit(?: varying)?"?\z/', $default, $matches):
// Bit string types
return $matches[1];
case preg_match('/\A\'(.*)\'::xml\z/m', $default, $matches):
// XML type
return $matches[1];
case preg_match('/\A\'(.*)\'::"?\D+"?\[\]\z/', $default, $matches):
// Arrays
return $matches[1];
case preg_match('/\A-?\d+\z/', $default, $matches):
// Object identifier types
return $matches[1];
default:
// Anything else is blank, some user type, or some function
// and we can't know the value of that, so return nil.
return null;
}
}
/**
* Used to convert from BLOBs (BYTEAs) to Strings.
*
* @return string
*/
public function binaryToString($value)
{
if (is_resource($value)) {
rewind($value);
$string = stream_get_contents($value);
fclose($value);
return $string;
}
return preg_replace_callback("/(?:\\\'|\\\\\\\\|\\\\\d{3})/", array($this, 'binaryToStringCallback'), $value);
}
/**
* Callback function for binaryToString().
*/
public function binaryToStringCallback($matches)
{
if ($matches[0] == '\\\'') {
return "'";
} elseif ($matches[0] == '\\\\\\\\') {
return '\\';
}
return chr(octdec(substr($matches[0], -3)));
}
/*##########################################################################
# Protected
##########################################################################*/
/**
* @param string $sqlType
* @return int
*/
protected function _extractLimit($sqlType)
{
if (preg_match('/^bigint/i', $sqlType)) {
return 8;
}
if (preg_match('/^smallint/i', $sqlType)) {
return 2;
}
return parent::_extractLimit($sqlType);
}
/**
* @param string $sqlType
* @return int
*/
protected function _extractPrecision($sqlType)
{
if (preg_match('/^money/', $sqlType)) {
return self::$moneyPrecision;
}
return parent::_extractPrecision($sqlType);
}
/**
* @param string $sqlType
* @return int
*/
protected function _extractScale($sqlType)
{
if (preg_match('/^money/', $sqlType)) {
return 2;
}
return parent::_extractScale($sqlType);
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Postgresql/Schema.php 0000664 0001750 0001750 00000122621 13054774261 021074 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* Class for PostgreSQL-specific managing of database schemes and handling of
* SQL dialects and quoting.
*
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Postgresql_Schema extends Horde_Db_Adapter_Base_Schema
{
/**
* The active schema search path.
*
* @var string
*/
protected $_schemaSearchPath = '';
/**
* Cached version.
*
* @var integer
*/
protected $_version;
/*##########################################################################
# Object factories
##########################################################################*/
/**
* Factory for Column objects.
*
* @param string $name The column's name, such as "supplier_id" in
* "supplier_id int(11)".
* @param string $default The type-casted default value, such as "new" in
* "sales_stage varchar(20) default 'new'".
* @param string $sqlType Used to extract the column's type, length and
* signed status, if necessary. For example
* "varchar" and "60" in "company_name varchar(60)"
* or "unsigned => true" in "int(10) UNSIGNED".
* @param boolean $null Whether this column allows NULL values.
*
* @return Horde_Db_Adapter_Postgresql_Column A column object.
*/
public function makeColumn($name, $default, $sqlType = null, $null = true)
{
return new Horde_Db_Adapter_Postgresql_Column($name, $default, $sqlType, $null);
}
/*##########################################################################
# Quoting
##########################################################################*/
/**
* Quotes the column value to help prevent SQL injection attacks.
*
* This method makes educated guesses on the scalar type based on the
* passed value. Make sure to correctly cast the value and/or pass the
* $column parameter to get the best results.
*
* @param mixed $value The scalar value to quote, a Horde_Db_Value,
* Horde_Date, or DateTime instance, or an object
* implementing quotedId().
* @param object $column An object implementing getType().
*
* @return string The correctly quoted value.
*/
public function quote($value, $column = null)
{
if (!$column) {
return parent::quote($value, $column);
}
if (is_string($value) &&
$column->getType() == 'binary') {
return $this->quoteBinary($value);
}
if (is_string($value) && $column->getSqlType() == 'xml') {
return "xml '" . $this->quoteString($value) . "'";
}
if (is_numeric($value) && $column->getSqlType() == 'money') {
// Not truly string input, so doesn't require (or allow) escape
// string syntax.
return "'" . $value . "'";
}
if (is_string($value) && substr($column->getSqlType(), 0, 3) == 'bit') {
if (preg_match('/^[0-9A-F]*$/i')) {
// Hexadecimal notation
return "X'" . $value . "'";
}
if (preg_match('/^[01]*$/', $value)) {
// Bit-string notation
return "B'" . $value . "'";
}
}
return parent::quote($value, $column);
}
/**
* Returns a quoted sequence name.
*
* PostgreSQL specific method.
*
* @param string $name A sequence name.
*
* @return string The quoted sequence name.
*/
public function quoteSequenceName($name)
{
return '\'' . str_replace('"', '""', $name) . '\'';
}
/**
* Returns a quoted boolean true.
*
* @return string The quoted boolean true.
*/
public function quoteTrue()
{
return "'t'";
}
/**
* Returns a quoted boolean false.
*
* @return string The quoted boolean false.
*/
public function quoteFalse()
{
return "'f'";
}
/**
* Returns a quoted binary value.
*
* @param mixed A binary value.
*
* @return string The quoted binary value.
*/
public function quoteBinary($value)
{
if ($this->postgresqlVersion() >= 90000) {
return "E'\\\\x" . bin2hex($value) . "'";
}
/* MUST escape zero octet(0), single quote (39), and backslash (92).
* MAY escape non-printable octets, but they are required in some
* instances so it is best to escape all. */
return "E'" . preg_replace_callback("/[\\x00-\\x1f\\x27\\x5c\\x7f-\\xff]/", array($this, '_quoteBinaryCallback'), $value) . "'";
}
/**
* Callback function for quoteBinary().
*
* @param array $matches Matches from preg_replace().
*
* @return string Escaped/encoded binary value.
*/
protected function _quoteBinaryCallback($matches)
{
return sprintf('\\\\%03.o', ord($matches[0]));
}
/*##########################################################################
# Schema Statements
##########################################################################*/
/**
* Returns a hash of mappings from the abstract data types to the native
* database types.
*
* See TableDefinition::column() for details on the recognized abstract
* data types.
*
* @see TableDefinition::column()
*
* @return array A database type map.
*/
public function nativeDatabaseTypes()
{
return array(
'autoincrementKey' => 'serial primary key',
'string' => array('name' => 'character varying',
'limit' => 255),
'text' => array('name' => 'text',
'limit' => null),
'mediumtext' => array('name' => 'text',
'limit' => null),
'longtext' => array('name' => 'text',
'limit' => null),
'integer' => array('name' => 'integer',
'limit' => null),
'float' => array('name' => 'float',
'limit' => null),
'decimal' => array('name' => 'decimal',
'limit' => null),
'datetime' => array('name' => 'timestamp',
'limit' => null),
'timestamp' => array('name' => 'timestamp',
'limit' => null),
'time' => array('name' => 'time',
'limit' => null),
'date' => array('name' => 'date',
'limit' => null),
'binary' => array('name' => 'bytea',
'limit' => null),
'boolean' => array('name' => 'boolean',
'limit' => null),
);
}
/**
* Returns the maximum length a table alias can have.
*
* Returns the configured supported identifier length supported by
* PostgreSQL.
*
* @return integer The maximum table alias length.
*/
public function tableAliasLength()
{
return (int)$this->selectValue('SHOW max_identifier_length');
}
/**
* Returns a list of all tables in the schema search path.
*
* @return array A table list.
*/
public function tables()
{
return $this->selectValues('SELECT table_name FROM information_schema.tables WHERE table_schema = ANY (CURRENT_SCHEMAS(false));');
}
/**
* Returns a table's primary key.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return Horde_Db_Adapter_Base_Index The primary key index object.
*/
public function primaryKey($tableName, $name = null)
{
$sql = '
SELECT column_name
FROM information_schema.constraint_column_usage
WHERE table_name = ?
AND constraint_name = (SELECT constraint_name
FROM information_schema.table_constraints
WHERE table_name = ?
AND constraint_type = ?)';
$pk = $this->selectValues($sql,
array($tableName, $tableName, 'PRIMARY KEY'),
$name);
return $this->makeIndex($tableName, 'PRIMARY', true, true, $pk);
}
/**
* Returns a list of tables indexes.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return array A list of Horde_Db_Adapter_Base_Index objects.
*/
public function indexes($tableName, $name = null)
{
$indexes = @unserialize($this->cacheRead("tables/indexes/$tableName"));
if (!$indexes) {
$sql = "
SELECT distinct i.relname, d.indisunique, a.attname
FROM pg_class t, pg_class i, pg_index d, pg_attribute a
WHERE i.relkind = 'i'
AND d.indexrelid = i.oid
AND d.indisprimary = 'f'
AND t.oid = d.indrelid
AND t.relname = " . $this->quote($tableName) . "
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY(CURRENT_SCHEMAS(false)))
AND a.attrelid = t.oid
AND (d.indkey[0] = a.attnum OR d.indkey[1] = a.attnum
OR d.indkey[2] = a.attnum OR d.indkey[3] = a.attnum
OR d.indkey[4] = a.attnum OR d.indkey[5] = a.attnum
OR d.indkey[6] = a.attnum OR d.indkey[7] = a.attnum
OR d.indkey[8] = a.attnum OR d.indkey[9] = a.attnum)
ORDER BY i.relname";
$result = $this->select($sql, $name);
$currentIndex = null;
$indexes = array();
foreach ($result as $row) {
if ($currentIndex != $row['relname']) {
$currentIndex = $row['relname'];
$indexes[] = $this->makeIndex(
$tableName, $row['relname'], false, $row['indisunique'] == 't', array());
}
$indexes[count($indexes) - 1]->columns[] = $row['attname'];
}
$this->cacheWrite("tables/indexes/$tableName", serialize($indexes));
}
return $indexes;
}
/**
* Returns a list of table columns.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return array A list of Horde_Db_Adapter_Base_Column objects.
*/
public function columns($tableName, $name = null)
{
$rows = @unserialize($this->cacheRead("tables/columns/$tableName"));
if (!$rows) {
$rows = $this->_columnDefinitions($tableName, $name);
$this->cacheWrite("tables/columns/$tableName", serialize($rows));
}
// Create columns from rows.
$columns = array();
foreach ($rows as $row) {
$columns[$row['attname']] = $this->makeColumn(
$row['attname'], $row['adsrc'], $row['format_type'], !(boolean)$row['attnotnull']);
}
return $columns;
}
/**
* Returns the list of a table's column names, data types, and default
* values.
*
* The underlying query is roughly:
* SELECT column.name, column.type, default.value
* FROM column LEFT JOIN default
* ON column.table_id = default.table_id
* AND column.num = default.column_num
* WHERE column.table_id = get_table_id('table_name')
* AND column.num > 0
* AND NOT column.is_dropped
* ORDER BY column.num
*
* If the table name is not prefixed with a schema, the database will take
* the first match from the schema search path.
*
* Query implementation notes:
* - format_type includes the column size constraint, e.g. varchar(50)
* - ::regclass is a function that gives the id for a table name
*/
protected function _columnDefinitions($tableName, $name = null)
{
/* @todo See if we can get this from information_schema instead */
return $this->selectAll('
SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
WHERE a.attrelid = ' . $this->quote($tableName) . '::regclass
AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum', $name);
}
/**
* Renames a table.
*
* @param string $name A table name.
* @param string $newName The new table name.
*/
public function renameTable($name, $newName)
{
$this->_clearTableCache($name);
return $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $this->quoteTableName($name), $this->quoteTableName($newName)));
}
/**
* Adds a new column to a table.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param string $type A data type.
* @param array $options Column options. See
* Horde_Db_Adapter_Base_TableDefinition#column()
* for details.
*/
public function addColumn($tableName, $columnName, $type,
$options = array())
{
$this->_clearTableCache($tableName);
$options = array_merge(
array('autoincrement' => null,
'limit' => null,
'precision' => null,
'scale' => null),
$options);
$sqltype = $this->typeToSql($type, $options['limit'],
$options['precision'], $options['scale']);
/* Convert to SERIAL type if needed. */
if ($options['autoincrement']) {
switch ($sqltype) {
case 'bigint':
$sqltype = 'BIGSERIAL';
break;
case 'integer':
default:
$sqltype = 'SERIAL';
break;
}
}
// Add the column.
$sql = sprintf('ALTER TABLE %s ADD COLUMN %s %s',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName),
$sqltype);
$this->execute($sql);
if (array_key_exists('default', $options)) {
$sql = sprintf('UPDATE %s SET %s = %s',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName),
$this->quote($options['default']));
$this->execute($sql);
$this->changeColumnDefault($tableName, $columnName,
$options['default']);
}
if (isset($options['null']) && $options['null'] === false) {
$this->changeColumnNull(
$tableName, $columnName, false,
isset($options['default']) ? $options['default'] : null);
}
}
/**
* Changes an existing column's definition.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param string $type A data type.
* @param array $options Column options. See
* Horde_Db_Adapter_Base_TableDefinition#column()
* for details.
*/
public function changeColumn($tableName, $columnName, $type,
$options = array())
{
$this->_clearTableCache($tableName);
$options = array_merge(
array('autoincrement' => null,
'limit' => null,
'precision' => null,
'scale' => null),
$options);
$quotedTableName = $this->quoteTableName($tableName);
$primaryKey = $type == 'autoincrementKey';
if ($primaryKey) {
$type = 'integer';
$options['autoincrement'] = true;
$options['limit'] = $options['precision'] = $options['scale'] = null;
try {
$this->removePrimaryKey($tableName);
} catch (Horde_Db_Exception $e) {
}
}
$sql = sprintf('ALTER TABLE %s ALTER COLUMN %s TYPE %s',
$quotedTableName,
$this->quoteColumnName($columnName),
$this->typeToSql($type,
$options['limit'],
$options['precision'],
$options['scale']));
try {
$this->execute($sql);
} catch (Horde_Db_Exception $e) {
// This is PostgreSQL 7.x, or the old type could not be coerced to
// the new type, so we have to use a more arcane way of doing it.
try {
// Booleans can't always be cast to other data types; do extra
// work to handle them.
$oldType = $this->column($tableName, $columnName)->getType();
$this->beginDbTransaction();
$tmpColumnName = $columnName.'_change_tmp';
$this->addColumn($tableName, $tmpColumnName, $type, $options);
if ($oldType == 'boolean') {
$sql = sprintf('UPDATE %s SET %s = CAST(CASE WHEN %s IS TRUE THEN 1 ELSE 0 END AS %s)',
$quotedTableName,
$this->quoteColumnName($tmpColumnName),
$this->quoteColumnName($columnName),
$this->typeToSql($type,
$options['limit'],
$options['precision'],
$options['scale']));
} else {
$sql = sprintf('UPDATE %s SET %s = CAST(%s AS %s)',
$quotedTableName,
$this->quoteColumnName($tmpColumnName),
$this->quoteColumnName($columnName),
$this->typeToSql($type,
$options['limit'],
$options['precision'],
$options['scale']));
}
$this->execute($sql);
$this->removeColumn($tableName, $columnName);
$this->renameColumn($tableName, $tmpColumnName, $columnName);
$this->commitDbTransaction();
} catch (Horde_Db_Exception $e) {
$this->rollbackDbTransaction();
throw $e;
}
}
if ($options['autoincrement']) {
$seq_name = $this->defaultSequenceName($tableName, $columnName);
try {
$this->execute('DROP SEQUENCE ' . $seq_name . ' CASCADE');
} catch (Horde_Db_Exception $e) {}
$this->execute('CREATE SEQUENCE ' . $seq_name);
$this->resetPkSequence($tableName, $columnName, $seq_name);
/* Can't use changeColumnDefault() since it quotes the
* default value (NEXTVAL is a postgres keyword, not a text
* value). */
$this->_clearTableCache($tableName);
$sql = sprintf('ALTER TABLE %s ALTER COLUMN %s SET DEFAULT NEXTVAL(%s)',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName),
$this->quoteSequenceName($seq_name));
$this->execute($sql);
$sql = sprintf('ALTER SEQUENCE %s OWNED BY %s.%s',
$seq_name,
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName));
$this->execute($sql);
} elseif (array_key_exists('default', $options)) {
$this->changeColumnDefault($tableName, $columnName,
$options['default']);
}
if ($primaryKey) {
$this->addPrimaryKey($tableName, $columnName);
}
if (array_key_exists('null', $options)) {
$this->changeColumnNull(
$tableName, $columnName, $options['null'],
isset($options['default']) ? $options['default'] : null);
}
}
/**
* Sets a new default value for a column.
*
* If you want to set the default value to NULL, you are out of luck. You
* need to execute the apppropriate SQL statement yourself.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param mixed $default The new default value.
*/
public function changeColumnDefault($tableName, $columnName, $default)
{
$this->_clearTableCache($tableName);
$sql = sprintf('ALTER TABLE %s ALTER COLUMN %s SET DEFAULT %s',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName),
$this->quote($default));
return $this->execute($sql);
}
/**
* Sets whether a column allows NULL values.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param boolean $null Whether NULL values are allowed.
* @param mixed $default The new default value.
*/
public function changeColumnNull($tableName, $columnName, $null,
$default = null)
{
$this->_clearTableCache($tableName);
if (!$null && !is_null($default)) {
$sql = sprintf('UPDATE %s SET %s = %s WHERE %s IS NULL',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName),
$this->quote($default),
$this->quoteColumnName($columnName));
$this->execute($sql);
}
$sql = sprintf('ALTER TABLE %s ALTER %s %s NOT NULL',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName),
$null ? 'DROP' : 'SET');
return $this->execute($sql);
}
/**
* Renames a column.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param string $newColumnName The new column name.
*/
public function renameColumn($tableName, $columnName, $newColumnName)
{
$this->_clearTableCache($tableName);
$sql = sprintf('ALTER TABLE %s RENAME COLUMN %s TO %s',
$this->quoteTableName($tableName),
$this->quoteColumnName($columnName),
$this->quoteColumnName($newColumnName));
return $this->execute($sql);
}
/**
* Removes a primary key from a table.
*
* @param string $tableName A table name.
*
* @throws Horde_Db_Exception
*/
public function removePrimaryKey($tableName)
{
$this->_clearTableCache($tableName);
$keyName = $this->selectValue(
'SELECT constraint_name
FROM information_schema.table_constraints
WHERE table_name = ?
AND constraint_type = ?',
array($tableName, 'PRIMARY KEY'));
if ($keyName) {
$sql = sprintf('ALTER TABLE %s DROP CONSTRAINT %s CASCADE',
$this->quoteTableName($tableName),
$this->quoteColumnName($keyName));
return $this->execute($sql);
}
}
/**
* Removes an index from a table.
*
* See parent class for examples.
*
* @param string $tableName A table name.
* @param string|array $options Either a column name or index options:
* - name: (string) the index name.
* - column: (string|array) column name(s).
*/
public function removeIndex($tableName, $options = array())
{
$this->_clearTableCache($tableName);
return $this->execute('DROP INDEX ' . $this->indexName($tableName, $options));
}
/**
* Creates a database.
*
* @param string $name A database name.
* @param array $options Database options: owner, template, charset,
* tablespace, and connection_limit.
*/
public function createDatabase($name, $options = array())
{
$options = array_merge(array('charset' => 'utf8'), $options);
$optionString = '';
foreach ($options as $key => $value) {
switch ($key) {
case 'owner':
$optionString .= " OWNER = '$value'";
break;
case 'template':
$optionString .= " TEMPLATE = $value";
break;
case 'charset':
$optionString .= " ENCODING = '$value'";
break;
case 'tablespace':
$optionString .= " TABLESPACE = $value";
break;
case 'connection_limit':
$optionString .= " CONNECTION LIMIT = $value";
}
}
return $this->execute('CREATE DATABASE ' . $this->quoteTableName($name) . $optionString);
}
/**
* Drops a database.
*
* @param string $name A database name.
*/
public function dropDatabase($name)
{
return $this->execute('DROP DATABASE IF EXISTS ' . $this->quoteTableName($name));
}
/**
* Returns the name of the currently selected database.
*
* @return string The database name.
*/
public function currentDatabase()
{
return $this->selectValue('SELECT current_database()');
}
/**
* Generates the SQL definition for a column type.
*
* @param string $type A column type.
* @param integer $limit Maximum column length (non decimal type only)
* @param integer $precision The number precision (decimal type only).
* @param integer $scale The number scaling (decimal columns only).
* @param boolean $unsigned Whether the column is an unsigned number
* (non decimal columns only).
*
* @return string The SQL definition. If $type is not one of the
* internally supported types, $type is returned unchanged.
*/
public function typeToSql($type, $limit = null, $precision = null,
$scale = null, $unsigned = null)
{
if ($type != 'integer') {
return parent::typeToSql($type, $limit, $precision, $scale);
}
switch ($limit) {
case 1:
case 2:
return 'smallint';
case 3:
case 4:
case null:
return 'integer';
case 5:
case 6:
case 7:
case 8:
return 'bigint';
}
throw new Horde_Db_Exception("No integer type has byte size $limit. Use a numeric with precision 0 instead.");
}
/**
* Generates a DISTINCT clause for SELECT queries.
*
* PostgreSQL requires the ORDER BY columns in the SELECT list for distinct
* queries, and requires that the ORDER BY include the DISTINCT column.
*
*
* $connection->distinct('posts.id', 'posts.created_at DESC')
*
*
* @param string $columns A column list.
* @param string $orderBy An ORDER clause.
*
* @return string The generated DISTINCT clause.
*/
public function distinct($columns, $orderBy = null)
{
if (empty($orderBy)) {
return 'DISTINCT ' . $columns;
}
// Construct a clean list of column names from the ORDER BY clause,
// removing any ASC/DESC modifiers.
$orderColumns = array();
foreach (preg_split('/\s*,\s*/', $orderBy, -1, PREG_SPLIT_NO_EMPTY) as $orderByClause) {
$orderColumns[] = current(preg_split('/\s+/', $orderByClause, -1, PREG_SPLIT_NO_EMPTY)) . ' AS alias_' . count($orderColumns);
}
// Return a DISTINCT ON() clause that's distinct on the columns we want
// but includes all the required columns for the ORDER BY to work
// properly.
return sprintf('DISTINCT ON (%s) %s, %s',
$columns, $columns, implode(', ', $orderColumns));
}
/**
* Adds an ORDER BY clause to an existing query.
*
* PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so
* we work around this by wrapping the $sql string as a sub-select and
* ordering in that query.
*
* @param string $sql An SQL query to manipulate.
* @param array $options Options:
* - order: Order column an direction.
*
* @return string The manipulated SQL query.
*/
public function addOrderByForAssociationLimiting($sql, $options)
{
if (empty($options['order'])) {
return $sql;
}
$order = array();
foreach (preg_split('/\s*,\s*/', $options['order'], -1, PREG_SPLIT_NO_EMPTY) as $s) {
if (preg_match('/\bdesc$/i', $s)) {
$s = 'DESC';
}
$order[] = 'id_list.alias_' . count($order) . ' ' . $s;
}
$order = implode(', ', $order);
return sprintf('SELECT * FROM (%s) AS id_list ORDER BY %s',
$sql, $order);
}
/**
* Generates an INTERVAL clause for SELECT queries.
*
* @param string $interval The interval.
* @param string $precision The precision.
*
* @return string The generated INTERVAL clause.
*/
public function interval($interval, $precision)
{
return 'INTERVAL \'' . $interval . ' ' . $precision . '\'';
}
/**
* Generates a modified date for SELECT queries.
*
* @param string $reference The reference date - this is a column
* referenced in the SELECT.
* @param string $operator Add or subtract time? (+/-)
* @param integer $amount The shift amount (number of days if $interval
* is DAY, etc).
* @param string $interval The interval (SECOND, MINUTE, HOUR, DAY,
* MONTH, YEAR).
*
* @return string The generated INTERVAL clause.
*/
public function modifyDate($reference, $operator, $amount, $interval)
{
if (!is_int($amount)) {
throw new InvalidArgumentException('$amount parameter must be an integer');
}
return sprintf('%s %s INTERVAL \'%s %s\'',
$reference,
$operator,
$amount,
$interval);
}
/**
* Returns an expression using the specified operator.
*
* @param string $lhs The column or expression to test.
* @param string $op The operator.
* @param string $rhs The comparison value.
* @param boolean $bind If true, the method returns the query and a list
* of values suitable for binding as an array.
* @param array $params Any additional parameters for the operator.
*
* @return string|array The SQL test fragment, or an array containing the
* query and a list of values if $bind is true.
*/
public function buildClause($lhs, $op, $rhs, $bind = false,
$params = array())
{
$lhs = $this->_escapePrepare($lhs);
switch ($op) {
case '|':
case '&':
/* Only PgSQL 7.3+ understands SQL99 'SIMILAR TO'; use ~ for
* greater backwards compatibility. */
$query = 'CASE WHEN CAST(%s AS VARCHAR) ~ \'^-?[0-9]+$\' THEN (CAST(%s AS INTEGER) %s %s) ELSE 0 END';
if ($bind) {
return array(sprintf($query, $lhs, $lhs, $op, '?'),
array((int)$rhs));
} else {
return sprintf($query, $lhs, $lhs, $op, (int)$rhs);
}
case 'LIKE':
$query = '%s ILIKE %s';
if ($bind) {
if (empty($params['begin'])) {
return array(sprintf($query, $lhs, '?'),
array('%' . $rhs . '%'));
}
return array(sprintf('(' . $query . ' OR ' . $query . ')',
$lhs, '?', $lhs, '?'),
array($rhs . '%', '% ' . $rhs . '%'));
}
if (empty($params['begin'])) {
return sprintf($query,
$lhs,
$this->_escapePrepare($this->quote('%' . $rhs . '%')));
}
return sprintf('(' . $query . ' OR ' . $query . ')',
$lhs,
$this->_escapePrepare($this->quote($rhs . '%')),
$lhs,
$this->_escapePrepare($this->quote('% ' . $rhs . '%')));
}
return parent::buildClause($lhs, $op, $rhs, $bind, $params);
}
/*##########################################################################
# PostgreSQL specific methods
##########################################################################*/
/**
* Returns the current database's encoding format.
*
* @return string The current database's encoding format.
*/
public function encoding()
{
return $this->selectValue(
'SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
WHERE pg_database.datname LIKE ' . $this->quote($this->currentDatabase()));
}
/**
* Sets the schema search path to a string of comma-separated schema names.
*
* Names beginning with $ have to be quoted (e.g. $user => '$user'). See:
* http://www.postgresql.org/docs/current/static/ddl-schemas.html
*
* @param string $schemaCsv A comma-separated schema name list.
*/
public function setSchemaSearchPath($schemaCsv)
{
if ($schemaCsv) {
$this->execute('SET search_path TO ' . $schemaCsv);
$this->_schemaSearchPath = $schemaCsv;
}
}
/**
* Returns the current client log message level.
*
* @return string The current client log message level.
*/
public function getClientMinMessages()
{
return $this->selectValue('SHOW client_min_messages');
}
/**
* Sets the client log message level.
*
* @param string $level The client log message level. One of DEBUG5,
* DEBUG4, DEBUG3, DEBUG2, DEBUG1, LOG, NOTICE,
* WARNING, ERROR, FATAL, or PANIC.
*/
public function setClientMinMessages($level)
{
return $this->execute('SET client_min_messages TO ' . $this->quote($level));
}
/**
* Returns the sequence name for a table's primary key or some other
* specified key.
*
* If a sequence name doesn't exist, it is built from the table and primary
* key name.
*
* @param string $tableName A table name.
* @param string $pk A primary key name. Overrides the existing key
* name when building a new sequence name.
*
* @return string The key's sequence name.
*/
public function defaultSequenceName($tableName, $pk = null)
{
list($defaultPk, $defaultSeq) = $this->pkAndSequenceFor($tableName);
if (!$defaultSeq) {
$defaultSeq = $tableName . '_' . ($pk ? $pk : ($defaultPk ? $defaultPk : 'id')) . '_seq';
}
return $defaultSeq;
}
/**
* Resets the sequence of a table's primary key to the maximum value.
*
* @param string $tableName A table name.
* @param string $pk A primary key name. Defaults to the existing
* primary key.
* @param string $sequence A sequence name. Defaults to the sequence name
* of the existing primary key.
*
* @return integer The (next) sequence value if a primary key and a
* sequence exist.
*/
public function resetPkSequence($table, $pk = null, $sequence = null)
{
if (!$pk || !$sequence) {
list($defaultPk, $defaultSequence) = $this->pkAndSequenceFor($table);
if (!$pk) {
$pk = $defaultPk;
}
if (!$sequence) {
$sequence = $defaultSequence;
}
}
if ($pk) {
if ($sequence) {
$quotedSequence = $this->quoteSequenceName($sequence);
$quotedTable = $this->quoteTableName($table);
$quotedPk = $this->quoteColumnName($pk);
$sql = sprintf('SELECT setval(%s, (SELECT COALESCE(MAX(%s) + (SELECT increment_by FROM %s), (SELECT min_value FROM %s)) FROM %s), false)',
$quotedSequence,
$quotedPk,
$sequence,
$sequence,
$quotedTable);
$this->selectValue($sql, 'Reset sequence');
} else {
if ($this->_logger) {
$this->_logger->warn(sprintf('%s has primary key %s with no default sequence', $table, $pk));
}
}
}
}
/**
* Returns a table's primary key and the key's sequence.
*
* @param string $tableName A table name.
*
* @return array Array with two values: the primary key name and the key's
* sequence name.
*/
public function pkAndSequenceFor($table)
{
// First try looking for a sequence with a dependency on the
// given table's primary key.
$sql = "
SELECT attr.attname, seq.relname
FROM pg_class seq,
pg_attribute attr,
pg_depend dep,
pg_namespace name,
pg_constraint cons
WHERE seq.oid = dep.objid
AND seq.relkind = 'S'
AND attr.attrelid = dep.refobjid
AND attr.attnum = dep.refobjsubid
AND attr.attrelid = cons.conrelid
AND attr.attnum = cons.conkey[1]
AND cons.contype = 'p'
AND dep.refobjid = '$table'::regclass";
$result = $this->selectOne($sql, 'PK and serial sequence');
if (!$result) {
// If that fails, try parsing the primary key's default value.
// Support the 7.x and 8.0 nextval('foo'::text) as well as
// the 8.1+ nextval('foo'::regclass).
$sql = "
SELECT attr.attname,
CASE
WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
substr(split_part(def.adsrc, '''', 2),
strpos(split_part(def.adsrc, '''', 2), '.')+1)
ELSE split_part(def.adsrc, '''', 2)
END AS relname
FROM pg_class t
JOIN pg_attribute attr ON (t.oid = attrelid)
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
WHERE t.oid = '$table'::regclass
AND cons.contype = 'p'
AND def.adsrc ~* 'nextval'";
$result = $this->selectOne($sql, 'PK and custom sequence');
}
// [primary_key, sequence]
return array($result['attname'], $result['relname']);
}
/**
* Returns the version of the connected PostgreSQL server.
*
* @return integer Zero padded PostgreSQL version, e.g. 80108 for 8.1.8.
*/
public function postgresqlVersion()
{
if (!$this->_version) {
try {
$version = $this->selectValue('SELECT version()');
if (preg_match('/PostgreSQL (\d+)\.(\d+)\.(\d+)/', $version, $matches))
$this->_version = ($matches[1] * 10000) + ($matches[2] * 100) + $matches[3];
} catch (Exception $e) {
return 0;
}
}
return $this->_version;
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Sqlite/Column.php 0000664 0001750 0001750 00000004366 13054774261 020234 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Sqlite_Column extends Horde_Db_Adapter_Base_Column
{
public function extractDefault($default)
{
$default = parent::extractDefault($default);
if ($this->isText()) {
$default = $this->_unquote($default);
}
return $default;
}
/*##########################################################################
# Type Juggling
##########################################################################*/
public function binaryToString($value)
{
return str_replace(array('%00', '%25'), array("\0", '%'), $value);
}
/**
* @param mixed $value
* @return boolean
*/
public function valueToBoolean($value)
{
if ($value == '"t"' || $value == "'t'") {
return true;
} elseif ($value == '""' || $value == "''") {
return null;
} else {
return parent::valueToBoolean($value);
}
}
/*##########################################################################
# Protected
##########################################################################*/
/**
* Unquote a string value
*
* @return string
*/
protected function _unquote($string)
{
$first = substr($string, 0, 1);
if ($first == "'" || $first == '"') {
$string = substr($string, 1);
if (substr($string, -1) == $first) {
$string = substr($string, 0, -1);
}
$string = str_replace("$first$first", $first, $string);
}
return $string;
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Sqlite/Schema.php 0000664 0001750 0001750 00000063766 13054774261 020210 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* Class for SQLite-specific managing of database schemes and handling of SQL
* dialects and quoting.
*
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Sqlite_Schema extends Horde_Db_Adapter_Base_Schema
{
/*##########################################################################
# Object factories
##########################################################################*/
/**
* Factory for Column objects.
*
* @param string $name The column's name, such as "supplier_id" in
* "supplier_id int(11)".
* @param string $default The type-casted default value, such as "new" in
* "sales_stage varchar(20) default 'new'".
* @param string $sqlType Used to extract the column's type, length and
* signed status, if necessary. For example
* "varchar" and "60" in "company_name varchar(60)"
* or "unsigned => true" in "int(10) UNSIGNED".
* @param boolean $null Whether this column allows NULL values.
*
* @return Horde_Db_Adapter_Base_Column A column object.
*/
public function makeColumn($name, $default, $sqlType = null, $null = true)
{
return new Horde_Db_Adapter_Sqlite_Column($name, $default, $sqlType, $null);
}
/*##########################################################################
# Quoting
##########################################################################*/
/**
* Returns a quoted binary value.
*
* @param mixed A binary value.
*
* @return string The quoted binary value.
*/
public function quoteBinary($value)
{
return "'" . str_replace(array("'", '%', "\0"), array("''", '%25', '%00'), $value) . "'";
}
/*##########################################################################
# Schema Statements
##########################################################################*/
/**
* Returns a hash of mappings from the abstract data types to the native
* database types.
*
* See TableDefinition::column() for details on the recognized abstract
* data types.
*
* @see TableDefinition::column()
*
* @return array A database type map.
*/
public function nativeDatabaseTypes()
{
return array(
'autoincrementKey' => $this->_defaultPrimaryKeyType(),
'string' => array('name' => 'varchar', 'limit' => 255),
'text' => array('name' => 'text', 'limit' => null),
'mediumtext' => array('name' => 'text', 'limit' => null),
'longtext' => array('name' => 'text', 'limit' => null),
'integer' => array('name' => 'int', 'limit' => null),
'float' => array('name' => 'float', 'limit' => null),
'decimal' => array('name' => 'decimal', 'limit' => null),
'datetime' => array('name' => 'datetime', 'limit' => null),
'timestamp' => array('name' => 'datetime', 'limit' => null),
'time' => array('name' => 'time', 'limit' => null),
'date' => array('name' => 'date', 'limit' => null),
'binary' => array('name' => 'blob', 'limit' => null),
'boolean' => array('name' => 'boolean', 'limit' => null),
);
}
/**
* Returns a list of all tables of the current database.
*
* @return array A table list.
*/
public function tables()
{
return $this->selectValues("SELECT name FROM sqlite_master WHERE type = 'table' UNION ALL SELECT name FROM sqlite_temp_master WHERE type = 'table' AND name != 'sqlite_sequence' ORDER BY name");
}
/**
* Returns a table's primary key.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return Horde_Db_Adapter_Base_Index The primary key index object.
*/
public function primaryKey($tableName, $name = null)
{
// Share the columns cache with the columns() method
$rows = @unserialize($this->cacheRead("tables/columns/$tableName"));
if (!$rows) {
$rows = $this->selectAll('PRAGMA table_info(' . $this->quoteTableName($tableName) . ')', $name);
$this->cacheWrite("tables/columns/$tableName", serialize($rows));
}
$pk = $this->makeIndex($tableName, 'PRIMARY', true, true, array());
foreach ($rows as $row) {
if ($row['pk']) {
$pk->columns[] = $row['name'];
}
}
return $pk;
}
/**
* Returns a list of tables indexes.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return array A list of Horde_Db_Adapter_Base_Index objects.
*/
public function indexes($tableName, $name = null)
{
$indexes = @unserialize($this->cacheRead("tables/indexes/$tableName"));
if (!$indexes) {
$indexes = array();
foreach ($this->select('PRAGMA index_list(' . $this->quoteTableName($tableName) . ')') as $row) {
if (strpos($row['name'], 'sqlite_') !== false) {
// ignore internal sqlite_* index tables
continue;
}
$index = $this->makeIndex(
$tableName, $row['name'], false, (bool)$row['unique'], array());
foreach ($this->select('PRAGMA index_info(' . $this->quoteColumnName($index->name) . ')') as $field) {
$index->columns[] = $field['name'];
}
$indexes[] = $index;
}
$this->cacheWrite("tables/indexes/$tableName", serialize($indexes));
}
return $indexes;
}
/**
* Returns a list of table columns.
*
* @param string $tableName A table name.
* @param string $name (can be removed?)
*
* @return array A list of Horde_Db_Adapter_Base_Column objects.
*/
public function columns($tableName, $name = null)
{
$rows = @unserialize($this->cacheRead("tables/columns/$tableName"));
if (!$rows) {
$rows = $this->selectAll('PRAGMA table_info(' . $this->quoteTableName($tableName) . ')', $name);
$this->cacheWrite("tables/columns/$tableName", serialize($rows));
}
// create columns from rows
$columns = array();
foreach ($rows as $row) {
$columns[$row['name']] = $this->makeColumn(
$row['name'], $row['dflt_value'], $row['type'], !(bool)$row['notnull']);
}
return $columns;
}
/**
* Renames a table.
*
* @param string $name A table name.
* @param string $newName The new table name.
*/
public function renameTable($name, $newName)
{
$this->_clearTableCache($name);
$sql = sprintf('ALTER TABLE %s RENAME TO %s',
$this->quoteTableName($name),
$this->quoteTableName($newName));
return $this->execute($sql);
}
/**
* Adds a new column to a table.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param string $type A data type.
* @param array $options Column options. See
* Horde_Db_Adapter_Base_TableDefinition#column()
* for details.
*/
public function addColumn($tableName, $columnName, $type, $options = array())
{
if ($this->transactionStarted()) {
throw new Horde_Db_Exception('Cannot add columns to a SQLite database while inside a transaction');
}
if ($type == 'autoincrementKey') {
$this->_alterTable(
$tableName,
array(),
function ($definition) use ($columnName, $type, $options) {
$definition->column($columnName, $type, $options);
}
);
} else {
parent::addColumn($tableName, $columnName, $type, $options);
}
// See last paragraph on http://www.sqlite.org/lang_altertable.html
$this->execute('VACUUM');
}
/**
* Removes a column from a table.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
*/
public function removeColumn($tableName, $columnName)
{
$this->_clearTableCache($tableName);
return $this->_alterTable(
$tableName,
array(),
function ($definition) use ($columnName) {
unset($definition[$columnName]);
}
);
}
/**
* Changes an existing column's definition.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param string $type A data type.
* @param array $options Column options. See
* Horde_Db_Adapter_Base_TableDefinition#column()
* for details.
*/
public function changeColumn($tableName, $columnName, $type, $options = array())
{
$this->_clearTableCache($tableName);
$defs = array(
function ($definition) use ($columnName, $type) {
$definition[$columnName]->setType($type);
},
function ($definition) use ($type) {
if ($type == 'autoincrementKey') {
$definition->primaryKey(false);
}
}
);
if (isset($options['limit'])) {
$defs[] = function ($definition) use ($columnName, $options) {
$definition[$columnName]->setLimit($options['limit']);
};
}
if (isset($options['null'])) {
$defs[] = function ($definition) use ($columnName, $options) {
$definition[$columnName]->setNull((bool)$options['null']);
};
}
if (isset($options['precision'])) {
$defs[] = function ($definition) use ($columnName, $options) {
$definition[$columnName]->setPrecision($options['precision']);
};
}
if (isset($options['scale'])) {
$defs[] = function ($definition) use ($columnName, $options) {
$definition[$columnName]->setScale($options['scale']);
};
}
if (array_key_exists('default', $options)) {
$defs[] = function ($definition) use ($columnName, $options) {
$definition[$columnName]->setDefault($options['default']);
};
}
return $this->_alterTable(
$tableName,
array(),
function ($definition) use ($defs) {
foreach ($defs as $callback) {
$callback($definition);
}
}
);
}
/**
* Sets a new default value for a column.
*
* If you want to set the default value to NULL, you are out of luck. You
* need to execute the apppropriate SQL statement yourself.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param mixed $default The new default value.
*/
public function changeColumnDefault($tableName, $columnName, $default)
{
$this->_clearTableCache($tableName);
return $this->_alterTable(
$tableName,
array(),
function ($definition) use ($columnName, $default) {
$definition[$columnName]->setDefault($default);
}
);
}
/**
* Renames a column.
*
* @param string $tableName A table name.
* @param string $columnName A column name.
* @param string $newColumnName The new column name.
*/
public function renameColumn($tableName, $columnName, $newColumnName)
{
$this->_clearTableCache($tableName);
return $this->_alterTable(
$tableName,
array('rename' => array($columnName => $newColumnName)));
}
/**
* Adds a primary key to a table.
*
* @param string $tableName A table name.
* @param string|array $columnName One or more column names.
*
* @throws Horde_Db_Exception
*/
public function addPrimaryKey($tableName, $columns)
{
$this->_clearTableCache($tableName);
$columns = (array)$columns;
$callback = function ($definition) use ($columns) {
$definition->primaryKey($columns);
};
$this->_alterTable($tableName, array(), $callback);
}
/**
* Removes a primary key from a table.
*
* @param string $tableName A table name.
*
* @throws Horde_Db_Exception
*/
public function removePrimaryKey($tableName)
{
$this->_clearTableCache($tableName);
$callback = function ($definition) {
$definition->primaryKey(false);
};
$this->_alterTable($tableName, array(), $callback);
}
/**
* Removes an index from a table.
*
* See parent class for examples.
*
* @param string $tableName A table name.
* @param string|array $options Either a column name or index options:
* - name: (string) the index name.
* - column: (string|array) column name(s).
*/
public function removeIndex($tableName, $options=array())
{
$this->_clearTableCache($tableName);
$index = $this->indexName($tableName, $options);
$sql = 'DROP INDEX ' . $this->quoteColumnName($index);
return $this->execute($sql);
}
/**
* Creates a database.
*
* @param string $name A database name.
* @param array $options Database options.
*/
public function createDatabase($name, $options = array())
{
return new PDO('sqlite:' . $name);
}
/**
* Drops a database.
*
* @param string $name A database name.
*/
public function dropDatabase($name)
{
if (!@file_exists($name)) {
throw new Horde_Db_Exception('database does not exist');
}
if (!@unlink($name)) {
throw new Horde_Db_Exception('could not remove the database file');
}
}
/**
* Returns the name of the currently selected database.
*
* @return string The database name.
*/
public function currentDatabase()
{
return $this->_config['dbname'];
}
/**
* Generates a modified date for SELECT queries.
*
* @param string $reference The reference date - this is a column
* referenced in the SELECT.
* @param string $operator Add or subtract time? (+/-)
* @param integer $amount The shift amount (number of days if $interval
* is DAY, etc).
* @param string $interval The interval (SECOND, MINUTE, HOUR, DAY,
* MONTH, YEAR).
*
* @return string The generated INTERVAL clause.
*/
public function modifyDate($reference, $operator, $amount, $interval)
{
if (!is_int($amount)) {
throw new InvalidArgumentException('$amount parameter must be an integer');
}
switch ($interval) {
case 'YEAR':
$interval = 'years';
break;
case 'MONTH':
$interval = 'months';
break;
case 'DAY':
$interval = 'days';
break;
case 'HOUR':
$interval = 'hours';
break;
case 'MINUTE':
$interval = 'minutes';
break;
case 'SECOND':
$interval = 'seconds';
break;
default:
break;
}
return 'datetime(' . $reference . ', \'' . $operator . $amount . ' '
. $interval . '\')';
}
/*##########################################################################
# Protected
##########################################################################*/
/**
* Returns a column type definition to be use for primary keys.
*
* @return string Primary key type definition.
*/
protected function _defaultPrimaryKeyType()
{
if ($this->supportsAutoIncrement()) {
return 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL';
} else {
return 'INTEGER PRIMARY KEY NOT NULL';
}
}
/**
* Alters a table.
*
* This is done by creating a temporary copy, applying changes and callback
* methods, and copying the table back.
*
* @param string $tableName A table name.
* @param array $options Any options to apply when creating the
* temporary table. Supports a 'rename' key for
* the new table name, additionally to the
* options in createTable().
* @param function $callback A callback function that can manipulate the
* Horde_Db_Adapter_Base_TableDefinition object
* available in $definition. See _copyTable().
*/
protected function _alterTable($tableName, $options = array(), $callback = null)
{
$this->beginDbTransaction();
$alteredTableName = 'altered_' . $tableName;
$this->_moveTable($tableName,
$alteredTableName,
array_merge($options, array('temporary' => true)));
$this->_moveTable($alteredTableName,
$tableName,
array(),
$callback);
$this->commitDbTransaction();
}
/**
* Moves a table.
*
* This is done by creating a temporary copy, applying changes and callback
* methods, and dropping the original table.
*
* @param string $from The name of the source table.
* @param string $to The name of the target table.
* @param array $options Any options to apply when creating the
* temporary table. Supports a 'rename' key for
* the new table name, additionally to the
* options in createTable().
* @param function $callback A callback function that can manipulate the
* Horde_Db_Adapter_Base_TableDefinition object
* available in $definition. See _copyTable().
*/
protected function _moveTable($from, $to, $options = array(),
$callback = null)
{
$this->_copyTable($from, $to, $options, $callback);
$this->dropTable($from);
}
/**
* Copies a table.
*
* Also applies changes and callback methods before creating the new table.
*
* @param string $from The name of the source table.
* @param string $to The name of the target table.
* @param array $options Any options to apply when creating the
* temporary table. Supports a 'rename' key for
* the new table name, additionally to the
* options in createTable().
* @param function $callback A callback function that can manipulate the
* Horde_Db_Adapter_Base_TableDefinition object
* available in $definition.
*/
protected function _copyTable($from, $to, $options = array(),
$callback = null)
{
$fromColumns = $this->columns($from);
$pk = $this->primaryKey($from);
if ($pk && count($pk->columns) == 1) {
/* A primary key is not necessarily what matches the pseudo type
* "autoincrementKey". We need to parse the table definition to
* find out if the column is AUTOINCREMENT too. */
$tableDefinition = $this->selectValue('SELECT sql FROM sqlite_master WHERE name = ? UNION ALL SELECT sql FROM sqlite_temp_master WHERE name = ?',
array($from, $from));
if (strpos($tableDefinition, $this->quoteColumnName($pk->columns[0]) . ' INTEGER PRIMARY KEY AUTOINCREMENT')) {
$pkColumn = $pk->columns[0];
} else {
$pkColumn = null;
}
} else {
$pkColumn = null;
}
$options = array_merge($options, array('autoincrementKey' => false));
$copyPk = true;
$definition = $this->createTable($to, $options);
foreach ($fromColumns as $column) {
$columnName = isset($options['rename'][$column->getName()])
? $options['rename'][$column->getName()]
: $column->getName();
$columnType = $column->getName() == $pkColumn
? 'autoincrementKey'
: $column->getType();
$columnOptions = array('limit' => $column->getLimit());
if ($columnType == 'autoincrementKey') {
$copyPk = false;
} else {
$columnOptions['default'] = $column->getDefault();
$columnOptions['null'] = $column->isNull();
}
$definition->column($columnName, $columnType, $columnOptions);
}
if ($pkColumn && count($pk->columns) && $copyPk) {
$definition->primaryKey($pk->columns);
}
if (is_callable($callback)) {
call_user_func($callback, $definition);
}
$definition->end();
$this->_copyTableIndexes(
$from,
$to,
isset($options['rename']) ? $options['rename'] : array());
$this->_copyTableContents(
$from,
$to,
array_map(
function($c) { return $c->getName(); },
iterator_to_array($definition)
),
isset($options['rename']) ? $options['rename'] : array());
}
/**
* Copies indexes from one table to another.
*
* @param string $from The name of the source table.
* @param string $to The name of the target table.
* @param array $rename A hash of columns to rename during the copy, with
* original names as keys and the new names as values.
*/
protected function _copyTableIndexes($from, $to, $rename = array())
{
$toColumnNames = array();
foreach ($this->columns($to) as $c) {
$toColumnNames[$c->getName()] = true;
}
foreach ($this->indexes($from) as $index) {
$name = $index->getName();
if ($to == 'altered_' . $from) {
$name = 'temp_' . $name;
} elseif ($from == 'altered_' . $to) {
$name = substr($name, 5);
}
$columns = array();
foreach ($index->columns as $c) {
if (isset($rename[$c])) {
$c = $rename[$c];
}
if (isset($toColumnNames[$c])) {
$columns[] = $c;
}
}
if (!empty($columns)) {
// Index name can't be the same
$opts = array('name' => str_replace('_' . $from . '_', '_' . $to . '_', $name));
if ($index->unique) {
$opts['unique'] = true;
}
$this->addIndex($to, $columns, $opts);
}
}
}
/**
* Copies the content of one table to another.
*
* @param string $from The name of the source table.
* @param string $to The name of the target table.
* @param array $columns A list of columns to copy.
* @param array $rename A hash of columns to rename during the copy, with
* original names as keys and the new names as
* values.
*/
protected function _copyTableContents($from, $to, $columns,
$rename = array())
{
$columnMappings = array_combine($columns, $columns);
foreach ($rename as $renameFrom => $renameTo) {
$columnMappings[$renameTo] = $renameFrom;
}
$fromColumns = array();
foreach ($this->columns($from) as $col) {
$fromColumns[] = $col->getName();
}
$tmpColumns = array();
foreach ($columns as $col) {
if (in_array($columnMappings[$col], $fromColumns)) {
$tmpColumns[] = $col;
}
}
$columns = $tmpColumns;
$fromColumns = array();
foreach ($columns as $col) {
$fromColumns[] = $columnMappings[$col];
}
$quotedTo = $this->quoteTableName($to);
$quotedToColumns = implode(', ', array_map(array($this, 'quoteColumnName'), $columns));
$quotedFrom = $this->quoteTableName($from);
$quotedFromColumns = implode(', ', array_map(array($this, 'quoteColumnName'), $fromColumns));
$sql = sprintf('INSERT INTO %s (%s) SELECT %s FROM %s',
$quotedTo,
$quotedToColumns,
$quotedFromColumns,
$quotedFrom);
$this->execute($sql);
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Base.php 0000664 0001750 0001750 00000057065 13054774261 016414 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
abstract class Horde_Db_Adapter_Base implements Horde_Db_Adapter
{
/**
* Config options.
*
* @var array
*/
protected $_config = array();
/**
* DB connection.
*
* @var mixed
*/
protected $_connection = null;
/**
* Has a transaction been started?
*
* @var integer
*/
protected $_transactionStarted = 0;
/**
* The last query sent to the database.
*
* @var string
*/
protected $_lastQuery;
/**
* Row count of last action.
*
* @var integer
*/
protected $_rowCount = null;
/**
* Runtime of last query.
*
* @var integer
*/
protected $_runtime;
/**
* Is connection active?
*
* @var boolean
*/
protected $_active = null;
/**
* Cache object.
*
* @var Horde_Cache
*/
protected $_cache;
/**
* Cache prefix.
*
* @var string
*/
protected $_cachePrefix;
/**
* Log object.
*
* @var Horde_Log_Logger
*/
protected $_logger;
/**
* Schema object.
*
* @var Horde_Db_Adapter_Base_Schema
*/
protected $_schema = null;
/**
* Schema class to use.
*
* @var string
*/
protected $_schemaClass = null;
/**
* List of schema methods.
*
* @var array
*/
protected $_schemaMethods = array();
/**
* Log query flag
*
* @var boolean
*/
protected $_logQueries = false;
/*##########################################################################
# Construct/Destruct
##########################################################################*/
/**
* Constructor.
*
* @param array $config Configuration options and optional objects:
*
* 'charset' - (string) TODO
*
*/
public function __construct($config)
{
/* Can't set cache/logger in constructor - these objects may use DB
* for storage. Add stubs for now - they have to be manually set
* later with setCache() and setLogger(). */
$this->_cache = new Horde_Support_Stub();
$this->_logger = new Horde_Support_Stub();
// Default to UTF-8
if (!isset($config['charset'])) {
$config['charset'] = 'UTF-8';
}
$this->_config = $config;
$this->_runtime = 0;
if (!$this->_schemaClass) {
$this->_schemaClass = __CLASS__ . '_Schema';
}
$this->connect();
}
/**
* Free any resources that are open.
*/
public function __destruct()
{
$this->disconnect();
}
/**
* Serialize callback.
*/
public function __sleep()
{
return array_diff(array_keys(get_class_vars(__CLASS__)), array('_active', '_connection'));
}
/**
* Unserialize callback.
*/
public function __wakeup()
{
$this->_schema->setAdapter($this);
$this->connect();
}
/**
* Returns an adaptor option set through the constructor.
*
* @param string $option The option to return.
*
* @return mixed The option value or null if option doesn't exist or is
* not set.
*/
public function getOption($option)
{
return isset($this->_config[$option]) ? $this->_config[$option] : null;
}
/*##########################################################################
# Dependency setters/getters
##########################################################################*/
/**
* Set a cache object.
*
* @inject
*
* @param Horde_Cache $cache The cache object.
*/
public function setCache(Horde_Cache $cache)
{
$this->_cache = $cache;
}
/**
* @return Horde_Cache
*/
public function getCache()
{
return $this->_cache;
}
/**
* Set a logger object.
*
* @inject
*
* @var Horde_Log_Logger $logger The logger object.
* @var boolean $log_queries If true, logs all queries at DEBUG level.
* NOTE: Horde_Db_Value_Binary objects and stream resource values are
* NEVER included in the log output regardless of this setting.
* @since 2.4.0
*/
public function setLogger(Horde_Log_Logger $logger, $log_queries = false)
{
$this->_logger = $logger;
$this->_logQueries = $log_queries;
}
/**
* return Horde_Log_Logger
*/
public function getLogger()
{
return $this->_logger;
}
/*##########################################################################
# Object composition
##########################################################################*/
/**
* Delegate calls to the schema object.
*
* @param string $method
* @param array $args
*
* @return mixed TODO
* @throws BadMethodCallException
*/
public function __call($method, $args)
{
if (!$this->_schema) {
// Create the database-specific (but not adapter specific) schema
// object.
$this->_schema = new $this->_schemaClass($this, array(
'cache' => $this->_cache,
'logger' => $this->_logger
));
$this->_schemaMethods = array_flip(get_class_methods($this->_schema));
}
if (isset($this->_schemaMethods[$method])) {
return call_user_func_array(array($this->_schema, $method), $args);
}
$support = new Horde_Support_Backtrace();
$context = $support->getContext(1);
$caller = $context['function'];
if (isset($context['class'])) {
$caller = $context['class'] . '::' . $caller;
}
throw new BadMethodCallException('Call to undeclared method "' . get_class($this) . '::' . $method . '" from "' . $caller . '"');
}
/*##########################################################################
# Public
##########################################################################*/
/**
* Returns the human-readable name of the adapter. Use mixed case - one
* can always use downcase if needed.
*
* @return string
*/
public function adapterName()
{
return 'Base';
}
/**
* Does this adapter support migrations? Backend specific, as the
* abstract adapter always returns +false+.
*
* @return boolean
*/
public function supportsMigrations()
{
return false;
}
/**
* Does this adapter support using DISTINCT within COUNT? This is +true+
* for all adapters except sqlite.
*
* @return boolean
*/
public function supportsCountDistinct()
{
return true;
}
/**
* Does this adapter support using INTERVAL statements? This is +true+
* for all adapters except sqlite.
*
* @return boolean
*/
public function supportsInterval()
{
return true;
}
/**
* Should primary key values be selected from their corresponding
* sequence before the insert statement? If true, next_sequence_value
* is called before each insert to set the record's primary key.
* This is false for all adapters but Firebird.
*
* @deprecated
* @return boolean
*/
public function prefetchPrimaryKey($tableName = null)
{
return false;
}
/**
* Get the last query run
*
* @return string
*/
public function getLastQuery()
{
return $this->_lastQuery;
}
/**
* Reset the timer
*
* @return integer
*/
public function resetRuntime()
{
$this->_runtime = 0;
return $this->_runtime;
}
/**
* Writes values to the cache handler.
*
* The key is automatically prefixed to avoid collisions when using
* different adapters or different configurations.
*
* @since Horde_Db 2.1.0
*
* @param string $key A cache key.
* @param string $value A value.
*/
public function cacheWrite($key, $value)
{
$this->_cache->set($this->_cacheKey($key), $value);
}
/**
* Reads values from the cache handler.
*
* The key is automatically prefixed to avoid collisions when using
* different adapters or different configurations.
*
* @since Horde_Db 2.1.0
*
* @param string $key A cache key.
*
* @return string A value.
*/
public function cacheRead($key)
{
return $this->_cache->get($this->_cacheKey($key), 0);
}
/*##########################################################################
# Connection Management
##########################################################################*/
/**
* Is the connection active?
*
* @return boolean
*/
public function isActive()
{
return $this->_active && $this->_connection;
}
/**
* Reconnect to the db.
*/
public function reconnect()
{
$this->disconnect();
$this->connect();
}
/**
* Disconnect from db.
*/
public function disconnect()
{
$this->_connection = null;
$this->_active = false;
}
/**
* Provides access to the underlying database connection. Useful for when
* you need to call a proprietary method such as postgresql's
* lo_* methods.
*
* @return resource
*/
public function rawConnection()
{
return $this->_connection;
}
/*##########################################################################
# Database Statements
##########################################################################*/
/**
* Returns an array of record hashes with the column names as keys and
* column values as values.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return array
* @throws Horde_Db_Exception
*/
public function selectAll($sql, $arg1 = null, $arg2 = null)
{
$rows = array();
$result = $this->select($sql, $arg1, $arg2);
if ($result) {
foreach ($result as $row) {
$rows[] = $row;
}
}
return $rows;
}
/**
* Returns a record hash with the column names as keys and column values
* as values.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return array
* @throws Horde_Db_Exception
*/
public function selectOne($sql, $arg1 = null, $arg2 = null)
{
$result = $this->selectAll($sql, $arg1, $arg2);
return $result
? next($result)
: array();
}
/**
* Returns a single value from a record
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return string
* @throws Horde_Db_Exception
*/
public function selectValue($sql, $arg1 = null, $arg2 = null)
{
$result = $this->selectOne($sql, $arg1, $arg2);
return $result
? next($result)
: null;
}
/**
* Returns an array of the values of the first column in a select:
* selectValues("SELECT id FROM companies LIMIT 3") => [1,2,3]
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return array
* @throws Horde_Db_Exception
*/
public function selectValues($sql, $arg1 = null, $arg2 = null)
{
$result = $this->selectAll($sql, $arg1, $arg2);
$values = array();
foreach ($result as $row) {
$values[] = next($row);
}
return $values;
}
/**
* Returns an array where the keys are the first column of a select, and the
* values are the second column:
*
* selectAssoc("SELECT id, name FROM companies LIMIT 3") => [1 => 'Ford', 2 => 'GM', 3 => 'Chrysler']
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return array
* @throws Horde_Db_Exception
*/
public function selectAssoc($sql, $arg1 = null, $arg2 = null)
{
$result = $this->selectAll($sql, $arg1, $arg2);
$values = array();
foreach ($result as $row) {
$values[current($row)] = next($row);
}
return $values;
}
/**
* Inserts a row including BLOBs into a table.
*
* @since Horde_Db 2.1.0
*
* @param string $table The table name.
* @param array $fields A hash of column names and values. BLOB/CLOB
* columns must be provided as Horde_Db_Value
* objects.
* @param string $pk The primary key column.
* @param integer $idValue The primary key value. This parameter is
* required if the primary key is inserted
* manually.
*
* @return integer Last inserted ID.
* @throws Horde_Db_Exception
*/
public function insertBlob($table, $fields, $pk = null, $idValue = null)
{
$query = sprintf(
'INSERT INTO %s (%s) VALUES (%s)',
$this->quoteTableName($table),
implode(', ', array_map(array($this, 'quoteColumnName'), array_keys($fields))),
implode(', ', array_fill(0, count($fields), '?'))
);
return $this->insert($query, $fields, null, $pk, $idValue);
}
/**
* Executes the update statement and returns the number of rows affected.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return integer Number of rows affected.
* @throws Horde_Db_Exception
*/
public function update($sql, $arg1 = null, $arg2 = null)
{
$this->execute($sql, $arg1, $arg2);
return $this->_rowCount;
}
/**
* Updates rows including BLOBs into a table.
*
* @since Horde_Db 2.2.0
*
* @param string $table The table name.
* @param array $fields A hash of column names and values. BLOB/CLOB
* columns must be provided as
* Horde_Db_Value objects.
* @param string|array $where A WHERE clause. Either a complete clause or
* an array containing a clause with
* placeholders and a list of values.
*
* @throws Horde_Db_Exception
*/
public function updateBlob($table, $fields, $where = null)
{
if (is_array($where)) {
$where = $this->_replaceParameters($where[0], $where[1]);
}
$fnames = array();
foreach (array_keys($fields) as $field) {
$fnames[] = $this->quoteColumnName($field) . ' = ?';
}
$query = sprintf(
'UPDATE %s SET %s%s',
$this->quoteTableName($table),
implode(', ', $fnames),
strlen($where) ? ' WHERE ' . $where : ''
);
return $this->update($query, $fields);
}
/**
* Executes the delete statement and returns the number of rows affected.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return integer Number of rows affected.
* @throws Horde_Db_Exception
*/
public function delete($sql, $arg1 = null, $arg2 = null)
{
$this->execute($sql, $arg1, $arg2);
return $this->_rowCount;
}
/**
* Check if a transaction has been started.
*
* @return boolean True if transaction has been started.
*/
public function transactionStarted()
{
return (bool)$this->_transactionStarted;
}
/**
* Appends LIMIT and OFFSET options to a SQL statement.
*
* @param string $sql SQL statement.
* @param array $options Hash with 'limit' and (optional) 'offset' values.
*
* @return string
*/
public function addLimitOffset($sql, $options)
{
if (isset($options['limit']) && $limit = $options['limit']) {
if (isset($options['offset']) && $offset = $options['offset']) {
$sql .= " LIMIT $offset, $limit";
} else {
$sql .= " LIMIT $limit";
}
}
return $sql;
}
/**
* TODO
*/
public function sanitizeLimit($limit)
{
return (strpos($limit, ',') !== false)
? implode(',', array_map('intval', explode(',', $limit)))
: intval($limit);
}
/**
* Appends a locking clause to an SQL statement.
* This method *modifies* the +sql+ parameter.
*
* # SELECT * FROM suppliers FOR UPDATE
* add_lock! 'SELECT * FROM suppliers', :lock => true
* add_lock! 'SELECT * FROM suppliers', :lock => ' FOR UPDATE'
*
* @param string &$sql SQL statment.
* @param array $options TODO.
*/
public function addLock(&$sql, array $options = array())
{
$sql .= (isset($options['lock']) && is_string($options['lock']))
? ' ' . $options['lock']
: ' FOR UPDATE';
}
/**
* Inserts the given fixture into the table. Overridden in adapters that
* require something beyond a simple insert (eg. Oracle).
*
* @param TODO $fixture TODO
* @param TODO $tableName TODO
*
* @return TODO
*/
public function insertFixture($fixture, $tableName)
{
/*@TODO*/
return $this->execute("INSERT INTO #{quote_table_name(table_name)} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert');
}
/**
* TODO
*
* @param string $tableName TODO
*
* @return string TODO
*/
public function emptyInsertStatement($tableName)
{
return 'INSERT INTO ' . $this->quoteTableName($tableName) . ' VALUES(DEFAULT)';
}
/*##########################################################################
# Protected
##########################################################################*/
/**
* Checks if required configuration keys are present.
*
* @param array $required Required configuration keys.
*
* @throws Horde_Db_Exception if a required key is missing.
*/
protected function _checkRequiredConfig(array $required)
{
$diff = array_diff_key(array_flip($required), $this->_config);
if (!empty($diff)) {
$msg = 'Required config missing: ' . implode(', ', array_keys($diff));
throw new Horde_Db_Exception($msg);
}
}
/**
* Replace ? in a SQL statement with quoted values from $args
*
* @param string $sql SQL statement.
* @param array $args An array of values to bind.
* @param boolean $no_binary If true, do not replace any
* Horde_Db_Value_Binary values. Used for
* logging purposes.
*
* @return string Modified SQL statement.
* @throws Horde_Db_Exception
*/
protected function _replaceParameters($sql, array $args, $no_binary = false)
{
$paramCount = substr_count($sql, '?');
if (count($args) != $paramCount) {
$this->_logError('Parameter count mismatch: ' . $sql, 'Horde_Db_Adapter_Base::_replaceParameters');
throw new Horde_Db_Exception(sprintf('Parameter count mismatch, expecting %d, got %d', $paramCount, count($args)));
}
$sqlPieces = explode('?', $sql);
$sql = array_shift($sqlPieces);
while (count($sqlPieces)) {
$value = array_shift($args);
if ($no_binary && $value instanceof Horde_Db_Value_Binary) {
$sql_value = '';
} else {
$sql_value = $this->quote($value);
}
$sql .= $sql_value . array_shift($sqlPieces);
}
return $sql;
}
/**
* Logs the SQL query for debugging.
*
* @param string $sql SQL statement.
* @param array $values An array of values to substitute for placeholders.
* @param string $name Optional queryname.
* @param float $runtime Runtime interval.
*/
protected function _logInfo($sql, $values, $name = null, $runtime = null)
{
if (!$this->_logger || !$this->_logQueries) {
return;
}
if (is_array($values)) {
$sql = $this->_replaceParameters($sql, $values, true);
}
$name = (empty($name) ? '' : $name)
. (empty($runtime) ? '' : sprintf(" (%.4fs)", $runtime));
$this->_logger->debug($this->_formatLogEntry($name, $sql));
}
protected function _logError($error, $name, $runtime = null)
{
if ($this->_logger) {
$name = (empty($name) ? '' : $name)
. (empty($runtime) ? '' : sprintf(" (%.4fs)", $runtime));
$this->_logger->err($this->_formatLogEntry($name, $error));
}
}
/**
* Formats the log entry.
*
* @param string $message Message.
* @param string $sql SQL statment.
*
* @return string Formatted log entry.
*/
protected function _formatLogEntry($message, $sql)
{
return "SQL $message \n\t" . wordwrap(preg_replace("/\s+/", ' ', $sql), 70, "\n\t ", 1);
}
/**
* Returns the prefixed cache key to use.
*
* @param string $key A cache key.
*
* @return string Prefixed cache key.
*/
protected function _cacheKey($key)
{
if (!isset($this->_cachePrefix)) {
$this->_cachePrefix = get_class($this) . hash((version_compare(PHP_VERSION, '5.4', '>=')) ? 'fnv132' : 'sha1', serialize($this->_config));
}
return $this->_cachePrefix . $key;
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Mysql.php 0000664 0001750 0001750 00000030004 13054774261 016627 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* MySQL Horde_Db_Adapter
*
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Mysql extends Horde_Db_Adapter_Base
{
/**
* Last auto-generated insert_id
* @var integer
*/
protected $_insertId;
/**
* @var string
*/
protected $_schemaClass = 'Horde_Db_Adapter_Mysql_Schema';
/*##########################################################################
# Public
##########################################################################*/
/**
* Returns the human-readable name of the adapter. Use mixed case - one
* can always use downcase if needed.
*
* @return string
*/
public function adapterName()
{
return 'MySQL';
}
/**
* Does this adapter support migrations? Backend specific, as the
* abstract adapter always returns +false+.
*
* @return boolean
*/
public function supportsMigrations()
{
return true;
}
/*##########################################################################
# Connection Management
##########################################################################*/
/**
* Connect to the db
*/
public function connect()
{
if ($this->_active) {
return;
}
$config = $this->_parseConfig();
$mysql = @mysql_connect($config['host'], $config['username'], $config['password']);
if (!$mysql) {
$error = error_get_last();
throw new Horde_Db_Exception('Connect failed: ' . $error['message']);
}
if (!mysql_select_db($config['dbname'])) {
throw new Horde_Db_Exception('Could not select database: ' . $config['dbname']);
}
$this->_connection = $mysql;
$this->_active = true;
// Set the default charset. http://dev.mysql.com/doc/refman/5.1/en/charset-connection.html
if (!empty($config['charset'])) {
$this->setCharset($config['charset']);
}
}
/**
* Disconnect from db
*/
public function disconnect()
{
if ($this->_connection) {
@mysql_close($this->_connection);
}
parent::disconnect();
}
/**
* Check if the connection is active
*
* @return boolean
*/
public function isActive()
{
return isset($this->_connection) && @mysql_ping($this->_connection);
}
/*##########################################################################
# Quoting
##########################################################################*/
/**
* Quotes a string, escaping any ' (single quote) and \ (backslash)
* characters..
*
* @param string $string
* @return string
*/
public function quoteString($string)
{
return "'" . mysql_real_escape_string($string, $this->_connection) . "'";
}
/*##########################################################################
# Database Statements
##########################################################################*/
/**
* Returns an array of records with the column names as keys, and
* column values as values.
*
* @param string $sql
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
* @return Horde_Db_Adapter_Mysql_Result
*/
public function select($sql, $arg1 = null, $arg2 = null)
{
return new Horde_Db_Adapter_Mysql_Result($this, $sql, $arg1, $arg2);
}
/**
* Returns an array of record hashes with the column names as keys and
* column values as values.
*
* @param string $sql
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
*/
public function selectAll($sql, $arg1 = null, $arg2 = null)
{
$result = $this->execute($sql, $arg1, $arg2);
$rows = array();
if ($result) {
while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
$rows[] = $row;
}
}
return $rows;
}
/**
* Returns a record hash with the column names as keys and column values as
* values.
*
* @param string $sql A query.
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
*
* @return array|boolean A record hash or false if no record found.
*/
public function selectOne($sql, $arg1 = null, $arg2 = null)
{
$result = $this->execute($sql, $arg1, $arg2);
return $result ? mysql_fetch_array($result, MYSQL_ASSOC) : array();
}
/**
* Returns a single value from a record
*
* @param string $sql
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
* @return string
*/
public function selectValue($sql, $arg1=null, $arg2=null)
{
$result = $this->selectOne($sql, $arg1, $arg2);
return $result ? current($result) : null;
}
/**
* Returns an array of the values of the first column in a select:
* select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
*
* @param string $sql
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
*/
public function selectValues($sql, $arg1=null, $arg2=null)
{
$values = array();
$result = $this->execute($sql, $arg1, $arg2);
if ($result) {
while ($row = mysql_fetch_row($result)) {
$values[] = $row[0];
}
}
return $values;
}
/**
* Executes the SQL statement in the context of this connection.
*
* @deprecated Deprecated for external usage. Use select() instead.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return resource
* @throws Horde_Db_Exception
*/
public function execute($sql, $arg1=null, $arg2=null)
{
if (is_array($arg1)) {
$query = $this->_replaceParameters($sql, $arg1);
$name = $arg2;
} else {
$query = $sql;
$name = $arg1;
$arg1 = array();
}
$t = new Horde_Support_Timer();
$t->push();
$this->_lastQuery = $query;
$stmt = mysql_query($query, $this->_connection);
if (!$stmt) {
$this->_logInfo($sql, $arg1, $name);
$this->_logError($query, 'QUERY FAILED: ' . mysql_error($this->_connection));
throw new Horde_Db_Exception('QUERY FAILED: ' . mysql_error($this->_connection) . "\n\n" . $query,
$this->_errorCode(null, mysql_errno($this->_connection)));
}
$this->_logInfo($sql, $arg1, $name, $t->pop());
$this->_rowCount = mysql_affected_rows($this->_connection);
$this->_insertId = mysql_insert_id($this->_connection);
return $stmt;
}
/**
* Returns the last auto-generated ID from the affected table.
*
* @param string $sql
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
* @param string $pk
* @param int $idValue
* @param string $sequenceName
*/
public function insert($sql, $arg1 = null, $arg2 = null, $pk = null, $idValue = null, $sequenceName = null)
{
$this->execute($sql, $arg1, $arg2);
return isset($idValue) ? $idValue : $this->_insertId;
}
/**
* Begins the transaction (and turns off auto-committing).
*/
public function beginDbTransaction()
{
$this->_transactionStarted++;
$this->_lastQuery = 'SET AUTOCOMMIT=0; BEGIN';
@mysql_query('SET AUTOCOMMIT=0', $this->_connection) && @mysql_query('BEGIN', $this->_connection);
}
/**
* Commits the transaction (and turns on auto-committing).
*/
public function commitDbTransaction()
{
$this->_transactionStarted--;
if (!$this->_transactionStarted) {
$this->_lastQuery = 'COMMIT; SET AUTOCOMMIT=1';
@mysql_query('COMMIT', $this->_connection) && @mysql_query('SET AUTOCOMMIT=1', $this->_connection);
}
}
/**
* Rolls back the transaction (and turns on auto-committing). Must be
* done if the transaction block raises an exception or returns false.
*/
public function rollbackDbTransaction()
{
if (!$this->_transactionStarted) {
return;
}
$this->_lastQuery = 'ROLLBACK; SET AUTOCOMMIT=1';
@mysql_query('ROLLBACK', $this->_connection) && @mysql_query('SET AUTOCOMMIT=1', $this->_connection);
$this->_transactionStarted = 0;
}
/*##########################################################################
# Protected
##########################################################################*/
/**
* Return a standard error code
*
* @param string $sqlstate
* @param integer $errno
* @return integer
*/
protected function _errorCode($sqlstate, $errno)
{
/*@TODO do something with standard sqlstate vs. MySQL error codes vs. whatever else*/
return $errno;
}
/**
* Parse configuration array into options for mysql_connect
*
* @throws Horde_Db_Exception
* @return array [host, username, password, dbname]
*/
protected function _parseConfig()
{
$this->_checkRequiredConfig(array('username'));
$rails2mysqli = array('database' => 'dbname');
foreach ($rails2mysqli as $from => $to) {
if (isset($this->_config[$from])) {
$this->_config[$to] = $this->_config[$from];
unset($this->_config[$from]);
}
}
if (!empty($this->_config['host']) &&
$this->_config['host'] == 'localhost') {
$this->_config['host'] = '127.0.0.1';
}
if (isset($this->_config['port'])) {
if (empty($this->_config['host'])) {
throw new Horde_Db_Exception('Host is required if port is specified');
}
$this->_config['host'] .= ':' . $this->_config['port'];
unset($this->_config['port']);
}
if (!empty($this->_config['socket'])) {
if (!empty($this->_config['host'])) {
throw new Horde_Db_Exception('Can only specify host or socket, not both');
}
$this->_config['host'] = ':' . $this->_config['socket'];
unset($this->_config['socket']);
}
$config = $this->_config;
if (!isset($config['host'])) $config['host'] = null;
if (!isset($config['username'])) $config['username'] = null;
if (!isset($config['password'])) $config['password'] = null;
if (!isset($config['dbname'])) $config['dbname'] = null;
return $config;
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Mysqli.php 0000664 0001750 0001750 00000034247 13054774261 017015 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* MySQL Improved Horde_Db_Adapter
*
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Mysqli extends Horde_Db_Adapter_Base
{
/**
* Last auto-generated insert_id
* @var integer
*/
protected $_insertId;
/**
* @var string
*/
protected $_schemaClass = 'Horde_Db_Adapter_Mysql_Schema';
/**
* @var boolean
*/
protected $_hasMysqliFetchAll = false;
/*##########################################################################
# Public
##########################################################################*/
/**
* Returns the human-readable name of the adapter. Use mixed case - one
* can always use downcase if needed.
*
* @return string
*/
public function adapterName()
{
return 'MySQLi';
}
/**
* Does this adapter support migrations? Backend specific, as the
* abstract adapter always returns +false+.
*
* @return boolean
*/
public function supportsMigrations()
{
return true;
}
/*##########################################################################
# Connection Management
##########################################################################*/
/**
* Connect to the db
*
* MySQLi can connect using SSL if $config contains an 'ssl' sub-array
* containing the following keys:
* + key The path to the key file.
* + cert The path to the certificate file.
* + ca The path to the certificate authority file.
* + capath The path to a directory that contains trusted SSL
* CA certificates in pem format.
* + cipher The list of allowable ciphers for SSL encryption.
*
* Example of how to connect using SSL:
*
* $config = array(
* 'username' => 'someuser',
* 'password' => 'apasswd',
* 'hostspec' => 'localhost',
* 'database' => 'thedb',
* 'ssl' => array(
* 'key' => 'client-key.pem',
* 'cert' => 'client-cert.pem',
* 'ca' => 'cacert.pem',
* 'capath' => '/path/to/ca/dir',
* 'cipher' => 'AES',
* ),
* );
*
* $db = new Horde_Db_Adapter_Mysqli($config);
*
*/
public function connect()
{
if ($this->_active) {
return;
}
$config = $this->_parseConfig();
if (!empty($config['ssl'])) {
$mysqli = mysqli_init();
$mysqli->ssl_set(
empty($config['ssl']['key']) ? null : $config['ssl']['key'],
empty($config['ssl']['cert']) ? null : $config['ssl']['cert'],
empty($config['ssl']['ca']) ? null : $config['ssl']['ca'],
empty($config['ssl']['capath']) ? null : $config['ssl']['capath'],
empty($config['ssl']['cipher']) ? null : $config['ssl']['cipher']
);
$mysqli->real_connect(
$config['host'], $config['username'], $config['password'],
$config['dbname'], $config['port'], $config['socket']);
} else {
$mysqli = new mysqli(
$config['host'], $config['username'], $config['password'],
$config['dbname'], $config['port'], $config['socket']);
}
if (mysqli_connect_errno()) {
throw new Horde_Db_Exception('Connect failed: (' . mysqli_connect_errno() . ') ' . mysqli_connect_error(), mysqli_connect_errno());
}
// If supported, request real datatypes from MySQL instead of returning
// everything as a string.
if (defined('MYSQLI_OPT_INT_AND_FLOAT_NATIVE')) {
$mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, true);
}
$this->_connection = $mysqli;
$this->_active = true;
// Set the default charset. http://dev.mysql.com/doc/refman/5.1/en/charset-connection.html
if (!empty($config['charset'])) {
$this->setCharset($config['charset']);
}
$this->_hasMysqliFetchAll = function_exists('mysqli_fetch_all');
}
/**
* Disconnect from db
*/
public function disconnect()
{
if ($this->_connection) {
$this->_connection->close();
}
parent::disconnect();
}
/**
* Check if the connection is active
*
* @return boolean
*/
public function isActive()
{
$this->_lastQuery = 'SELECT 1';
return isset($this->_connection) && $this->_connection->query('SELECT 1');
}
/*##########################################################################
# Quoting
##########################################################################*/
/**
* Quotes a string, escaping any ' (single quote) and \ (backslash)
* characters..
*
* @param string $string
* @return string
*/
public function quoteString($string)
{
return "'".$this->_connection->real_escape_string($string)."'";
}
/*##########################################################################
# Database Statements
##########################################################################*/
/**
* Returns an array of records with the column names as keys, and
* column values as values.
*
* @param string $sql
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
* @return Horde_Db_Adapter_Mysqli_Result
*/
public function select($sql, $arg1=null, $arg2=null)
{
return new Horde_Db_Adapter_Mysqli_Result($this, $sql, $arg1, $arg2);
}
/**
* Returns an array of record hashes with the column names as keys and
* column values as values.
*
* @param string $sql
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
*/
public function selectAll($sql, $arg1=null, $arg2=null)
{
$result = $this->execute($sql, $arg1, $arg2);
if ($this->_hasMysqliFetchAll) {
return $result->fetch_all(MYSQLI_ASSOC);
} else {
$rows = array();
if ($result) {
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
$rows[] = $row;
}
}
}
return $rows;
}
/**
* Returns a record hash with the column names as keys and column values as
* values.
*
* @param string $sql A query.
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
*
* @return array|boolean A record hash or false if no record found.
*/
public function selectOne($sql, $arg1 = null, $arg2 = null)
{
$result = $this->execute($sql, $arg1, $arg2);
$result = $result ? $result->fetch_array(MYSQLI_ASSOC) : array();
return is_null($result) ? false : $result;
}
/**
* Returns a single value from a record
*
* @param string $sql
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
* @return string
*/
public function selectValue($sql, $arg1=null, $arg2=null)
{
$result = $this->selectOne($sql, $arg1, $arg2);
return $result ? current($result) : null;
}
/**
* Returns an array of the values of the first column in a select:
* select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
*
* @param string $sql
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
*/
public function selectValues($sql, $arg1=null, $arg2=null)
{
$values = array();
$result = $this->execute($sql, $arg1, $arg2);
if ($result) {
while ($row = $result->fetch_row()) {
$values[] = $row[0];
}
}
return $values;
}
/**
* Executes the SQL statement in the context of this connection.
*
* @deprecated Deprecated for external usage. Use select() instead.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return mysqli_result
* @throws Horde_Db_Exception
*/
public function execute($sql, $arg1=null, $arg2=null)
{
if (is_array($arg1)) {
$query = $this->_replaceParameters($sql, $arg1);
$name = $arg2;
} else {
$name = $arg1;
$query = $sql;
$arg1 = array();
}
$t = new Horde_Support_Timer();
$t->push();
$this->_lastQuery = $query;
$stmt = $this->_connection->query($query);
if (!$stmt) {
$this->_logInfo($sql, $arg1, $name);
$this->_logError($query, 'QUERY FAILED: ' . $this->_connection->error);
throw new Horde_Db_Exception('QUERY FAILED: ' . $this->_connection->error . "\n\n" . $query,
$this->_errorCode($this->_connection->sqlstate, $this->_connection->errno));
}
$this->_logInfo($sql, $arg1, $name, $t->pop());
//@TODO if ($this->_connection->info) $this->_loginfo($sql, $this->_connection->info);
//@TODO also log warnings? http://php.net/mysqli.warning-count and http://php.net/mysqli.get-warnings
$this->_rowCount = $this->_connection->affected_rows;
$this->_insertId = $this->_connection->insert_id;
return $stmt;
}
/**
* Returns the last auto-generated ID from the affected table.
*
* @param string $sql
* @param mixed $arg1 Either an array of bound parameters or a query name.
* @param string $arg2 If $arg1 contains bound parameters, the query name.
* @param string $pk
* @param int $idValue
* @param string $sequenceName
*/
public function insert($sql, $arg1=null, $arg2=null, $pk=null, $idValue=null, $sequenceName=null)
{
$this->execute($sql, $arg1, $arg2);
return isset($idValue) ? $idValue : $this->_insertId;
}
/**
* Begins the transaction (and turns off auto-committing).
*/
public function beginDbTransaction()
{
$this->_connection->autocommit(false);
$this->_transactionStarted++;
}
/**
* Commits the transaction (and turns on auto-committing).
*/
public function commitDbTransaction()
{
$this->_transactionStarted--;
if (!$this->_transactionStarted) {
$this->_connection->commit();
$this->_connection->autocommit(true);
}
}
/**
* Rolls back the transaction (and turns on auto-committing). Must be
* done if the transaction block raises an exception or returns false.
*/
public function rollbackDbTransaction()
{
if (!$this->_transactionStarted) {
return;
}
$this->_connection->rollback();
$this->_transactionStarted = 0;
$this->_connection->autocommit(true);
}
/*##########################################################################
# Protected
##########################################################################*/
/**
* Return a standard error code
*
* @param string $sqlstate
* @param integer $errno
* @return integer
*/
protected function _errorCode($sqlstate, $errno)
{
/*@TODO do something with standard sqlstate vs. MySQL error codes vs. whatever else*/
return $errno;
}
/**
* Parse configuration array into options for MySQLi constructor.
*
* @throws Horde_Db_Exception
* @return array [host, username, password, dbname, port, socket]
*/
protected function _parseConfig()
{
$this->_checkRequiredConfig(array('username'));
$rails2mysqli = array('database' => 'dbname');
foreach ($rails2mysqli as $from => $to) {
if (isset($this->_config[$from])) {
$this->_config[$to] = $this->_config[$from];
unset($this->_config[$from]);
}
}
if (!empty($this->_config['host']) &&
$this->_config['host'] == 'localhost') {
$this->_config['host'] = '127.0.0.1';
}
if (!empty($this->_config['host']) && !empty($this->_config['socket'])) {
throw new Horde_Db_Exception('Can only specify host or socket, not both');
}
if (isset($this->_config['port'])) {
if (empty($this->_config['host'])) {
throw new Horde_Db_Exception('Host is required if port is specified');
}
}
$config = $this->_config;
if (!isset($config['host'])) $config['host'] = null;
if (!isset($config['username'])) $config['username'] = null;
if (!isset($config['password'])) $config['password'] = null;
if (!isset($config['dbname'])) $config['dbname'] = null;
if (!isset($config['port'])) $config['port'] = null;
if (!isset($config['socket'])) $config['socket'] = null;
return $config;
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/Oci8.php 0000664 0001750 0001750 00000047703 13054774261 016342 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* @since Horde_Db 2.1.0
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_Oci8 extends Horde_Db_Adapter_Base
{
/**
* Schema class to use.
*
* @var string
*/
protected $_schemaClass = 'Horde_Db_Adapter_Oracle_Schema';
/*#########################################################################
# Public
#########################################################################*/
/**
* Returns the human-readable name of the adapter. Use mixed case - one
* can always use downcase if needed.
*
* @return string
*/
public function adapterName()
{
return 'Oracle';
}
/**
* Does this adapter support migrations?
*
* @return boolean
*/
public function supportsMigrations()
{
return true;
}
/*#########################################################################
# Connection Management
#########################################################################*/
/**
* Connect to the db
*/
public function connect()
{
if ($this->_active) {
return;
}
$this->_checkRequiredConfig(array('username'));
if (!isset($this->_config['tns']) && empty($this->_config['host'])) {
throw new Horde_Db_Exception('Either a TNS name or a host name must be specified');
}
if (isset($this->_config['tns'])) {
$connection = $this->_config['tns'];
} else {
$connection = $this->_config['host'];
if (!empty($this->_config['port'])) {
$connection .= ':' . $this->_config['port'];
}
if (!empty($this->_config['service'])) {
$connection .= '/' . $this->_config['service'];
}
if (!empty($this->_config['type'])) {
$connection .= ':' . $this->_config['type'];
}
if (!empty($this->_config['instance'])) {
$connection .= '/' . $this->_config['instance'];
}
}
$oci = oci_connect(
$this->_config['username'],
isset($this->_config['password']) ? $this->_config['password'] : '',
$connection,
$this->_oracleCharsetName($this->_config['charset'])
);
if (!$oci) {
if ($error = oci_error()) {
throw new Horde_Db_Exception(
sprintf(
'Connect failed: (%d) %s',
$error['code'],
$error['message']
),
$error['code']
);
} else {
throw new Horde_Db_Exception('Connect failed');
}
}
$this->_connection = $oci;
$this->_active = true;
$this->execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'");
}
/**
* Disconnect from db
*/
public function disconnect()
{
if ($this->_connection) {
oci_close($this->_connection);
}
parent::disconnect();
}
/*#########################################################################
# Quoting
#########################################################################*/
/**
* Quotes a string, escaping any special characters.
*
* @param string $string
* @return string
*/
public function quoteString($string)
{
return "'" . str_replace("'", "''", $string) . "'";
}
/*#########################################################################
# Database Statements
#########################################################################*/
/**
* Returns an array of records with the column names as keys, and
* column values as values.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return Horde_Db_Adapter_Oracle_Result
* @throws Horde_Db_Exception
*/
public function select($sql, $arg1 = null, $arg2 = null)
{
return new Horde_Db_Adapter_Oracle_Result($this, $sql, $arg1, $arg2);
}
/**
* Returns an array of record hashes with the column names as keys and
* column values as values.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return array
* @throws Horde_Db_Exception
*/
public function selectAll($sql, $arg1 = null, $arg2 = null)
{
$stmt = $this->execute($sql, $arg1, $arg2);
$result = oci_fetch_all($stmt, $rows, 0, -1, OCI_FETCHSTATEMENT_BY_ROW);
if ($result === false) {
$this->_handleError($stmt, 'selectAll');
}
foreach ($rows as &$row) {
$row = array_change_key_case($row, CASE_LOWER);
}
return $rows;
}
/**
* Returns a record hash with the column names as keys and column values
* as values.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return array
* @throws Horde_Db_Exception
*/
public function selectOne($sql, $arg1 = null, $arg2 = null)
{
if ($row = oci_fetch_assoc($this->execute($sql, $arg1, $arg2))) {
return array_change_key_case(
$row,
CASE_LOWER
);
}
}
/**
* Returns a single value from a record
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return string
* @throws Horde_Db_Exception
*/
public function selectValue($sql, $arg1 = null, $arg2 = null)
{
$stmt = $this->execute($sql, $arg1, $arg2);
if (!oci_fetch($stmt)) {
return;
}
if (($result = oci_result($stmt, 1)) === false) {
$this->_handleError($stmt, 'selectValue');
}
return $result;
}
/**
* Returns an array of the values of the first column in a select:
* selectValues("SELECT id FROM companies LIMIT 3") => [1,2,3]
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return array
* @throws Horde_Db_Exception
*/
public function selectValues($sql, $arg1 = null, $arg2 = null)
{
$stmt = $this->execute($sql, $arg1, $arg2);
$values = array();
while (oci_fetch($stmt)) {
if (($result = oci_result($stmt, 1)) === false) {
$this->_handleError($stmt, 'selectValues');
}
$values[] = $result;
}
return $values;
}
/**
* Executes the SQL statement in the context of this connection.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return resource
* @throws Horde_Db_Exception
*/
public function execute($sql, $arg1 = null, $arg2 = null, $lobs = array())
{
if (is_array($arg1)) {
$query = $this->_replaceParameters($sql, $arg1);
$name = $arg2;
} else {
$name = $arg1;
$query = $sql;
$arg1 = array();
}
$t = new Horde_Support_Timer;
$t->push();
$this->_lastQuery = $query;
$stmt = @oci_parse($this->_connection, $query);
$descriptors = array();
foreach ($lobs as $name => $lob) {
$descriptors[$name] = oci_new_descriptor($this->_connection, OCI_DTYPE_LOB);
oci_bind_by_name($stmt, ':' . $name, $descriptors[$name], -1, $lob instanceof Horde_Db_Value_Text ? OCI_B_CLOB : OCI_B_BLOB);
}
$flags = $lobs
? OCI_DEFAULT
: ($this->_transactionStarted
? OCI_NO_AUTO_COMMIT
: OCI_COMMIT_ON_SUCCESS
);
if (!$stmt ||
!@oci_execute($stmt, $flags)) {
$error = oci_error($stmt ?: $this->_connection);
if ($stmt) {
oci_free_statement($stmt);
}
$this->_logInfo($sql, $arg1, $name);
$this->_logError($query, 'QUERY FAILED: ' . $error['message']);
throw new Horde_Db_Exception(
$this->_errorMessage($error),
$error['code']
);
}
foreach ($lobs as $name => $lob) {
$stream = $lob->stream;
rewind($stream);
while (!feof($stream)) {
$descriptors[$name]->write(fread($stream, 8192));
}
}
if ($lobs) {
oci_commit($this->_connection);
}
$this->_logInfo($sql, $arg1, $name, $t->pop());
$this->_rowCount = oci_num_rows($stmt);
return $stmt;
}
/**
* Inserts a row into a table.
*
* @param string $sql SQL statement.
* @param array|string $arg1 Either an array of bound parameters or a
* query name.
* @param string $arg2 If $arg1 contains bound parameters, the
* query name.
* @param string $pk The primary key column.
* @param integer $idValue The primary key value. This parameter is
* required if the primary key is inserted
* manually.
* @param string $sequenceName The sequence name.
*
* @return integer Last inserted ID.
* @throws Horde_Db_Exception
*/
public function insert($sql, $arg1 = null, $arg2 = null, $pk = null,
$idValue = null, $sequenceName = null)
{
$this->execute($sql, $arg1, $arg2);
return $idValue
? $idValue
: $this->selectValue('SELECT id FROM horde_db_autoincrement');
}
/**
* Inserts a row including BLOBs into a table.
*
* @since Horde_Db 2.1.0
*
* @param string $table The table name.
* @param array $fields A hash of column names and values. BLOB/CLOB
* columns must be provided as Horde_Db_Value
* objects.
* @param string $pk The primary key column.
* @param integer $idValue The primary key value. This parameter is
* required if the primary key is inserted
* manually.
*
* @return integer Last inserted ID.
* @throws Horde_Db_Exception
*/
public function insertBlob($table, $fields, $pk = null, $idValue = null)
{
list($fields, $blobs, $locators) = $this->_prepareBlobs($fields);
$sql = 'INSERT INTO ' . $this->quoteTableName($table) . ' ('
. implode(
', ',
array_map(array($this, 'quoteColumnName'), array_keys($fields))
)
. ') VALUES (' . implode(', ', $fields) . ')';
// Protect against empty values being passed for blobs.
if (!empty($blobs)) {
$sql .= ' RETURNING ' . implode(', ', array_keys($blobs)) . ' INTO '
. implode(', ', $locators);
}
$this->execute($sql, null, null, $blobs);
return $idValue
? $idValue
: $this->selectValue('SELECT id FROM horde_db_autoincrement');
}
/**
* Updates rows including BLOBs into a table.
*
* @since Horde_Db 2.2.0
*
* @param string $table The table name.
* @param array $fields A hash of column names and values. BLOB/CLOB
* columns must be provided as
* Horde_Db_Value objects.
* @param string|array $where A WHERE clause. Either a complete clause or
* an array containing a clause with
* placeholders and a list of values.
*
* @throws Horde_Db_Exception
*/
public function updateBlob($table, $fields, $where = null)
{
list($fields, $blobs, $locators) = $this->_prepareBlobs($fields);
if (is_array($where)) {
$where = $this->_replaceParameters($where[0], $where[1]);
}
$fnames = array();
foreach ($fields as $field => $value) {
$fnames[] = $this->quoteColumnName($field) . ' = ' . $value;
}
$sql = sprintf(
'UPDATE %s SET %s%s',
$this->quoteTableName($table),
implode(', ', $fnames),
strlen($where) ? ' WHERE ' . $where : ''
);
// Protect against empty values for blobs.
if (!empty($blobs)) {
$sql .= sprintf(' RETURNING %s INTO %s',
implode(', ', array_keys($blobs)),
implode(', ', $locators)
);
}
$this->execute($sql, null, null, $blobs);
return $this->_rowCount;
}
/**
* Prepares a list of field values to be consumed by insertBlob() or
* updateBlob().
*
* @param array $fields A hash of column names and values. BLOB/CLOB
* columns must be provided as Horde_Db_Value objects.
*
* @return array A list of fields, blobs, and locators.
*/
protected function _prepareBlobs($fields)
{
$blobs = $locators = array();
foreach ($fields as $column => &$field) {
if ($field instanceof Horde_Db_Value_Binary ||
$field instanceof Horde_Db_Value_Text) {
$blobs[$this->quoteColumnName($column)] = $field;
$locators[] = ':' . $this->quoteColumnName($column);
$field = $field instanceof Horde_Db_Value_Text
? 'EMPTY_CLOB()'
: 'EMPTY_BLOB()';
} else {
$field = $this->quote($field);
}
}
return array($fields, $blobs, $locators);
}
/**
* Begins the transaction (and turns off auto-committing).
*/
public function beginDbTransaction()
{
$this->_transactionStarted++;
}
/**
* Commits the transaction (and turns on auto-committing).
*/
public function commitDbTransaction()
{
$this->_transactionStarted--;
if (!$this->_transactionStarted) {
if (!oci_commit($this->_connection)) {
$this->_handleError($this->_connection, 'commitDbTransaction');
}
}
}
/**
* Rolls back the transaction (and turns on auto-committing). Must be
* done if the transaction block raises an exception or returns false.
*/
public function rollbackDbTransaction()
{
if (!$this->_transactionStarted) {
return;
}
$this->_transactionStarted = 0;
if (!oci_rollback($this->_connection)) {
$this->_handleError($this->_connection, 'rollbackDbTransaction');
}
}
/**
* Appends LIMIT and OFFSET options to a SQL statement.
*
* @param string $sql SQL statement.
* @param array $options Hash with 'limit' and (optional) 'offset' values.
*
* @return string
*/
public function addLimitOffset($sql, $options)
{
if (isset($options['limit'])) {
$offset = isset($options['offset']) ? $options['offset'] : 0;
$limit = $options['limit'] + $offset;
if ($limit) {
$sql = "SELECT a.*, ROWNUM rnum FROM ($sql) a WHERE ROWNUM <= $limit";
if ($offset) {
$sql = "SELECT * FROM ($sql) WHERE rnum > $offset";
}
}
}
return $sql;
}
/*#########################################################################
# Protected
#########################################################################*/
/**
* Returns the Oracle name of a character set.
*
* @param string $charset A charset name.
*
* @return string Oracle-normalized charset.
*/
public function _oracleCharsetName($charset)
{
return str_replace(
array(
'iso-8859-1',
'iso-8859-2',
'iso-8859-4',
'iso-8859-5',
'iso-8859-6',
'iso-8859-7',
'iso-8859-8',
'iso-8859-9',
'iso-8859-10',
'iso-8859-13',
'iso-8859-15',
'shift_jis',
'shift-jis',
'windows-949',
'windows-950',
'windows-1250',
'windows-1251',
'windows-1252',
'windows-1253',
'windows-1254',
'windows-1255',
'windows-1256',
'windows-1257',
'windows-1258',
'utf-8',
),
array(
'WE8ISO8859P1',
'EE8ISO8859P2',
'NEE8ISO8859P4',
'CL8ISO8859P5',
'AR8ISO8859P6',
'EL8ISO8859P7',
'IW8ISO8859P8',
'WE8ISO8859P9',
'NE8ISO8859P10',
'BLT8ISO8859P13',
'WE8ISO8859P15',
'JA16SJIS',
'JA16SJIS',
'KO16MSWIN949',
'ZHT16MSWIN950',
'EE8MSWIN1250',
'CL8MSWIN1251',
'WE8MSWIN1252',
'EL8MSWIN1253',
'TR8MSWIN1254',
'IW8MSWIN1255',
'AR8MSWIN1256',
'BLT8MSWIN1257',
'VN8MSWIN1258',
'AL32UTF8',
),
Horde_String::lower($charset)
);
}
/**
* Creates a formatted error message from a oci_error() result hash.
*
* @param array $error Hash returned from oci_error().
*
* @return string The formatted error message.
*/
protected function _errorMessage($error)
{
return 'QUERY FAILED: ' . $error['message']
. "\n\nat offset " . $error['offset']
. "\n" . $error['sqltext'];
}
/**
* Log and throws an exception for the last error.
*
* @param resource $resource The resource (connection or statement) to
* call oci_error() upon.
* @param string $method The calling method.
*
* @throws Horde_Db_Exception
*/
protected function _handleError($resource, $method)
{
$error = oci_error($resource);
$this->_logError(
$error['message'],
'Horde_Db_Adapter_Oci8::' . $method. '()'
);
throw new Horde_Db_Exception(
$this->_errorMessage($error),
$error['code']
);
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter/SplitRead.php 0000664 0001750 0001750 00000040543 13054774261 017422 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Michael J. Rubinsky
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* The Horde_Db_Adapter_SplitRead:: class wraps two individual adapters to
* provide support for split read/write database setups.
*
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Michael J. Rubinsky
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
class Horde_Db_Adapter_SplitRead implements Horde_Db_Adapter
{
/**
* The read adapter
*
* @var Horde_Db_Adapter
*/
private $_read;
/**
* The write adapter
*
* @var Horde_Db_Adapter
*/
private $_write;
/**
* Const'r
*
* @param Horde_Db_Adapter $read
* @param Horde_Db_Adapter $write
*/
public function __construct(Horde_Db_Adapter $read, Horde_Db_Adapter $write)
{
$this->_read = $read;
$this->_write = $write;
}
/**
* Delegate unknown methods to the _write adapter.
*
* @param string $method
* @param array $args
*/
public function __call($method, $args)
{
$result = call_user_func_array(array($this->_write, $method), $args);
$this->_lastQuery = $this->_write->getLastQuery();
return $result;
}
/**
* Returns the human-readable name of the adapter. Use mixed case - one
* can always use downcase if needed.
*
* @return string
*/
public function adapterName()
{
return 'SplitRead';
}
/**
* Does this adapter support migrations?
*
* @return boolean
*/
public function supportsMigrations()
{
return $this->_write->supportsMigrations();
}
/**
* Does this adapter support using DISTINCT within COUNT? This is +true+
* for all adapters except sqlite.
*
* @return boolean
*/
public function supportsCountDistinct()
{
return $this->_read->supportsCountDistinct();
}
/**
* Should primary key values be selected from their corresponding
* sequence before the insert statement? If true, next_sequence_value
* is called before each insert to set the record's primary key.
* This is false for all adapters but Firebird.
*
* @return boolean
*/
public function prefetchPrimaryKey($tableName = null)
{
return $this->_write->prefetchPrimaryKey($tableName);
}
/*##########################################################################
# Connection Management
##########################################################################*/
/**
* Connect to the db.
* @TODO: Lazy connect?
*
*/
public function connect()
{
$this->_write->connect();
$this->_read->connect();
}
/**
* Is the connection active?
*
* @return boolean
*/
public function isActive()
{
return ($this->_read->isActive() && $this->_write->isActive());
}
/**
* Reconnect to the db.
*/
public function reconnect()
{
$this->disconnect();
$this->connect();
}
/**
* Disconnect from db.
*/
public function disconnect()
{
$this->_read->disconnect();
$this->_write->disconnect();
}
/**
* Provides access to the underlying database connection. Useful for when
* you need to call a proprietary method such as postgresql's
* lo_* methods.
*
* @return resource
*/
public function rawConnection()
{
return $this->_write->rawConnection();
}
/*##########################################################################
# Quoting
##########################################################################*/
/**
* Quotes a string, escaping any special characters.
*
* @param string $string
* @return string
*/
public function quoteString($string)
{
return $this->_read->quoteString($string);
}
/*##########################################################################
# Database Statements
##########################################################################*/
/**
* Returns an array of records with the column names as keys, and
* column values as values.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return PDOStatement
* @throws Horde_Db_Exception
*/
public function select($sql, $arg1 = null, $arg2 = null)
{
$result = $this->_read->select($sql, $arg1, $arg2);
$this->_lastQuery = $this->_read->getLastQuery();
return $result;
}
/**
* Returns an array of record hashes with the column names as keys and
* column values as values.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return array
* @throws Horde_Db_Exception
*/
public function selectAll($sql, $arg1 = null, $arg2 = null)
{
$result = $this->_read->selectAll($sql, $arg1, $arg2);
$this->_lastQuery = $this->_read->getLastQuery();
return $result;
}
/**
* Returns a record hash with the column names as keys and column values
* as values.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return array
* @throws Horde_Db_Exception
*/
public function selectOne($sql, $arg1 = null, $arg2 = null)
{
$result = $this->_read->selectOne($sql, $arg1, $arg2);
$this->_lastQuery = $this->_read->getLastQuery();
return $result;
}
/**
* Returns a single value from a record
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return string
* @throws Horde_Db_Exception
*/
public function selectValue($sql, $arg1 = null, $arg2 = null)
{
$result = $this->_read->selectValue($sql, $arg1, $arg2);
$this->_lastQuery = $this->_read->getLastQuery();
return $result;
}
/**
* Returns an array of the values of the first column in a select:
* selectValues("SELECT id FROM companies LIMIT 3") => [1,2,3]
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return array
* @throws Horde_Db_Exception
*/
public function selectValues($sql, $arg1 = null, $arg2 = null)
{
$result = $this->_read->selectValues($sql, $arg1, $arg2);
$this->_lastQuery = $this->_read->getLastQuery();
return $result;
}
/**
* Returns an array where the keys are the first column of a select, and the
* values are the second column:
*
* selectAssoc("SELECT id, name FROM companies LIMIT 3") => [1 => 'Ford', 2 => 'GM', 3 => 'Chrysler']
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return array
* @throws Horde_Db_Exception
*/
public function selectAssoc($sql, $arg1 = null, $arg2 = null)
{
$result = $this->_read->selectAssoc($sql, $arg1, $arg2);
$this->_lastQuery = $this->_read->getLastQuery();
return $result;
}
/**
* Executes the SQL statement in the context of this connection.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return PDOStatement
* @throws Horde_Db_Exception
*/
public function execute($sql, $arg1 = null, $arg2 = null)
{
// Can't assume this will always be a read action, use _write.
$result = $this->_write->execute($sql, $arg1, $arg2);
$this->_lastQuery = $this->_write->getLastQuery();
// Once doing writes, keep using the write backend even for reads
// at least during the same request, to help against stale data.
$this->_read = $this->_write;
return $result;
}
/**
* Returns the last auto-generated ID from the affected table.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a
* query name.
* @param string $arg2 If $arg1 contains bound parameters, the
* query name.
* @param string $pk TODO
* @param integer $idValue TODO
* @param string $sequenceName TODO
*
* @return integer Last inserted ID.
* @throws Horde_Db_Exception
*/
public function insert($sql, $arg1 = null, $arg2 = null, $pk = null,
$idValue = null, $sequenceName = null)
{
$result = $this->_write->insert($sql, $arg1, $arg2, $pk, $idValue, $sequenceName);
$this->_lastQuery = $this->_write->getLastQuery();
// Once doing writes, keep using the write backend even for reads
// at least during the same request, to help against stale data.
$this->_read = $this->_write;
return $result;
}
/**
* Inserts a row including BLOBs into a table.
*
* @since Horde_Db 2.1.0
*
* @param string $table The table name.
* @param array $fields A hash of column names and values. BLOB columns
* must be provided as Horde_Db_Value_Binary
* objects.
* @param string $pk The primary key column.
* @param integer $idValue The primary key value. This parameter is
* required if the primary key is inserted
* manually.
*
* @return integer Last inserted ID.
* @throws Horde_Db_Exception
*/
public function insertBlob($table, $fields, $pk = null, $idValue = null)
{
$result = $this->_write->insertBlob($table, $fields, $pk, $idValue);
$this->_lastQuery = $this->_write->getLastQuery();
// Once doing writes, keep using the write backend even for reads
// at least during the same request, to help against stale data.
$this->_read = $this->_write;
return $result;
}
/**
* Executes the update statement and returns the number of rows affected.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return integer Number of rows affected.
* @throws Horde_Db_Exception
*/
public function update($sql, $arg1 = null, $arg2 = null)
{
$result = $this->_write->update($sql, $arg1, $arg2);
$this->_lastQuery = $this->_write->getLastQuery();
// Once doing writes, keep using the write backend even for reads
// at least during the same request, to help against stale data.
$this->_read = $this->_write;
return $result;
}
/**
* Updates rows including BLOBs into a table.
*
* @since Horde_Db 2.2.0
*
* @param string $table The table name.
* @param array $fields A hash of column names and values. BLOB columns
* must be provided as Horde_Db_Value_Binary objects.
* @param string $where A WHERE clause.
*
* @throws Horde_Db_Exception
*/
public function updateBlob($table, $fields, $where = '')
{
$result = $this->_write->updateBlob($table, $fields, $where);
$this->_lastQuery = $this->_write->getLastQuery();
// Once doing writes, keep using the write backend even for reads
// at least during the same request, to help against stale data.
$this->_read = $this->_write;
return $result;
}
/**
* Executes the delete statement and returns the number of rows affected.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return integer Number of rows affected.
* @throws Horde_Db_Exception
*/
public function delete($sql, $arg1 = null, $arg2 = null)
{
$result = $this->_write->delete($sql, $arg1, $arg2);
$this->_lastQuery = $this->_write->getLastQuery();
// Once doing writes, keep using the write backend even for reads
// at least during the same request, to help against stale data.
$this->_read = $this->_write;
return $result;
}
/**
* Check if a transaction has been started.
*
* @return boolean True if transaction has been started.
*/
public function transactionStarted()
{
$result = $this->_write->transactionStarted();
$this->_lastQuery = $this->_write->getLastQuery();
return $result;
}
/**
* Begins the transaction (and turns off auto-committing).
*/
public function beginDbTransaction()
{
$result = $this->_write->beginDbTransaction();
$this->_lastQuery = $this->_write->getLastQuery();
return $result;
}
/**
* Commits the transaction (and turns on auto-committing).
*/
public function commitDbTransaction()
{
$result = $this->_write->commitDbTransaction();
$this->_lastQuery = $this->_write->getLastQuery();
return $result;
}
/**
* Rolls back the transaction (and turns on auto-committing). Must be
* done if the transaction block raises an exception or returns false.
*/
public function rollbackDbTransaction()
{
$result = $this->_write->rollbackDbTransaction();
$this->_lastQuery = $this->_write->getLastQuery();
return $result;
}
/**
* Appends +LIMIT+ and +OFFSET+ options to a SQL statement.
*
* @param string $sql SQL statement.
* @param array $options TODO
*
* @return string
*/
public function addLimitOffset($sql, $options)
{
$result = $this->_read->addLimitOffset($sql, $options);
$this->_lastQuery = $this->_write->getLastQuery();
return $result;
}
/**
* Appends a locking clause to an SQL statement.
* This method *modifies* the +sql+ parameter.
*
* # SELECT * FROM suppliers FOR UPDATE
* add_lock! 'SELECT * FROM suppliers', :lock => true
* add_lock! 'SELECT * FROM suppliers', :lock => ' FOR UPDATE'
*
* @param string &$sql SQL statment.
* @param array $options TODO.
*/
public function addLock(&$sql, array $options = array())
{
$this->_write->addLock($sql, $options);
$this->_lastQuery = $this->_write->getLastQuery();
}
}
Horde_Db-2.4.0/lib/Horde/Db/Migration/Base.php 0000664 0001750 0001750 00000010530 13054774261 016747 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Migration
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Migration
*/
class Horde_Db_Migration_Base
{
/**
* The migration version
* @var integer
*/
public $version = null;
/**
* The logger
* @var Horde_Log_Logger
*/
protected $_logger;
/**
* Database connection adapter
* @var Horde_Db_Adapter_Base
*/
protected $_connection;
/*##########################################################################
# Constructor
##########################################################################*/
/**
*/
public function __construct(Horde_Db_Adapter $connection, $version = null)
{
$this->_connection = $connection;
$this->version = $version;
}
/*##########################################################################
# Public
##########################################################################*/
/**
* Proxy methods over to the connection
* @param string $method
* @param array $args
*/
public function __call($method, $args)
{
$a = array();
foreach ($args as $arg) {
if (is_array($arg)) {
$vals = array();
foreach ($arg as $key => $value) {
$vals[] = var_export($key, true) . ' => ' . var_export($value, true);
}
$a[] = 'array(' . implode(', ', $vals) . ')';
} else {
$a[] = var_export($arg, true);
}
}
$this->say("$method(" . implode(", ", $a) . ")");
// benchmark method call
$t = new Horde_Support_Timer();
$t->push();
$result = call_user_func_array(array($this->_connection, $method), $args);
$time = $t->pop();
// print stats
$this->say(sprintf("%.4fs", $time), 'subitem');
if (is_int($result)) {
$this->say("$result rows", 'subitem');
}
return $result;
}
public function upWithBechmarks()
{
$this->migrate('up');
}
public function downWithBenchmarks()
{
$this->migrate('down');
}
/**
* Execute this migration in the named direction
*/
public function migrate($direction)
{
if (!method_exists($this, $direction)) { return; }
if ($direction == 'up') { $this->announce("migrating"); }
if ($direction == 'down') { $this->announce("reverting"); }
$result = null;
$t = new Horde_Support_Timer();
$t->push();
$result = $this->$direction();
$time = $t->pop();
if ($direction == 'up') {
$this->announce("migrated (" . sprintf("%.4fs", $time) . ")");
$this->log();
}
if ($direction == 'down') {
$this->announce("reverted (" . sprintf("%.4fs", $time) . ")");
$this->log();
}
return $result;
}
/**
* @param string $text
*/
public function log($text = '')
{
if ($this->_logger) {
$this->_logger->info($text);
}
}
/**
* @param Horde_Log_Logger $logger
*/
public function setLogger($logger)
{
$this->_logger = $logger;
}
/**
* Announce migration
* @param string $message
*/
public function announce($message)
{
$text = "$this->version " . get_class($this) . ": $message";
$length = 75 - strlen($text) > 0 ? 75 - strlen($text) : 0;
$this->log(sprintf("== %s %s", $text, str_repeat('=', $length)));
}
/**
* @param string $message
* @param boolean $subitem
*/
public function say($message, $subitem = false)
{
$this->log(($subitem ? " ->" : "--") . " $message");
}
}
Horde_Db-2.4.0/lib/Horde/Db/Migration/Exception.php 0000664 0001750 0001750 00000001336 13054774261 020037 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Migration
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Migration
*/
class Horde_Db_Migration_Exception extends Horde_Db_Exception
{
}
Horde_Db-2.4.0/lib/Horde/Db/Migration/Migrator.php 0000664 0001750 0001750 00000023037 13054774261 017667 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Migration
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Migration
*/
class Horde_Db_Migration_Migrator
{
/**
* @var string
*/
protected $_direction = null;
/**
* @var string
*/
protected $_migrationsPath = null;
/**
* @var integer
*/
protected $_targetVersion = null;
/**
* @var string
*/
protected $_schemaTableName = 'schema_info';
/**
* Constructor.
*
* @param Horde_Db_Adapter $connection A DB connection object.
* @param Horde_Log_Logger $logger A logger object.
* @param array $options Additional options for the migrator:
* - migrationsPath: directory with the
* migration files.
* - schemaTableName: table for storing
* the schema version.
*
* @throws Horde_Db_Migration_Exception
*/
public function __construct(Horde_Db_Adapter $connection,
Horde_Log_Logger $logger = null,
array $options = array())
{
if (!$connection->supportsMigrations()) {
throw new Horde_Db_Migration_Exception('This database does not yet support migrations');
}
$this->_connection = $connection;
$this->_logger = $logger ? $logger : new Horde_Support_Stub();
$this->_inflector = new Horde_Support_Inflector();
if (isset($options['migrationsPath'])) {
$this->_migrationsPath = $options['migrationsPath'];
}
if (isset($options['schemaTableName'])) {
$this->_schemaTableName = $options['schemaTableName'];
}
$this->_initializeSchemaInformation();
}
/**
* @param string $targetVersion
*/
public function migrate($targetVersion = null)
{
$currentVersion = $this->getCurrentVersion();
if ($targetVersion == null || $currentVersion < $targetVersion) {
$this->up($targetVersion);
} elseif ($currentVersion > $targetVersion) {
// migrate down
$this->down($targetVersion);
}
}
/**
* @param string $targetVersion
*/
public function up($targetVersion = null)
{
$this->_targetVersion = $targetVersion;
$this->_direction = 'up';
$this->_doMigrate();
}
/**
* @param string $targetVersion
*/
public function down($targetVersion = null)
{
$this->_targetVersion = $targetVersion;
$this->_direction = 'down';
$this->_doMigrate();
}
/**
* @return integer
*/
public function getCurrentVersion()
{
return in_array($this->_schemaTableName, $this->_connection->tables())
? $this->_connection->selectValue('SELECT version FROM ' . $this->_schemaTableName)
: 0;
}
/**
* @return integer
*/
public function getTargetVersion()
{
$migrations = array();
foreach ($this->_getMigrationFiles() as $migrationFile) {
list($version, $name) = $this->_getMigrationVersionAndName($migrationFile);
$this->_assertUniqueMigrationVersion($migrations, $version);
$migrations[$version] = $name;
}
// Sort by version.
uksort($migrations, 'strnatcmp');
return key(array_reverse($migrations, true));
}
/**
* @param string $migrationsPath Path to migration files.
*/
public function setMigrationsPath($migrationsPath)
{
$this->_migrationsPath = $migrationsPath;
}
/**
* @param Horde_Log_Logger $logger
*/
public function setLogger(Horde_Log_Logger $logger)
{
$this->_logger = $logger;
}
/**
* @param Horde_Support_Inflector $inflector
*/
public function setInflector(Horde_Support_Inflector $inflector)
{
$this->_inflector = $inflector;
}
/**
* Performs the migration.
*/
protected function _doMigrate()
{
foreach ($this->_getMigrationClasses() as $migration) {
if ($this->_hasReachedTargetVersion($migration->version)) {
$this->_logger->info('Reached target version: ' . $this->_targetVersion);
return;
}
if ($this->_isIrrelevantMigration($migration->version)) {
continue;
}
$this->_logger->info('Migrating ' . ($this->_direction == 'up' ? 'to ' : 'from ') . get_class($migration) . ' (' . $migration->version . ')');
$migration->migrate($this->_direction);
$this->_setSchemaVersion($migration->version);
}
}
/**
* @return array
*/
protected function _getMigrationClasses()
{
$migrations = array();
foreach ($this->_getMigrationFiles() as $migrationFile) {
require_once $migrationFile;
list($version, $name) = $this->_getMigrationVersionAndName($migrationFile);
$this->_assertUniqueMigrationVersion($migrations, $version);
$migrations[$version] = $this->_getMigrationClass($name, $version);
}
// Sort by version.
uksort($migrations, 'strnatcmp');
$sorted = array_values($migrations);
return $this->_isDown() ? array_reverse($sorted) : $sorted;
}
/**
* @param array $migrations
* @param integer $version
*
* @throws Horde_Db_Migration_Exception
*/
protected function _assertUniqueMigrationVersion($migrations, $version)
{
if (isset($migrations[$version])) {
throw new Horde_Db_Migration_Exception('Multiple migrations have the version number ' . $version);
}
}
/**
* Returns the list of migration files.
*
* @return array
*/
protected function _getMigrationFiles()
{
return array_keys(
iterator_to_array(
new RegexIterator(
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
$this->_migrationsPath
)
),
'/' . preg_quote(DIRECTORY_SEPARATOR, '/') . '\d+_.*\.php$/',
RecursiveRegexIterator::MATCH,
RegexIterator::USE_KEY
)
)
);
}
/**
* Actually returns object, and not class.
*
* @param string $migrationName
* @param integer $version
*
* @return Horde_Db_Migration_Base
*/
protected function _getMigrationClass($migrationName, $version)
{
$className = $this->_inflector->camelize($migrationName);
$class = new $className($this->_connection, $version);
$class->setLogger($this->_logger);
return $class;
}
/**
* @param string $migrationFile
*
* @return array ($version, $name)
*/
protected function _getMigrationVersionAndName($migrationFile)
{
preg_match_all('/([0-9]+)_([_a-z0-9]*).php/', $migrationFile, $matches);
return array($matches[1][0], $matches[2][0]);
}
/**
* @TODO
*/
protected function _initializeSchemaInformation()
{
if (in_array($this->_schemaTableName, $this->_connection->tables())) {
return;
}
$schemaTable = $this->_connection->createTable($this->_schemaTableName, array('autoincrementKey' => false));
$schemaTable->column('version', 'integer');
$schemaTable->end();
$this->_connection->insert('INSERT INTO ' . $this->_schemaTableName . ' (version) VALUES (0)', null, null, null, 1);
}
/**
* @param integer $version
*/
protected function _setSchemaVersion($version)
{
$version = $this->_isDown() ? $version - 1 : $version;
if ($version) {
$sql = 'UPDATE ' . $this->_schemaTableName . ' SET version = ' . (int)$version;
$this->_connection->update($sql);
} else {
$this->_connection->dropTable($this->_schemaTableName);
}
}
/**
* @return boolean
*/
protected function _isUp()
{
return $this->_direction == 'up';
}
/**
* @return boolean
*/
protected function _isDown()
{
return $this->_direction == 'down';
}
/**
* @return boolean
*/
protected function _hasReachedTargetVersion($version)
{
if ($this->_targetVersion === null) {
return false;
}
return ($this->_isUp() && $version - 1 >= $this->_targetVersion) ||
($this->_isDown() && $version <= $this->_targetVersion);
}
/**
* @param integer $version
*
* @return boolean
*/
protected function _isIrrelevantMigration($version)
{
return ($this->_isUp() && $version <= self::getCurrentVersion()) ||
($this->_isDown() && $version > self::getCurrentVersion());
}
}
Horde_Db-2.4.0/lib/Horde/Db/Value/Binary.php 0000664 0001750 0001750 00000001505 13054774261 016446 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
*/
/**
* Encapsulation object for binary values to be used in SQL statements to
* ensure proper quoting, escaping, retrieval, etc.
*
* @property $value The binary value as a string. @since Horde_Db 2.1.0
* @property $stream The binary value as a stream. @since Horde_Db 2.4.0
*
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
*/
class Horde_Db_Value_Binary extends Horde_Db_Value_Lob
{
/**
* @param Horde_Db_Adapter $db
*/
public function quote(Horde_Db_Adapter $db)
{
return $db->quoteBinary($this->value);
}
}
Horde_Db-2.4.0/lib/Horde/Db/Value/Lob.php 0000664 0001750 0001750 00000004433 13054774261 015741 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
*/
/**
* Encapsulation object for LOB values to be used in SQL statements to ensure
* proper quoting, escaping, retrieval, etc.
*
* @property $value The binary value as a string. @since Horde_Db 2.1.0
* @property $stream The binary value as a stream. @since Horde_Db 2.4.0
*
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
*/
abstract class Horde_Db_Value_Lob implements Horde_Db_Value
{
/**
* Binary scalar value to be quoted
*
* @var string
*/
protected $_value;
/**
* Binary stream value to be quoted
*
* @var stream
*/
protected $_stream;
/**
* Constructor
*
* @param string|stream $binaryValue The binary value in a string or
* stream resource.
*/
public function __construct($binaryValue)
{
if (is_resource($binaryValue)) {
$this->stream = $binaryValue;
} else {
$this->value = $binaryValue;
}
}
/**
* Getter for $value and $stream properties.
*/
public function __get($name)
{
switch ($name) {
case 'value':
if (isset($this->_value)) {
return $this->_value;
}
if (isset($this->_stream)) {
rewind($this->_stream);
return stream_get_contents($this->_stream);
}
break;
case 'stream':
if (isset($this->_stream)) {
return $this->_stream;
}
if (isset($this->_value)) {
$stream = @fopen('php://temp', 'r+');
fwrite($stream, $this->_value);
rewind($stream);
return $stream;
}
break;
}
}
/**
* Setter for $value and $stream properties.
*/
public function __set($name, $value)
{
switch ($name) {
case 'value':
case 'stream':
$this->{'_' . $name} = $value;
break;
}
}
}
Horde_Db-2.4.0/lib/Horde/Db/Value/Text.php 0000664 0001750 0001750 00000001250 13054774261 016143 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
*/
/**
* Encapsulation object for long text values to be used in SQL statements to
* ensure proper quoting, escaping, retrieval, etc.
*
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
*/
class Horde_Db_Value_Text extends Horde_Db_Value_Lob
{
/**
* @param Horde_Db_Adapter $db
*/
public function quote(Horde_Db_Adapter $db)
{
return $db->quoteString($this->value);
}
}
Horde_Db-2.4.0/lib/Horde/Db/Adapter.php 0000664 0001750 0001750 00000026002 13054774261 015525 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage Adapter
*/
interface Horde_Db_Adapter
{
/**
* Returns the human-readable name of the adapter. Use mixed case - one
* can always use downcase if needed.
*
* @return string
*/
public function adapterName();
/**
* Does this adapter support migrations? Backend specific, as the
* abstract adapter always returns +false+.
*
* @return boolean
*/
public function supportsMigrations();
/**
* Does this adapter support using DISTINCT within COUNT? This is +true+
* for all adapters except sqlite.
*
* @return boolean
*/
public function supportsCountDistinct();
/**
* Should primary key values be selected from their corresponding
* sequence before the insert statement? If true, next_sequence_value
* is called before each insert to set the record's primary key.
* This is false for all adapters but Firebird.
*
* @return boolean
*/
public function prefetchPrimaryKey($tableName = null);
/*##########################################################################
# Connection Management
##########################################################################*/
/**
* Connect to the db.
*/
public function connect();
/**
* Is the connection active?
*
* @return boolean
*/
public function isActive();
/**
* Reconnect to the db.
*/
public function reconnect();
/**
* Disconnect from db.
*/
public function disconnect();
/**
* Provides access to the underlying database connection. Useful for when
* you need to call a proprietary method such as postgresql's
* lo_* methods.
*
* @return resource
*/
public function rawConnection();
/*##########################################################################
# Quoting
##########################################################################*/
/**
* Quotes a string, escaping any special characters.
*
* @param string $string
* @return string
*/
public function quoteString($string);
/*##########################################################################
# Database Statements
##########################################################################*/
/**
* Returns an array of records with the column names as keys, and
* column values as values.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return Horde_Db_Adapter_Base_Result
* @throws Horde_Db_Exception
*/
public function select($sql, $arg1 = null, $arg2 = null);
/**
* Returns an array of record hashes with the column names as keys and
* column values as values.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return array
* @throws Horde_Db_Exception
*/
public function selectAll($sql, $arg1 = null, $arg2 = null);
/**
* Returns a record hash with the column names as keys and column values
* as values.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return array
* @throws Horde_Db_Exception
*/
public function selectOne($sql, $arg1 = null, $arg2 = null);
/**
* Returns a single value from a record
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return string
* @throws Horde_Db_Exception
*/
public function selectValue($sql, $arg1 = null, $arg2 = null);
/**
* Returns an array of the values of the first column in a select:
* selectValues("SELECT id FROM companies LIMIT 3") => [1,2,3]
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return array
* @throws Horde_Db_Exception
*/
public function selectValues($sql, $arg1 = null, $arg2 = null);
/**
* Returns an array where the keys are the first column of a select, and the
* values are the second column:
*
* selectAssoc("SELECT id, name FROM companies LIMIT 3") => [1 => 'Ford', 2 => 'GM', 3 => 'Chrysler']
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return array
* @throws Horde_Db_Exception
*/
public function selectAssoc($sql, $arg1 = null, $arg2 = null);
/**
* Executes the SQL statement in the context of this connection.
*
* @deprecated Deprecated for external usage. Use select() instead.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return mixed
* @throws Horde_Db_Exception
*/
public function execute($sql, $arg1 = null, $arg2 = null);
/**
* Inserts a row into a table.
*
* @param string $sql SQL statement.
* @param array|string $arg1 Either an array of bound parameters or a
* query name.
* @param string $arg2 If $arg1 contains bound parameters, the
* query name.
* @param string $pk The primary key column.
* @param integer $idValue The primary key value. This parameter is
* required if the primary key is inserted
* manually.
* @param string $sequenceName The sequence name.
*
* @return integer Last inserted ID.
* @throws Horde_Db_Exception
*/
public function insert($sql, $arg1 = null, $arg2 = null, $pk = null,
$idValue = null, $sequenceName = null);
/**
* Inserts a row including BLOBs into a table.
*
* @since Horde_Db 2.1.0
*
* @param string $table The table name.
* @param array $fields A hash of column names and values. BLOB columns
* must be provided as Horde_Db_Value_Binary
* objects.
* @param string $pk The primary key column.
* @param integer $idValue The primary key value. This parameter is
* required if the primary key is inserted
* manually.
*
* @return integer Last inserted ID.
* @throws Horde_Db_Exception
*/
public function insertBlob($table, $fields, $pk = null, $idValue = null);
/**
* Executes the update statement and returns the number of rows affected.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return integer Number of rows affected.
* @throws Horde_Db_Exception
*/
public function update($sql, $arg1 = null, $arg2 = null);
/**
* Updates rows including BLOBs into a table.
*
* @since Horde_Db 2.2.0
*
* @param string $table The table name.
* @param array $fields A hash of column names and values. BLOB columns
* must be provided as Horde_Db_Value_Binary objects.
* @param string $where A WHERE clause.
*
* @throws Horde_Db_Exception
*/
public function updateBlob($table, $fields, $where = '');
/**
* Executes the delete statement and returns the number of rows affected.
*
* @param string $sql SQL statement.
* @param mixed $arg1 Either an array of bound parameters or a query
* name.
* @param string $arg2 If $arg1 contains bound parameters, the query
* name.
*
* @return integer Number of rows affected.
* @throws Horde_Db_Exception
*/
public function delete($sql, $arg1 = null, $arg2 = null);
/**
* Check if a transaction has been started.
*
* @return boolean True if transaction has been started.
*/
public function transactionStarted();
/**
* Begins the transaction (and turns off auto-committing).
*/
public function beginDbTransaction();
/**
* Commits the transaction (and turns on auto-committing).
*/
public function commitDbTransaction();
/**
* Rolls back the transaction (and turns on auto-committing). Must be
* done if the transaction block raises an exception or returns false.
*/
public function rollbackDbTransaction();
/**
* Appends +LIMIT+ and +OFFSET+ options to a SQL statement.
*
* @param string $sql SQL statement.
* @param array $options TODO
*
* @return string
*/
public function addLimitOffset($sql, $options);
/**
* Appends a locking clause to an SQL statement.
* This method *modifies* the +sql+ parameter.
*
* # SELECT * FROM suppliers FOR UPDATE
* add_lock! 'SELECT * FROM suppliers', :lock => true
* add_lock! 'SELECT * FROM suppliers', :lock => ' FOR UPDATE'
*
* @param string &$sql SQL statment.
* @param array $options TODO.
*/
public function addLock(&$sql, array $options = array());
}
Horde_Db-2.4.0/lib/Horde/Db/Exception.php 0000664 0001750 0001750 00000001301 13054774261 016076 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
*/
/**
* Db Exception class.
*
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
*/
class Horde_Db_Exception extends Horde_Exception_Wrapped
{
}
Horde_Db-2.4.0/lib/Horde/Db/SearchParser.php 0000664 0001750 0001750 00000013302 13054774261 016526 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
*/
class Horde_Db_SearchParser
{
/**
* Parses a keyword expression.
*
* @param string $column This is the SQL field name the resulting
* expression should test against.
* @param string $expr This is the keyword expression we want to parse.
*
* @return string The query expression.
* @throws Horde_Db_Exception
*/
public static function parse($column, $expr)
{
/* First pass - scan the string for tokens. Bare words are tokens, or
* the user can quote strings to have embedded spaces, keywords, or
* parentheses. Parentheses can be used for grouping boolean
* operators, and the boolean operators AND, OR, and NOT are all
* recognized.
*
* The tokens are returned in the $tokens array -- an array of strings.
* Each string in the array starts with either a `!' or a `='. `=' is
* a bare word or quoted string we are searching for, and `!' indicates
* a boolean operator or parenthesis. A token that starts with a '.'
* indicates a PostgreSQL word boundary search. */
$tokens = array();
while (!empty($expr)) {
$expr = preg_replace('/^\s+/', '', $expr);
if (empty($expr)) {
break;
}
if (substr($expr,0,1) == '(') {
$expr = substr($expr, 1);
$token = '!(';
} elseif (substr($expr, 0, 1) == ')') {
$expr = substr($expr, 1);
$token = '!)';
} elseif (substr($expr, 0, 1) == ',') {
$expr = substr($expr, 1);
$token = '!OR';
} elseif (preg_match('/^(AND|OR|NOT)([^a-z].*)?$/i', $expr,
$matches)) {
$token = '!' . Horde_String::upper($matches[1]);
$expr = substr($expr, strlen($matches[1]));
} elseif (preg_match('/^"(([^"]|\\[0-7]+|\\[Xx][0-9a-fA-F]+|\\[^Xx0-7])*)"/',
$expr, $matches)) {
$token = '=' . stripcslashes($matches[1]);
$expr = substr($expr, strlen($matches[0]));
} elseif (preg_match('/^[^\\s\\(\\),]+/', $expr, $matches)) {
$token = '=' . $matches[0];
$expr = substr($expr,strlen($token)-1);
} else {
throw new Horde_Db_Exception('Syntax error in search terms');
}
if ($token == '!AND') {
/* !AND is implied by concatenation. */
continue;
}
$tokens[] = $token;
}
/* Call the expression parser. */
return self::_parseKeywords1($column, $tokens);
}
protected static function _parseKeywords1($column, &$tokens)
{
if (count($tokens) == 0) {
throw new Horde_Db_Exception('Empty search terms');
}
$lhs = self::_parseKeywords2($column, $tokens);
if (count($tokens) == 0 || $tokens[0] != '!OR') {
return $lhs;
}
array_shift($tokens);
$rhs = self::_parseKeywords1($column, $tokens);
return "($lhs OR $rhs)";
}
protected static function _parseKeywords2($column, &$tokens)
{
$lhs = self::_parseKeywords3($column, $tokens);
if (sizeof($tokens) == 0 || $tokens[0] == '!)' || $tokens[0] == '!OR') {
return $lhs;
}
$rhs = self::_parseKeywords2($column, $tokens);
return "($lhs AND $rhs)";
}
protected static function _parseKeywords3($column, &$tokens)
{
if ($tokens[0] == '!NOT') {
array_shift($tokens);
$lhs = self::_parseKeywords4($column, $tokens);
if (is_a($lhs, 'PEAR_Error')) {
return $lhs;
}
return "(NOT $lhs)";
}
return self::_parseKeywords4($column, $tokens);
}
protected static function _parseKeywords4($column, &$tokens)
{
if ($tokens[0] == '!(') {
array_shift($tokens);
$lhs = self::_parseKeywords1($column, $tokens);
if (sizeof($tokens) == 0 || $tokens[0] != '!)') {
throw new Horde_Db_Exception('Expected ")"');
}
array_shift($tokens);
return $lhs;
}
if (substr($tokens[0], 0, 1) != '=' &&
substr($tokens[0], 0, 2) != '=.') {
throw new Horde_Db_Exception('Expected bare word or quoted search term');
}
$val = Horde_String::lower(substr(array_shift($tokens), 1));
$val = addslashes(str_replace("%", "\\%", $val));
return "(LOWER($column) LIKE '%$val%')";
}
}
Horde_Db-2.4.0/lib/Horde/Db/StatementParser.php 0000664 0001750 0001750 00000004635 13054774261 017276 0 ustar jan jan
* @author James Pepin
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
*/
/**
* Class for parsing a stream into individual SQL statements.
*
* @author Chuck Hagenbuch
* @author James Pepin
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
*/
class Horde_Db_StatementParser implements Iterator
{
protected $_count = 0;
protected $_currentStatement;
public function __construct($file)
{
if (is_string($file)) {
$file = new SplFileObject($file, 'r');
}
$this->_file = $file;
}
public function current()
{
if (is_null($this->_currentStatement)) {
$this->rewind();
}
return $this->_currentStatement;
}
public function key()
{
if (is_null($this->_currentStatement)) {
$this->rewind();
}
return $this->_count;
}
public function next()
{
if ($statement = $this->_getNextStatement()) {
$this->_count++;
return $statement;
}
return null;
}
public function rewind()
{
$this->_count = 0;
$this->_currentStatement = null;
$this->_file->rewind();
$this->next();
}
public function valid()
{
return !$this->_file->eof() && $this->_file->isReadable();
}
/**
* Read the next sql statement from our file. Statements are terminated by
* semicolons.
*
* @return string The next SQL statement in the file.
*/
protected function _getNextStatement()
{
$this->_currentStatement = '';
while (!$this->_file->eof()) {
$line = $this->_file->fgets();
if (!trim($line)) { continue; }
if (!$this->_currentStatement && substr($line, 0, 2) == '--') { continue; }
$trimmedline = rtrim($line);
if (substr($trimmedline, -1) == ';') {
// Leave off the ending ;
$this->_currentStatement .= substr($trimmedline, 0, -1);
return $this->_currentStatement;
}
$this->_currentStatement .= $line;
}
return $this->_currentStatement;
}
}
Horde_Db-2.4.0/lib/Horde/Db/Value.php 0000664 0001750 0001750 00000001036 13054774261 015221 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
*/
/**
* Interface for values with specific quoting rules.
*
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
*/
interface Horde_Db_Value
{
/**
* @param Horde_Db_Adapter $db
*/
public function quote(Horde_Db_Adapter $db);
}
Horde_Db-2.4.0/lib/Horde/Db.php 0000664 0001750 0001750 00000001770 13054774261 014152 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
*/
/**
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
*/
class Horde_Db
{
/**
* Specifies that the fetch method shall return each row as an array
* indexed by column name as returned in the corresponding result set.
*/
const FETCH_ASSOC = 2;
/**
* Specifies that the fetch method shall return each row as an array
* indexed by column number as returned in the corresponding result set,
* starting at column 0.
*/
const FETCH_NUM = 3;
/**
* Specifies that the fetch method shall return each row as an array
* indexed by both column name and number as returned in the corresponding
* result set, starting at column 0.
*/
const FETCH_BOTH = 4;
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/Mysql/ColumnDefinition.php 0000664 0001750 0001750 00000007563 13054774261 022324 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
class Horde_Db_Adapter_Mysql_ColumnDefinition extends Horde_Test_Case
{
public $conn;
public function testConstruct()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$this->assertEquals('col_name', $col->getName());
$this->assertEquals('string', $col->getType());
}
public function testToSql()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$this->assertEquals('`col_name` varchar(255)', $col->toSql());
}
public function testToSqlLimit()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string', 40
);
$this->assertEquals('`col_name` varchar(40)', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$col->setLimit(40);
$this->assertEquals('`col_name` varchar(40)', $col->toSql());
}
public function testToSqlPrecisionScale()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'decimal', null, 5, 2
);
$this->assertEquals('`col_name` decimal(5, 2)', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'decimal'
);
$col->setPrecision(5);
$col->setScale(2);
$this->assertEquals('`col_name` decimal(5, 2)', $col->toSql());
}
public function testToSqlUnsigned()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'integer', null, null, null, true
);
$this->assertEquals('`col_name` int(10) UNSIGNED', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'integer'
);
$col->setUnsigned(true);
$this->assertEquals('`col_name` int(10) UNSIGNED', $col->toSql());
}
public function testToSqlNotNull()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string', null, null, null, null, null, false
);
$this->assertEquals('`col_name` varchar(255) NOT NULL', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$col->setNull(false);
$this->assertEquals('`col_name` varchar(255) NOT NULL', $col->toSql());
}
public function testToSqlDefault()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string', null, null, null, null, 'test'
);
$this->assertEquals("`col_name` varchar(255) DEFAULT 'test'", $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$col->setDefault('test');
$this->assertEquals("`col_name` varchar(255) DEFAULT 'test'", $col->toSql());
}
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/Mysql/ColumnTest.php 0000664 0001750 0001750 00000005023 13054774261 021140 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
class Horde_Db_Adapter_Mysql_ColumnTest extends Horde_Db_Adapter_ColumnBase
{
protected $_class = 'Horde_Db_Adapter_Mysql_Column';
/*##########################################################################
# Types
##########################################################################*/
public function testTypeInteger()
{
parent::testTypeInteger();
$col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'int(11)');
$this->assertFalse($col->isUnsigned());
}
public function testTypeIntegerUnsigned()
{
$col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'int(10) UNSIGNED');
$this->assertTrue($col->isUnsigned());
}
/*##########################################################################
# Type Cast Values
##########################################################################*/
public function testTypeCastBooleanFalse()
{
$col = new Horde_Db_Adapter_Mysql_Column('is_active', '0', 'tinyint(1)', false);
$this->assertSame(false, $col->getDefault());
}
public function testTypeCastBooleanTrue()
{
$col = new Horde_Db_Adapter_Mysql_Column('is_active', '1', 'tinyint(1)', false);
$this->assertSame(true, $col->getDefault());
}
/*##########################################################################
# Column Types
##########################################################################*/
public function testColumnTypeEnum()
{
$col = new Horde_Db_Adapter_Mysql_Column('user', 'NULL', "enum('derek', 'mike')");
$this->assertEquals('string', $col->getType());
}
public function testColumnTypeBoolean()
{
$col = new Horde_Db_Adapter_Mysql_Column('is_active', 'NULL', 'tinyint(1)');
$this->assertEquals('boolean', $col->getType());
}
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/Mysql/TestTableDefinition.php 0000664 0001750 0001750 00000001426 13054774261 022746 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
class Horde_Db_Adapter_Mysql_TestTableDefinition extends Horde_Db_Adapter_TestTableDefinition
{
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/Oracle/ColumnDefinition.php 0000664 0001750 0001750 00000007307 13054774261 022420 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
class Horde_Db_Adapter_Oracle_ColumnDefinition extends Horde_Test_Case
{
public $conn;
public function testConstruct()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$this->assertEquals('col_name', $col->getName());
$this->assertEquals('string', $col->getType());
}
public function testToSql()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$this->assertEquals('col_name varchar2(255)', $col->toSql());
}
public function testToSqlLimit()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string', 40
);
$this->assertEquals('col_name varchar2(40)', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$col->setLimit(40);
$this->assertEquals('col_name varchar2(40)', $col->toSql());
}
public function testToSqlPrecisionScale()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'decimal', null, 5, 2
);
$this->assertEquals('col_name number(5, 2)', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'decimal'
);
$col->setPrecision(5);
$col->setScale(2);
$this->assertEquals('col_name number(5, 2)', $col->toSql());
}
public function testToSqlNotNull()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string', null, null, null, null, null, false
);
$this->assertEquals('col_name varchar2(255) NOT NULL', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$col->setNull(false);
$this->assertEquals('col_name varchar2(255) NOT NULL', $col->toSql());
// set attribute to the default (true)
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$col->setNull(true);
$this->assertEquals('col_name varchar2(255) NULL', $col->toSql());
}
public function testToSqlDefault()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string', null, null, null, null, 'test', null
);
$this->assertEquals('col_name varchar2(255) DEFAULT \'test\'', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$col->setDefault('test');
$this->assertEquals('col_name varchar2(255) DEFAULT \'test\'', $col->toSql());
}
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/Oracle/ColumnTest.php 0000664 0001750 0001750 00000005143 13054774261 021243 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
class Horde_Db_Adapter_Oracle_ColumnTest extends Horde_Db_Adapter_ColumnBase
{
protected $_class = 'Horde_Db_Adapter_Oracle_Column';
/*##########################################################################
# Types
##########################################################################*/
public function testTypeDecimal()
{
$col = new $this->_class('age', 'NULL', 'decimal', true, 11, 11, 1);
$this->assertEquals('decimal', $col->getType());
}
/*##########################################################################
# Extract Limit
##########################################################################*/
public function testExtractLimitInt()
{
$col = new $this->_class('test', 'NULL', 'int', true, 11);
$this->assertEquals(11, $col->getLimit());
}
public function testExtractLimitVarchar()
{
$col = new $this->_class('test', 'NULL', 'varchar', true, 255);
$this->assertEquals(255, $col->getLimit());
}
public function testExtractLimitDecimal()
{
$col = new $this->_class('test', 'NULL', 'decimal', true, 11, 11, 1);
$this->assertEquals('11', $col->getLimit());
}
/*##########################################################################
# Extract Precision/Scale
##########################################################################*/
public function testExtractPrecisionScale()
{
$col = new $this->_class('test', 'NULL', 'decimal', true, 12, 12, 1);
$this->assertEquals('12', $col->precision());
$this->assertEquals('1', $col->scale());
}
/*##########################################################################
# Type Cast Values
##########################################################################*/
public function testTypeCastBooleanFalse()
{
$col = new $this->_class('is_active', '0', 'number', false, null, 1);
$this->assertSame(false, $col->getDefault());
}
public function testTypeCastBooleanTrue()
{
$col = new $this->_class('is_active', '1', 'number', false, null, 1);
$this->assertSame(true, $col->getDefault());
}
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/Oracle/TestTableDefinition.php 0000664 0001750 0001750 00000001012 13054774261 023035 0 ustar jan jan
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
class Horde_Db_Adapter_Oracle_TestTableDefinition extends Horde_Db_Adapter_TestTableDefinition
{
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/Pdo/MysqlTest.php 0000664 0001750 0001750 00000004525 13054774261 020433 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
class Horde_Db_Adapter_Pdo_MysqlTest extends Horde_Db_Adapter_MysqlBase
{
protected static function _available()
{
return extension_loaded('pdo') &&
in_array('mysql', PDO::getAvailableDrivers());
}
protected static function _getConnection($overrides = array())
{
$config = Horde_Test_Case::getConfig('DB_ADAPTER_PDO_MYSQL_TEST_CONFIG',
__DIR__ . '/..',
array('host' => 'localhost',
'username' => '',
'password' => '',
'dbname' => 'test'));
if (isset($config['db']['adapter']['pdo']['mysql']['test'])) {
$config = $config['db']['adapter']['pdo']['mysql']['test'];
}
if (!is_array($config)) {
self::$_skip = true;
self::$_reason = 'No configuration for pdo_mysql test';
return;
}
$config = array_merge($config, $overrides);
$conn = new Horde_Db_Adapter_Pdo_Mysql($config);
$cache = new Horde_Cache(new Horde_Cache_Storage_Mock());
$conn->setCache($cache);
return array($conn, $cache);
}
/*##########################################################################
# Accessor
##########################################################################*/
public function testAdapterName()
{
$this->assertEquals('PDO_MySQL', $this->_conn->adapterName());
}
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/Pdo/PgsqlTest.php 0000664 0001750 0001750 00000035514 13054774261 020416 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
class Horde_Db_Adapter_Pdo_PgsqlTest extends Horde_Db_Adapter_TestBase
{
public static function setUpBeforeClass()
{
self::$_reason = 'The pgsql adapter is not available';
if (extension_loaded('pdo') &&
in_array('pgsql', PDO::getAvailableDrivers())) {
self::$_skip = false;
list($conn,) = static::_getConnection();
if ($conn) {
$conn->disconnect();
}
}
self::$_columnTest = new Horde_Db_Adapter_Postgresql_ColumnDefinition();
self::$_tableTest = new Horde_Db_Adapter_Postgresql_TestTableDefinition();
}
protected static function _getConnection($overrides = array())
{
$config = Horde_Test_Case::getConfig('DB_ADAPTER_PDO_PGSQL_TEST_CONFIG',
__DIR__ . '/..',
array('username' => '',
'password' => '',
'dbname' => 'test'));
if (isset($config['db']['adapter']['pdo']['pgsql']['test'])) {
$config = $config['db']['adapter']['pdo']['pgsql']['test'];
}
if (!is_array($config)) {
self::$_skip = true;
self::$_reason = 'No configuration for pdo_pgsql test';
return;
}
$config = array_merge($config, $overrides);
$conn = new Horde_Db_Adapter_Pdo_Pgsql($config);
$cache = new Horde_Cache(new Horde_Cache_Storage_Mock());
$conn->setCache($cache);
return array($conn, $cache);
}
/*##########################################################################
# Accessor
##########################################################################*/
public function testAdapterName()
{
$this->assertEquals('PDO_PostgreSQL', $this->_conn->adapterName());
}
public function testSupportsMigrations()
{
$this->assertTrue($this->_conn->supportsMigrations());
}
public function testSupportsCountDistinct()
{
$this->assertTrue($this->_conn->supportsCountDistinct());
}
public function testSupportsInterval()
{
$this->assertTrue($this->_conn->supportsInterval());
}
/*##########################################################################
# Quoting
##########################################################################*/
public function testQuoteNull()
{
$this->assertEquals('NULL', $this->_conn->quote(null));
}
public function testQuoteTrue()
{
$this->assertEquals("'t'", $this->_conn->quote(true));
}
public function testQuoteFalse()
{
$this->assertEquals("'f'", $this->_conn->quote(false));
}
public function testQuoteInteger()
{
$this->assertEquals('42', $this->_conn->quote(42));
}
public function testQuoteFloat()
{
$this->assertEquals('42.2', $this->_conn->quote(42.2));
setlocale(LC_NUMERIC, 'de_DE.UTF-8');
$this->assertEquals('42.2', $this->_conn->quote(42.2));
}
public function testQuoteString()
{
$this->assertEquals("'my string'", $this->_conn->quote('my string'));
}
public function testQuoteDirtyString()
{
$this->assertEquals("'derek''s string'", $this->_conn->quote('derek\'s string'));
}
public function testQuoteColumnName()
{
$col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'int(11)');
$this->assertEquals('1', $this->_conn->quote(true, $col));
}
/*##########################################################################
# Schema Statements
##########################################################################*/
public function testNativeDatabaseTypes()
{
$types = $this->_conn->nativeDatabaseTypes();
$this->assertEquals(array('name' => 'integer', 'limit' => null), $types['integer']);
}
public function testTableAliasLength()
{
$len = $this->_conn->tableAliasLength();
$this->assertGreaterThanOrEqual(63, $len);
}
public function testColumns()
{
$col = parent::testColumns();
$this->assertEquals(null, $col->getLimit());
$this->assertEquals('integer', $col->getSqlType());
}
public function testCreateTableWithSeparatePk()
{
$pkColumn = parent::testCreateTableWithSeparatePk();
$this->assertEquals('"foo" serial primary key', $pkColumn->toSql());
}
public function testChangeColumnType()
{
$this->_createTestTable('sports');
$beforeChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('boolean', $beforeChange->getSqlType());
$this->_conn->changeColumn('sports', 'is_college', 'string');
$afterChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('character varying(255)', $afterChange->getSqlType());
$table = $this->_conn->createTable('text_to_binary');
$table->column('data', 'text');
$table->end();
$this->_conn->insert('INSERT INTO text_to_binary (data) VALUES (?)',
array("foo"));
$this->_conn->changeColumn('text_to_binary', 'data', 'binary');
$afterChange = $this->_getColumn('text_to_binary', 'data');
$this->assertEquals('bytea', $afterChange->getSqlType());
$this->assertEquals(
"foo",
stream_get_contents($this->_conn->selectValue('SELECT data FROM text_to_binary')));
}
public function testChangeColumnLimit()
{
$this->_createTestTable('sports');
$beforeChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('boolean', $beforeChange->getSqlType());
$this->_conn->changeColumn('sports', 'is_college', 'string',
array('limit' => '40'));
$afterChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('character varying(40)', $afterChange->getSqlType());
}
public function testChangeColumnPrecisionScale()
{
$this->_createTestTable('sports');
$beforeChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('boolean', $beforeChange->getSqlType());
$this->_conn->changeColumn('sports', 'is_college', 'decimal',
array('precision' => '5', 'scale' => '2'));
$afterChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('numeric(5,2)', $afterChange->getSqlType());
}
public function testRenameColumn()
{
$this->_createTestUsersTable();
$this->_conn->renameColumn('users', 'first_name', 'nick_name');
$this->assertTrue(in_array('nick_name', $this->_columnNames('users')));
$this->_createTestTable('sports');
$beforeChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('boolean', $beforeChange->getSqlType());
$this->_conn->renameColumn('sports', 'is_college', 'is_renamed');
$afterChange = $this->_getColumn('sports', 'is_renamed');
$this->assertEquals('boolean', $afterChange->getSqlType());
}
public function testTypeToSqlTypePrimaryKey()
{
$result = $this->_conn->typeToSql('autoincrementKey');
$this->assertEquals('serial primary key', $result);
}
public function testTypeToSqlTypeString()
{
$result = $this->_conn->typeToSql('string');
$this->assertEquals('character varying(255)', $result);
}
public function testTypeToSqlTypeText()
{
$result = $this->_conn->typeToSql('text');
$this->assertEquals('text', $result);
}
public function testTypeToSqlTypeBinary()
{
$result = $this->_conn->typeToSql('binary');
$this->assertEquals('bytea', $result);
}
public function testTypeToSqlTypeFloat()
{
$result = $this->_conn->typeToSql('float');
$this->assertEquals('float', $result);
}
public function testTypeToSqlTypeDatetime()
{
$result = $this->_conn->typeToSql('datetime');
$this->assertEquals('timestamp', $result);
}
public function testTypeToSqlTypeTimestamp()
{
$result = $this->_conn->typeToSql('timestamp');
$this->assertEquals('timestamp', $result);
}
public function testTypeToSqlInt()
{
$result = $this->_conn->typeToSql('integer');
$this->assertEquals('integer', $result);
}
public function testTypeToSqlIntLimit()
{
$result = $this->_conn->typeToSql('integer', '1');
$this->assertEquals('smallint', $result);
}
public function testTypeToSqlDecimalPrecision()
{
$result = $this->_conn->typeToSql('decimal', null, '5');
$this->assertEquals('decimal(5)', $result);
}
public function testTypeToSqlDecimalScale()
{
$result = $this->_conn->typeToSql('decimal', null, '5', '2');
$this->assertEquals('decimal(5, 2)', $result);
}
public function testTypeToSqlBoolean()
{
$result = $this->_conn->typeToSql('boolean');
$this->assertEquals('boolean', $result);
}
public function testAddColumnOptions()
{
$result = $this->_conn->addColumnOptions("test", array());
$this->assertEquals("test", $result);
}
public function testAddColumnOptionsDefault()
{
$options = array('default' => '0');
$result = $this->_conn->addColumnOptions("test", $options);
$this->assertEquals("test DEFAULT '0'", $result);
}
public function testAddColumnOptionsNull()
{
$options = array('null' => true);
$result = $this->_conn->addColumnOptions("test", $options);
$this->assertEquals("test", $result);
}
public function testAddColumnOptionsNotNull()
{
$options = array('null' => false);
$result = $this->_conn->addColumnOptions("test", $options);
$this->assertEquals("test NOT NULL", $result);
}
public function testInterval()
{
$this->assertEquals('INTERVAL \'1 DAY \'',
$this->_conn->interval('1 DAY', ''));
}
public function testModifyDate()
{
$modifiedDate = $this->_conn->modifyDate('mystart', '+', 1, 'DAY');
$this->assertEquals('mystart + INTERVAL \'1 DAY\'', $modifiedDate);
$t = $this->_conn->createTable('dates');
$t->column('mystart', 'datetime');
$t->column('myend', 'datetime');
$t->end();
$this->_conn->insert(
'INSERT INTO dates (mystart, myend) VALUES (?, ?)',
array(
'2011-12-10 00:00:00',
'2011-12-11 00:00:00'
)
);
$this->assertEquals(
1,
$this->_conn->selectValue('SELECT COUNT(*) FROM dates WHERE '
. $modifiedDate . ' = myend')
);
}
public function testBuildClause()
{
$this->assertEquals(
"CASE WHEN CAST(bitmap AS VARCHAR) ~ '^-?[0-9]+$' THEN (CAST(bitmap AS INTEGER) & 2) ELSE 0 END",
$this->_conn->buildClause('bitmap', '&', 2));
$this->assertEquals(
array("CASE WHEN CAST(bitmap AS VARCHAR) ~ '^-?[0-9]+$' THEN (CAST(bitmap AS INTEGER) & ?) ELSE 0 END", array(2)),
$this->_conn->buildClause('bitmap', '&', 2, true));
$this->assertEquals(
"CASE WHEN CAST(bitmap AS VARCHAR) ~ '^-?[0-9]+$' THEN (CAST(bitmap AS INTEGER) | 2) ELSE 0 END",
$this->_conn->buildClause('bitmap', '|', 2));
$this->assertEquals(
array("CASE WHEN CAST(bitmap AS VARCHAR) ~ '^-?[0-9]+$' THEN (CAST(bitmap AS INTEGER) | ?) ELSE 0 END", array(2)),
$this->_conn->buildClause('bitmap', '|', 2, true));
$this->assertEquals(
"name ILIKE '%search%'",
$this->_conn->buildClause('name', 'LIKE', "search"));
$this->assertEquals(
array("name ILIKE ?", array('%search%')),
$this->_conn->buildClause('name', 'LIKE', "search", true));
$this->assertEquals(
"name ILIKE '%search\&replace\?%'",
$this->_conn->buildClause('name', 'LIKE', "search&replace?"));
$this->assertEquals(
array("name ILIKE ?", array('%search&replace?%')),
$this->_conn->buildClause('name', 'LIKE', "search&replace?", true));
$this->assertEquals(
"(name ILIKE 'search\&replace\?%' OR name ILIKE '% search\&replace\?%')",
$this->_conn->buildClause('name', 'LIKE', "search&replace?", false, array('begin' => true)));
$this->assertEquals(
array("(name ILIKE ? OR name ILIKE ?)",
array('search&replace?%', '% search&replace?%')),
$this->_conn->buildClause('name', 'LIKE', "search&replace?", true, array('begin' => true)));
$this->assertEquals(
'value = 2',
$this->_conn->buildClause('value', '=', 2));
$this->assertEquals(
array('value = ?', array(2)),
$this->_conn->buildClause('value', '=', 2, true));
$this->assertEquals(
"value = 'foo'",
$this->_conn->buildClause('value', '=', 'foo'));
$this->assertEquals(
array('value = ?', array('foo')),
$this->_conn->buildClause('value', '=', 'foo', true));
$this->assertEquals(
"value = 'foo\?bar'",
$this->_conn->buildClause('value', '=', 'foo?bar'));
$this->assertEquals(
array('value = ?', array('foo?bar')),
$this->_conn->buildClause('value', '=', 'foo?bar', true));
}
/*##########################################################################
# Protected
##########################################################################*/
/**
* Create table to perform tests on
*/
protected function _createTestTable($name, $options = array())
{
parent::_createTestTable($name, $options = array());
try {
// make sure table was created
$sql = "INSERT INTO $name
VALUES (1, 'mlb', 'f')";
$this->_conn->insert($sql);
} catch (Exception $e) {
}
}
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/Pdo/SqliteTest.php 0000664 0001750 0001750 00000036416 13054774261 020573 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
class Horde_Db_Adapter_Pdo_SqliteTest extends Horde_Db_Adapter_TestBase
{
public static function setUpBeforeClass()
{
self::$_reason = 'The sqlite adapter is not available';
if (extension_loaded('pdo') &&
in_array('sqlite', PDO::getAvailableDrivers())) {
self::$_skip = false;
list($conn,) = static::_getConnection();
$conn->disconnect();
}
self::$_columnTest = new Horde_Db_Adapter_Sqlite_ColumnDefinition();
self::$_tableTest = new Horde_Db_Adapter_Sqlite_TestTableDefinition();
}
protected static function _getConnection($overrides = array())
{
$config = array(
'dbname' => ':memory:',
);
$config = array_merge($config, $overrides);
$conn = new Horde_Db_Adapter_Pdo_Sqlite($config);
$cache = new Horde_Cache(new Horde_Cache_Storage_Mock());
$conn->setCache($cache);
//$conn->setLogger(new Horde_Log_Logger(new Horde_Log_Handler_Cli()));
return array($conn, $cache);
}
/*##########################################################################
# Accessor
##########################################################################*/
public function testAdapterName()
{
$this->assertEquals('PDO_SQLite', $this->_conn->adapterName());
}
public function testSupportsMigrations()
{
$this->assertTrue($this->_conn->supportsMigrations());
}
public function testSupportsCountDistinct()
{
$version = $this->_conn->selectValue('SELECT sqlite_version(*)');
if ($version >= '3.2.6') {
$this->assertTrue($this->_conn->supportsCountDistinct());
} else {
$this->assertFalse($this->_conn->supportsCountDistinct());
}
}
public function testSupportsInterval()
{
$this->assertFalse($this->_conn->supportsInterval());
}
/*##########################################################################
# Quoting
##########################################################################*/
public function testQuoteNull()
{
$this->assertEquals('NULL', $this->_conn->quote(null));
}
public function testQuoteTrue()
{
$this->assertEquals('1', $this->_conn->quote(true));
}
public function testQuoteFalse()
{
$this->assertEquals('0', $this->_conn->quote(false));
}
public function testQuoteInteger()
{
$this->assertEquals('42', $this->_conn->quote(42));
}
public function testQuoteFloat()
{
$this->assertEquals('42.2', $this->_conn->quote(42.2));
setlocale(LC_NUMERIC, 'de_DE.UTF-8');
$this->assertEquals('42.2', $this->_conn->quote(42.2));
}
public function testQuoteString()
{
$this->assertEquals("'my string'", $this->_conn->quote('my string'));
}
public function testQuoteDirtyString()
{
$this->assertEquals("'derek''s string'", $this->_conn->quote('derek\'s string'));
}
public function testQuoteColumnName()
{
$col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'int(11)');
$this->assertEquals('1', $this->_conn->quote(true, $col));
}
/*##########################################################################
# Schema Statements
##########################################################################*/
public function testNativeDatabaseTypes()
{
$types = $this->_conn->nativeDatabaseTypes();
$this->assertEquals(array('name' => 'int', 'limit' => null), $types['integer']);
}
public function testTableAliasLength()
{
$len = $this->_conn->tableAliasLength();
$this->assertEquals(255, $len);
}
public function testColumns()
{
$col = parent::testColumns();
$this->assertEquals(null, $col->getLimit());
$this->assertEquals('INTEGER', $col->getSqlType());
}
public function testCreateTableWithSeparatePk()
{
$pkColumn = parent::testCreateTableWithSeparatePk();
$this->assertEquals('"foo" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL', $pkColumn->toSql());
}
public function testChangeColumnType()
{
$this->_createTestTable('sports');
$beforeChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('boolean', $beforeChange->getSqlType());
$this->_conn->changeColumn('sports', 'is_college', 'string');
$afterChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('varchar(255)', $afterChange->getSqlType());
$table = $this->_conn->createTable('text_to_binary');
$table->column('data', 'text');
$table->end();
$this->_conn->insert('INSERT INTO text_to_binary (data) VALUES (?)',
array("foo"));
$this->_conn->changeColumn('text_to_binary', 'data', 'binary');
$afterChange = $this->_getColumn('text_to_binary', 'data');
$this->assertEquals('blob', $afterChange->getSqlType());
$this->assertEquals(
"foo",
$this->_conn->selectValue('SELECT data FROM text_to_binary'));
}
public function testChangeColumnLimit()
{
$this->_createTestTable('sports');
$beforeChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('boolean', $beforeChange->getSqlType());
$this->_conn->changeColumn('sports', 'is_college', 'string',
array('limit' => '40'));
$afterChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('varchar(40)', $afterChange->getSqlType());
}
public function testChangeColumnPrecisionScale()
{
$this->_createTestTable('sports');
$beforeChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('boolean', $beforeChange->getSqlType());
$this->_conn->changeColumn('sports', 'is_college', 'decimal',
array('precision' => '5', 'scale' => '2'));
$afterChange = $this->_getColumn('sports', 'is_college');
$this->assertRegExp('/^decimal\(5,\s*2\)$/', $afterChange->getSqlType());
}
public function testRenameColumn()
{
$this->_createTestUsersTable();
$this->_conn->renameColumn('users', 'first_name', 'nick_name');
$this->assertTrue(in_array('nick_name', $this->_columnNames('users')));
$this->_createTestTable('sports');
$beforeChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('boolean', $beforeChange->getSqlType());
$this->_conn->renameColumn('sports', 'is_college', 'is_renamed');
$afterChange = $this->_getColumn('sports', 'is_renamed');
$this->assertEquals('boolean', $afterChange->getSqlType());
}
public function testTypeToSqlTypePrimaryKey()
{
$result = $this->_conn->typeToSql('autoincrementKey');
$this->assertEquals('INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL', $result);
}
public function testTypeToSqlTypeString()
{
$result = $this->_conn->typeToSql('string');
$this->assertEquals('varchar(255)', $result);
}
public function testTypeToSqlTypeText()
{
$result = $this->_conn->typeToSql('text');
$this->assertEquals('text', $result);
}
public function testTypeToSqlTypeBinary()
{
$result = $this->_conn->typeToSql('binary');
$this->assertEquals('blob', $result);
}
public function testTypeToSqlTypeFloat()
{
$result = $this->_conn->typeToSql('float');
$this->assertEquals('float', $result);
}
public function testTypeToSqlTypeDatetime()
{
$result = $this->_conn->typeToSql('datetime');
$this->assertEquals('datetime', $result);
}
public function testTypeToSqlTypeTimestamp()
{
$result = $this->_conn->typeToSql('timestamp');
$this->assertEquals('datetime', $result);
}
public function testTypeToSqlInt()
{
$result = $this->_conn->typeToSql('integer');
$this->assertEquals('int', $result);
}
public function testTypeToSqlIntLimit()
{
$result = $this->_conn->typeToSql('integer', '1');
$this->assertEquals('int(1)', $result);
}
public function testTypeToSqlDecimalPrecision()
{
$result = $this->_conn->typeToSql('decimal', null, '5');
$this->assertEquals('decimal(5)', $result);
}
public function testTypeToSqlDecimalScale()
{
$result = $this->_conn->typeToSql('decimal', null, '5', '2');
$this->assertEquals('decimal(5, 2)', $result);
}
public function testTypeToSqlBoolean()
{
$result = $this->_conn->typeToSql('boolean');
$this->assertEquals('boolean', $result);
}
public function testAddColumnOptions()
{
$result = $this->_conn->addColumnOptions('test', array());
$this->assertEquals('test', $result);
}
public function testAddColumnOptionsDefault()
{
$options = array('default' => '0');
$result = $this->_conn->addColumnOptions('test', $options);
$this->assertEquals("test DEFAULT '0'", $result);
}
public function testAddColumnOptionsNull()
{
$options = array('null' => true);
$result = $this->_conn->addColumnOptions('test', $options);
$this->assertEquals('test', $result);
}
public function testAddColumnOptionsNotNull()
{
$options = array('null' => false);
$result = $this->_conn->addColumnOptions('test', $options);
$this->assertEquals('test NOT NULL', $result);
}
public function testModifyDate()
{
$modifiedDate = $this->_conn->modifyDate('start', '+', 1, 'DAY');
$this->assertEquals('datetime(start, \'+1 days\')', $modifiedDate);
$t = $this->_conn->createTable('dates');
$t->column('start', 'datetime');
$t->column('end', 'datetime');
$t->end();
$this->_conn->insert(
'INSERT INTO dates (start, end) VALUES (?, ?)',
array(
'2011-12-10 00:00:00',
'2011-12-11 00:00:00'
)
);
$this->assertEquals(
1,
$this->_conn->selectValue('SELECT COUNT(*) FROM dates WHERE '
. $modifiedDate . ' = end')
);
$this->assertEquals(
'datetime(start, \'+2 seconds\')',
$this->_conn->modifyDate('start', '+', 2, 'SECOND'));
$this->assertEquals(
'datetime(start, \'+3 minutes\')',
$this->_conn->modifyDate('start', '+', 3, 'MINUTE'));
$this->assertEquals(
'datetime(start, \'+4 hours\')',
$this->_conn->modifyDate('start', '+', 4, 'HOUR'));
$this->assertEquals(
'datetime(start, \'-2 months\')',
$this->_conn->modifyDate('start', '-', 2, 'MONTH'));
$this->assertEquals(
'datetime(start, \'-3 years\')',
$this->_conn->modifyDate('start', '-', 3, 'YEAR'));
}
public function testBuildClause()
{
$this->assertEquals(
'bitmap & 2',
$this->_conn->buildClause('bitmap', '&', 2));
$this->assertEquals(
array('bitmap & ?', array(2)),
$this->_conn->buildClause('bitmap', '&', 2, true));
$this->assertEquals(
'bitmap | 2',
$this->_conn->buildClause('bitmap', '|', 2));
$this->assertEquals(
array('bitmap | ?', array(2)),
$this->_conn->buildClause('bitmap', '|', 2, true));
$this->assertEquals(
"LOWER(name) LIKE LOWER('%search%')",
$this->_conn->buildClause('name', 'LIKE', "search"));
$this->assertEquals(
array("LOWER(name) LIKE LOWER(?)", array('%search%')),
$this->_conn->buildClause('name', 'LIKE', "search", true));
$this->assertEquals(
"LOWER(name) LIKE LOWER('%search\&replace\?%')",
$this->_conn->buildClause('name', 'LIKE', "search&replace?"));
$this->assertEquals(
array("LOWER(name) LIKE LOWER(?)", array('%search&replace?%')),
$this->_conn->buildClause('name', 'LIKE', "search&replace?", true));
$this->assertEquals(
"(LOWER(name) LIKE LOWER('search\&replace\?%') OR LOWER(name) LIKE LOWER('% search\&replace\?%'))",
$this->_conn->buildClause('name', 'LIKE', "search&replace?", false, array('begin' => true)));
$this->assertEquals(
array("(LOWER(name) LIKE LOWER(?) OR LOWER(name) LIKE LOWER(?))",
array('search&replace?%', '% search&replace?%')),
$this->_conn->buildClause('name', 'LIKE', "search&replace?", true, array('begin' => true)));
$this->assertEquals(
'value = 2',
$this->_conn->buildClause('value', '=', 2));
$this->assertEquals(
array('value = ?', array(2)),
$this->_conn->buildClause('value', '=', 2, true));
$this->assertEquals(
"value = 'foo'",
$this->_conn->buildClause('value', '=', 'foo'));
$this->assertEquals(
array('value = ?', array('foo')),
$this->_conn->buildClause('value', '=', 'foo', true));
$this->assertEquals(
"value = 'foo\?bar'",
$this->_conn->buildClause('value', '=', 'foo?bar'));
$this->assertEquals(
array('value = ?', array('foo?bar')),
$this->_conn->buildClause('value', '=', 'foo?bar', true));
}
public function testInsertAndReadInCp1257()
{
list($conn,) = static::_getConnection(array('charset' => 'cp1257'));
$table = $conn->createTable('charset_cp1257');
$table->column('text', 'string');
$table->end();
$input = file_get_contents(__DIR__ . '/../../fixtures/charsets/cp1257.txt');
$conn->insert('INSERT INTO charset_cp1257 (text) VALUES (?)', array($input));
$output = $conn->selectValue('SELECT text FROM charset_cp1257');
$this->assertEquals($input, $output);
}
/*##########################################################################
# Protected
##########################################################################*/
/**
* Create table to perform tests on
*/
protected function _createTestTable($name, $options = array())
{
parent::_createTestTable($name, $options = array());
try {
// make sure table was created
$sql = "INSERT INTO $name
VALUES (1, 'mlb', 0)";
$this->_conn->insert($sql);
} catch (Exception $e) {
}
}
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/Postgresql/ColumnDefinition.php 0000664 0001750 0001750 00000006662 13054774261 023361 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
class Horde_Db_Adapter_Postgresql_ColumnDefinition extends Horde_Test_Case
{
public $conn;
public function testConstruct()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$this->assertEquals('col_name', $col->getName());
$this->assertEquals('string', $col->getType());
}
public function testToSql()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$this->assertEquals('"col_name" character varying(255)', $col->toSql());
}
public function testToSqlLimit()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string', 40
);
$this->assertEquals('"col_name" character varying(40)', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$col->setLimit(40);
$this->assertEquals('"col_name" character varying(40)', $col->toSql());
}
public function testToSqlPrecisionScale()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'decimal', null, 5, 2
);
$this->assertEquals('"col_name" decimal(5, 2)', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'decimal'
);
$col->setPrecision(5);
$col->setScale(2);
$this->assertEquals('"col_name" decimal(5, 2)', $col->toSql());
}
public function testToSqlNotNull()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string', null, null, null, null, null, false
);
$this->assertEquals('"col_name" character varying(255) NOT NULL', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$col->setNull(false);
$this->assertEquals('"col_name" character varying(255) NOT NULL', $col->toSql());
}
public function testToSqlDefault()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string', null, null, null, null, 'test'
);
$this->assertEquals('"col_name" character varying(255) DEFAULT \'test\'', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$col->setDefault('test');
$this->assertEquals('"col_name" character varying(255) DEFAULT \'test\'', $col->toSql());
}
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/Postgresql/ColumnTest.php 0000664 0001750 0001750 00000012115 13054774261 022176 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
class Horde_Db_Adapter_Postgresql_ColumnTest extends Horde_Db_Adapter_ColumnBase
{
protected $_class = 'Horde_Db_Adapter_Postgresql_Column';
/*##########################################################################
# Construction
##########################################################################*/
public function testDefaultNull()
{
$col = new Horde_Db_Adapter_Postgresql_Column('name', 'NULL', 'character varying(255)');
$this->assertEquals(true, $col->isNull());
}
public function testNotNull()
{
$col = new Horde_Db_Adapter_Postgresql_Column('name', 'NULL', 'character varying(255)', false);
$this->assertEquals(false, $col->isNull());
}
public function testName()
{
$col = new Horde_Db_Adapter_Postgresql_Column('name', 'NULL', 'character varying(255)');
$this->assertEquals('name', $col->getName());
}
public function testSqlType()
{
$col = new Horde_Db_Adapter_Postgresql_Column('name', 'NULL', 'character varying(255)');
$this->assertEquals('character varying(255)', $col->getSqlType());
}
public function testIsText()
{
$col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'character varying(255)');
$this->assertTrue($col->isText());
$col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'text');
$this->assertTrue($col->isText());
$col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'int(11)');
$this->assertFalse($col->isText());
$col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'float(11,1)');
$this->assertFalse($col->isText());
}
public function testIsNumber()
{
$col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'character varying(255)');
$this->assertFalse($col->isNumber());
$col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'text');
$this->assertFalse($col->isNumber());
$col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'int(11)');
$this->assertTrue($col->isNumber());
$col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'float(11,1)');
$this->assertTrue($col->isNumber());
}
/*##########################################################################
# Types
##########################################################################*/
public function testTypeString()
{
$col = new Horde_Db_Adapter_Postgresql_Column('name', 'NULL', 'character varying(255)');
$this->assertEquals('string', $col->getType());
}
/*##########################################################################
# Extract Limit
##########################################################################*/
public function testExtractLimitVarchar()
{
$col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'character varying(255)');
$this->assertEquals(255, $col->getLimit());
}
/*##########################################################################
# Type Cast Values
##########################################################################*/
public function testTypeCastString()
{
$col = new Horde_Db_Adapter_Postgresql_Column('name', "'n/a'::character varying", 'character varying(255)', false);
$this->assertEquals('n/a', $col->getDefault());
}
public function testTypeCastBooleanFalse()
{
$col = new Horde_Db_Adapter_Postgresql_Column('is_active', '0', 'boolean', false);
$this->assertSame(false, $col->getDefault());
}
public function testTypeCastBooleanTrue()
{
$col = new Horde_Db_Adapter_Postgresql_Column('is_active', '1', 'boolean', false);
$this->assertSame(true, $col->getDefault());
}
/*##########################################################################
# Column Types
##########################################################################*/
/*@TODO tests for PostgreSQL-specific column types */
/*##########################################################################
# Defaults
##########################################################################*/
public function testDefaultString()
{
$col = new Horde_Db_Adapter_Postgresql_Column('name', '', 'character varying(255)');
$this->assertEquals('', $col->getDefault());
}
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/Postgresql/TestTableDefinition.php 0000664 0001750 0001750 00000001433 13054774261 024002 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
class Horde_Db_Adapter_Postgresql_TestTableDefinition extends Horde_Db_Adapter_TestTableDefinition
{
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/Sqlite/ColumnDefinition.php 0000664 0001750 0001750 00000007166 13054774261 022457 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
class Horde_Db_Adapter_Sqlite_ColumnDefinition extends Horde_Test_Case
{
public $conn;
public function testConstruct()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$this->assertEquals('col_name', $col->getName());
$this->assertEquals('string', $col->getType());
}
public function testToSql()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$this->assertEquals('"col_name" varchar(255)', $col->toSql());
}
public function testToSqlLimit()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string', 40
);
$this->assertEquals('"col_name" varchar(40)', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$col->setLimit(40);
$this->assertEquals('"col_name" varchar(40)', $col->toSql());
}
public function testToSqlPrecisionScale()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'decimal', null, 5, 2
);
$this->assertEquals('"col_name" decimal(5, 2)', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'decimal'
);
$col->setPrecision(5);
$col->setScale(2);
$this->assertEquals('"col_name" decimal(5, 2)', $col->toSql());
}
public function testToSqlNotNull()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string', null, null, null, null, null, false
);
$this->assertEquals('"col_name" varchar(255) NOT NULL', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$col->setNull(false);
$this->assertEquals('"col_name" varchar(255) NOT NULL', $col->toSql());
// set attribute to the default (true)
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$col->setNull(true);
$this->assertEquals('"col_name" varchar(255)', $col->toSql());
}
public function testToSqlDefault()
{
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string', null, null, null, null, 'test', null
);
$this->assertEquals('"col_name" varchar(255) DEFAULT \'test\'', $col->toSql());
// set attribute instead
$col = new Horde_Db_Adapter_Base_ColumnDefinition(
$this->conn, 'col_name', 'string'
);
$col->setDefault('test');
$this->assertEquals('"col_name" varchar(255) DEFAULT \'test\'', $col->toSql());
}
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/Sqlite/ColumnTest.php 0000664 0001750 0001750 00000002612 13054774261 021275 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
class Horde_Db_Adapter_Sqlite_ColumnTest extends Horde_Db_Adapter_ColumnBase
{
protected $_class = 'Horde_Db_Adapter_Sqlite_Column';
/*##########################################################################
# Type Cast Values
##########################################################################*/
public function testTypeCastBooleanFalse()
{
$col = new Horde_Db_Adapter_Sqlite_Column('is_active', 'f', 'boolean', false);
$this->assertSame(false, $col->getDefault());
}
public function testTypeCastBooleanTrue()
{
$col = new Horde_Db_Adapter_Sqlite_Column('is_active', 't', 'boolean', false);
$this->assertSame(true, $col->getDefault());
}
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/Sqlite/TestTableDefinition.php 0000664 0001750 0001750 00000001427 13054774261 023103 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
class Horde_Db_Adapter_Sqlite_TestTableDefinition extends Horde_Db_Adapter_TestTableDefinition
{
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/ColumnBase.php 0000664 0001750 0001750 00000017551 13054774261 017777 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
abstract class Horde_Db_Adapter_ColumnBase extends Horde_Test_Case
{
protected $_class;
/*##########################################################################
# Construction
##########################################################################*/
public function testDefaultNull()
{
$col = new $this->_class('name', 'NULL', 'varchar(255)');
$this->assertEquals(true, $col->isNull());
}
public function testNotNull()
{
$col = new $this->_class('name', 'NULL', 'varchar(255)', false);
$this->assertEquals(false, $col->isNull());
}
public function testName()
{
$col = new $this->_class('name', 'NULL', 'varchar(255)');
$this->assertEquals('name', $col->getName());
}
public function testSqlType()
{
$col = new $this->_class('name', 'NULL', 'varchar(255)');
$this->assertEquals('varchar(255)', $col->getSqlType());
}
public function testIsText()
{
$col = new $this->_class('test', 'NULL', 'varchar(255)');
$this->assertTrue($col->isText());
$col = new $this->_class('test', 'NULL', 'text');
$this->assertTrue($col->isText());
$col = new $this->_class('test', 'NULL', 'int(11)');
$this->assertFalse($col->isText());
$col = new $this->_class('test', 'NULL', 'float(11,1)');
$this->assertFalse($col->isText());
}
public function testIsNumber()
{
$col = new $this->_class('test', 'NULL', 'varchar(255)');
$this->assertFalse($col->isNumber());
$col = new $this->_class('test', 'NULL', 'text');
$this->assertFalse($col->isNumber());
$col = new $this->_class('test', 'NULL', 'int(11)');
$this->assertTrue($col->isNumber());
$col = new $this->_class('test', 'NULL', 'float(11,1)');
$this->assertTrue($col->isNumber());
}
/*##########################################################################
# Types
##########################################################################*/
public function testTypeInteger()
{
$col = new $this->_class('age', 'NULL', 'int(11)');
$this->assertEquals('integer', $col->getType());
}
public function testTypeFloat()
{
$col = new $this->_class('age', 'NULL', 'float(11,1)');
$this->assertEquals('float', $col->getType());
}
public function testTypeDecimalPrecisionNone()
{
$col = new $this->_class('age', 'NULL', 'decimal(11,0)');
$this->assertEquals('integer', $col->getType());
}
public function testTypeDecimal()
{
$col = new $this->_class('age', 'NULL', 'decimal(11,1)');
$this->assertEquals('decimal', $col->getType());
}
public function testTypeDatetime()
{
$col = new $this->_class('age', 'NULL', 'datetime');
$this->assertEquals('datetime', $col->getType());
}
public function testTypeTimestamp()
{
$col = new $this->_class('age', 'CURRENT_TIMESTAMP', 'timestamp');
$this->assertEquals('timestamp', $col->getType());
}
public function testTypeTime()
{
$col = new $this->_class('age', 'NULL', 'time');
$this->assertEquals('time', $col->getType());
}
public function testTypeDate()
{
$col = new $this->_class('age', 'NULL', 'date');
$this->assertEquals('date', $col->getType());
}
public function testTypeText()
{
$col = new $this->_class('age', 'NULL', 'text');
$this->assertEquals('text', $col->getType());
}
public function testTypeBinary()
{
$col = new $this->_class('age', 'NULL', 'blob(255)');
$this->assertEquals('binary', $col->getType());
}
public function testTypeString()
{
$col = new $this->_class('name', 'NULL', 'varchar(255)');
$this->assertEquals('string', $col->getType());
}
/*##########################################################################
# Extract Limit
##########################################################################*/
public function testExtractLimitInt()
{
$col = new $this->_class('test', 'NULL', 'int(11)');
$this->assertEquals(11, $col->getLimit());
}
public function testExtractLimitVarchar()
{
$col = new $this->_class('test', 'NULL', 'varchar(255)');
$this->assertEquals(255, $col->getLimit());
}
public function testExtractLimitDecimal()
{
$col = new $this->_class('test', 'NULL', 'decimal(11,1)');
$this->assertEquals('11', $col->getLimit());
}
public function testExtractLimitText()
{
$col = new $this->_class('test', 'NULL', 'text');
$this->assertEquals(null, $col->getLimit());
}
public function testExtractLimitNone()
{
$col = new $this->_class('test', 'NULL');
$this->assertEquals(null, $col->getLimit());
}
/*##########################################################################
# Extract Precision/Scale
##########################################################################*/
public function testExtractPrecisionScale()
{
$col = new $this->_class('test', 'NULL', 'decimal(12,1)');
$this->assertEquals('12', $col->precision());
$this->assertEquals('1', $col->scale());
}
/*##########################################################################
# Type Cast Values
##########################################################################*/
public function testTypeCastInteger()
{
$col = new $this->_class('name', '1', 'int(11)', false);
$this->assertEquals(1, $col->getDefault());
}
public function testTypeCastFloat()
{
$col = new $this->_class('version', '1.0', 'float(11,1)', false);
$this->assertEquals(1.0, $col->getDefault());
}
public function testTypeCastString()
{
$col = new $this->_class('name', 'n/a', 'varchar(255)', false);
$this->assertEquals('n/a', $col->getDefault());
}
abstract public function testTypeCastBooleanFalse();
abstract public function testTypeCastBooleanTrue();
/*##########################################################################
# Defaults
##########################################################################*/
public function testDefaultDatetime()
{
$col = new $this->_class('name', '', 'datetime');
$this->assertEquals(null, $col->getDefault());
}
public function testDefaultInteger()
{
$col = new $this->_class('name', '', 'int(11)');
$this->assertEquals(null, $col->getDefault());
}
public function testDefaultString()
{
$col = new $this->_class('name', '', 'varchar(255)');
$this->assertEquals('', $col->getDefault());
}
public function testDefaultText()
{
$col = new $this->_class('name', '', 'text');
$this->assertEquals('', $col->getDefault());
}
public function testDefaultBinary()
{
$col = new $this->_class('name', '', 'blob(255)');
$this->assertEquals('', $col->getDefault());
}
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/conf.php.dist 0000664 0001750 0001750 00000002367 13054774261 017635 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @category Horde
* @package Db
* @subpackage UnitTests
*/
/**
* @author Mike Naberezny
* @author Derek DeVries
* @author Chuck Hagenbuch
* @author Jan Schneider
* @license http://www.horde.org/licenses/bsd
* @group horde_db
* @category Horde
* @package Db
* @subpackage UnitTests
*/
abstract class Horde_Db_Adapter_MysqlBase extends Horde_Db_Adapter_TestBase
{
protected static function _available()
{
throw new LogicException('_available() must be implemented in a sub-class.');
}
public static function setUpBeforeClass()
{
self::$_reason = 'The MySQL adapter is not available';
if (static::_available()) {
self::$_skip = false;
list($conn,) = static::_getConnection();
if (self::$_skip) {
return;
}
$conn->disconnect();
}
self::$_columnTest = new Horde_Db_Adapter_Mysql_ColumnDefinition();
self::$_tableTest = new Horde_Db_Adapter_Mysql_TestTableDefinition();
}
/*##########################################################################
# Accessor
##########################################################################*/
public function testSupportsMigrations()
{
$this->assertTrue($this->_conn->supportsMigrations());
}
public function testSupportsCountDistinct()
{
$this->assertTrue($this->_conn->supportsCountDistinct());
}
public function testSupportsInterval()
{
$this->assertTrue($this->_conn->supportsInterval());
}
public function testGetCharset()
{
$this->assertEquals('utf8', Horde_String::lower($this->_conn->getCharset()));
}
/*##########################################################################
# Database Statements
##########################################################################*/
public function testTransactionCommit()
{
parent::testTransactionCommit();
// query without transaction and with new connection (see bug #10578).
$sql = "INSERT INTO unit_tests (id, integer_value) VALUES (8, 1000)";
$this->_conn->insert($sql);
// make sure it inserted
$this->_conn->reconnect();
$sql = "SELECT integer_value FROM unit_tests WHERE id='8'";
$this->assertEquals('1000', $this->_conn->selectValue($sql));
}
public function testTransactionRollback()
{
parent::testTransactionRollback();
// query without transaction and with new connection (see bug #10578).
$sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)";
$this->_conn->insert($sql, null, null, 'id', 7);
// make sure it inserted
$this->_conn->reconnect();
$sql = "SELECT integer_value FROM unit_tests WHERE id='7'";
$this->assertEquals(999, $this->_conn->selectValue($sql));
}
/*##########################################################################
# Quoting
##########################################################################*/
public function testQuoteNull()
{
$this->assertEquals('NULL', $this->_conn->quote(null));
}
public function testQuoteTrue()
{
$this->assertEquals('1', $this->_conn->quote(true));
}
public function testQuoteFalse()
{
$this->assertEquals('0', $this->_conn->quote(false));
}
public function testQuoteInteger()
{
$this->assertEquals('42', $this->_conn->quote(42));
}
public function testQuoteFloat()
{
$this->assertEquals('42.2', $this->_conn->quote(42.2));
setlocale(LC_NUMERIC, 'de_DE.UTF-8');
$this->assertEquals('42.2', $this->_conn->quote(42.2));
}
public function testQuoteString()
{
$this->assertEquals("'my string'", $this->_conn->quote('my string'));
}
public function testQuoteDirtyString()
{
$this->assertEquals("'derek\'s string'", $this->_conn->quote('derek\'s string'));
}
public function testQuoteColumnName()
{
$col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'int(11)');
$this->assertEquals('1', $this->_conn->quote(true, $col));
}
/*##########################################################################
# Schema Statements
##########################################################################*/
/**
* We specifically do a manual INSERT here, and then test only the SELECT
* functionality. This allows us to more easily catch INSERT being broken,
* but SELECT actually working fine.
*/
public function testNativeDecimalInsertManualVsAutomatic()
{
$this->_createTestUsersTable();
$correctValue = 12345678901234567890.0123456789;
$this->_conn->addColumn("users", "wealth", 'decimal', array('precision' => 30, 'scale' => 10));
// do a manual insertion
$this->_conn->execute("INSERT INTO users (wealth) VALUES ('12345678901234567890.0123456789')");
// SELECT @todo - type cast attribute values
$user = (object)$this->_conn->selectOne('SELECT * FROM users');
// assert_kind_of BigDecimal, row.wealth
// If this assert fails, that means the SELECT is broken!
$this->assertEquals($correctValue, $user->wealth);
// Reset to old state
$this->_conn->delete('DELETE FROM users');
// Now use the Adapter insertion
$this->_conn->insert('INSERT INTO users (wealth) VALUES (12345678901234567890.0123456789)');
// SELECT @todo - type cast attribute values
$user = (object)$this->_conn->selectOne('SELECT * FROM users');
// assert_kind_of BigDecimal, row.wealth
// If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
$this->assertEquals($correctValue, $user->wealth);
}
public function testNativeTypes()
{
$this->_createTestUsersTable();
$this->_conn->addColumn("users", "last_name", 'string');
$this->_conn->addColumn("users", "bio", 'text');
$this->_conn->addColumn("users", "age", 'integer');
$this->_conn->addColumn("users", "height", 'float');
$this->_conn->addColumn("users", "wealth", 'decimal', array('precision' => '30', 'scale' => '10'));
$this->_conn->addColumn("users", "birthday", 'datetime');
$this->_conn->addColumn("users", "favorite_day", 'date');
$this->_conn->addColumn("users", "moment_of_truth", 'datetime');
$this->_conn->addColumn("users", "male", 'boolean');
$this->_conn->insert('INSERT INTO users (first_name, last_name, bio, age, height, wealth, birthday, favorite_day, moment_of_truth, male, company_id) ' .
"VALUES ('bob', 'bobsen', 'I was born ....', 18, 1.78, 12345678901234567890.0123456789, '2005-01-01 12:23:40', '1980-03-05', '1582-10-10 21:40:18', 1, 1)");
$bob = (object)$this->_conn->selectOne('SELECT * FROM users');
$this->assertEquals('bob', $bob->first_name);
$this->assertEquals('bobsen', $bob->last_name);
$this->assertEquals('I was born ....', $bob->bio);
$this->assertEquals(18, $bob->age);
// Test for 30 significent digits (beyond the 16 of float), 10 of them
// after the decimal place.
$this->assertEquals('12345678901234567890.0123456789', $bob->wealth);
$this->assertEquals(1, $bob->male);
// @todo - type casting
}
public function testNativeDatabaseTypes()
{
$types = $this->_conn->nativeDatabaseTypes();
$this->assertEquals(array('name' => 'int', 'limit' => 11), $types['integer']);
}
public function testUnabstractedDatabaseDependentTypes()
{
$this->_createTestUsersTable();
$this->_conn->delete('DELETE FROM users');
$this->_conn->addColumn('users', 'intelligence_quotient', 'tinyint');
try {
$this->_conn->insert('INSERT INTO users (intelligence_quotient) VALUES (300)');
$jonnyg = (object)$this->_conn->selectOne('SELECT * FROM users');
$this->assertEquals('127', $jonnyg->intelligence_quotient);
} catch (Horde_Db_Exception $e) {
if (strpos($e->getMessage(), "Out of range value for column 'intelligence_quotient' at row 1") === false) {
throw $e;
}
}
}
public function testTableAliasLength()
{
$len = $this->_conn->tableAliasLength();
$this->assertEquals(255, $len);
}
public function testColumns()
{
$col = parent::testColumns();
$this->assertEquals(10, $col->getLimit());
$this->assertEquals(true, $col->isUnsigned());
$this->assertEquals('int(10) unsigned', $col->getSqlType());
}
public function testCreateTableWithSeparatePk()
{
$pkColumn = parent::testCreateTableWithSeparatePk();
$this->assertEquals('`foo` int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', $pkColumn->toSql());
}
public function testChangeColumnType()
{
$this->_createTestTable('sports');
$beforeChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('tinyint(1)', $beforeChange->getSqlType());
$this->_conn->changeColumn('sports', 'is_college', 'string');
$afterChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('varchar(255)', $afterChange->getSqlType());
$table = $this->_conn->createTable('text_to_binary');
$table->column('data', 'text');
$table->end();
$this->_conn->insert('INSERT INTO text_to_binary (data) VALUES (?)',
array("foo"));
$this->_conn->changeColumn('text_to_binary', 'data', 'binary');
$afterChange = $this->_getColumn('text_to_binary', 'data');
$this->assertEquals('longblob', $afterChange->getSqlType());
$this->assertEquals(
"foo",
$this->_conn->selectValue('SELECT data FROM text_to_binary'));
}
public function testChangeColumnLimit()
{
$this->_createTestTable('sports');
$beforeChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('tinyint(1)', $beforeChange->getSqlType());
$this->_conn->changeColumn('sports', 'is_college', 'string',
array('limit' => '40'));
$afterChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('varchar(40)', $afterChange->getSqlType());
}
public function testChangeColumnPrecisionScale()
{
$this->_createTestTable('sports');
$beforeChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('tinyint(1)', $beforeChange->getSqlType());
$this->_conn->changeColumn('sports', 'is_college', 'decimal',
array('precision' => '5', 'scale' => '2'));
$afterChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('decimal(5,2)', $afterChange->getSqlType());
}
public function testChangeColumnUnsigned()
{
$table = $this->_conn->createTable('testings');
$table->column('foo', 'integer');
$table->end();
$beforeChange = $this->_getColumn('testings', 'foo');
$this->assertFalse($beforeChange->isUnsigned());
$this->_conn->execute('INSERT INTO testings (id, foo) VALUES (1, -1)');
try {
$this->_conn->changeColumn('testings', 'foo', 'integer', array('unsigned' => true));
$afterChange = $this->_getColumn('testings', 'foo');
$this->assertTrue($afterChange->isUnsigned());
$row = (object)$this->_conn->selectOne('SELECT * FROM testings');
$this->assertEquals(0, $row->foo);
} catch (Horde_Db_Exception $e) {
if (strpos($e->getMessage(), "Out of range value for column 'foo' at row 1") === false) {
throw $e;
}
}
}
public function testRenameColumn()
{
$this->_createTestUsersTable();
$this->_conn->renameColumn('users', 'first_name', 'nick_name');
$this->assertTrue(in_array('nick_name', $this->_columnNames('users')));
$this->_createTestTable('sports');
$beforeChange = $this->_getColumn('sports', 'is_college');
$this->assertEquals('tinyint(1)', $beforeChange->getSqlType());
$this->_conn->renameColumn('sports', 'is_college', 'is_renamed');
$afterChange = $this->_getColumn('sports', 'is_renamed');
$this->assertEquals('tinyint(1)', $afterChange->getSqlType());
}
public function testTypeToSqlTypePrimaryKey()
{
$result = $this->_conn->typeToSql('autoincrementKey');
$this->assertEquals('int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', $result);
}
public function testTypeToSqlTypeString()
{
$result = $this->_conn->typeToSql('string');
$this->assertEquals('varchar(255)', $result);
}
public function testTypeToSqlTypeText()
{
$result = $this->_conn->typeToSql('text');
$this->assertEquals('text', $result);
}
public function testTypeToSqlTypeBinary()
{
$result = $this->_conn->typeToSql('binary');
$this->assertEquals('longblob', $result);
}
public function testTypeToSqlTypeFloat()
{
$result = $this->_conn->typeToSql('float');
$this->assertEquals('float', $result);
}
public function testTypeToSqlTypeDatetime()
{
$result = $this->_conn->typeToSql('datetime');
$this->assertEquals('datetime', $result);
}
public function testTypeToSqlTypeTimestamp()
{
$result = $this->_conn->typeToSql('timestamp');
$this->assertEquals('datetime', $result);
}
public function testTypeToSqlInt()
{
$result = $this->_conn->typeToSql('integer');
$this->assertEquals('int(11)', $result);
}
public function testTypeToSqlIntUnsigned()
{
$result = $this->_conn->typeToSql('integer', null, null, null, true);
$this->assertEquals('int(10) UNSIGNED', $result);
}
public function testTypeToSqlIntLimit()
{
$result = $this->_conn->typeToSql('integer', '1');
$this->assertEquals('int(1)', $result);
}
public function testTypeToSqlDecimalPrecision()
{
$result = $this->_conn->typeToSql('decimal', null, '5');
$this->assertEquals('decimal(5)', $result);
}
public function testTypeToSqlDecimalScale()
{
$result = $this->_conn->typeToSql('decimal', null, '5', '2');
$this->assertEquals('decimal(5, 2)', $result);
}
public function testTypeToSqlBoolean()
{
$result = $this->_conn->typeToSql('boolean');
$this->assertEquals('tinyint(1)', $result);
}
public function testAddColumnOptions()
{
$result = $this->_conn->addColumnOptions("test", array());
$this->assertEquals("test", $result);
}
public function testAddColumnOptionsDefault()
{
$options = array('default' => '0');
$result = $this->_conn->addColumnOptions("test", $options);
$this->assertEquals("test DEFAULT '0'", $result);
}
public function testAddColumnOptionsNull()
{
$options = array('null' => true);
$result = $this->_conn->addColumnOptions("test", $options);
$this->assertEquals("test", $result);
}
public function testAddColumnOptionsNotNull()
{
$options = array('null' => false);
$result = $this->_conn->addColumnOptions("test", $options);
$this->assertEquals("test NOT NULL", $result);
}
public function testInterval()
{
$this->assertEquals('INTERVAL 1 DAY',
$this->_conn->interval('1 DAY', ''));
}
public function testModifyDate()
{
$modifiedDate = $this->_conn->modifyDate('start', '+', 1, 'DAY');
$this->assertEquals('start + INTERVAL \'1\' DAY', $modifiedDate);
$t = $this->_conn->createTable('dates');
$t->column('start', 'datetime');
$t->column('end', 'datetime');
$t->end();
$this->_conn->insert(
'INSERT INTO dates (start, end) VALUES (?, ?)',
array(
'2011-12-10 00:00:00',
'2011-12-11 00:00:00'
)
);
$this->assertEquals(
1,
$this->_conn->selectValue('SELECT COUNT(*) FROM dates WHERE '
. $modifiedDate . ' = end')
);
}
public function testBuildClause()
{
$this->assertEquals(
'bitmap & 2',
$this->_conn->buildClause('bitmap', '&', 2));
$this->assertEquals(
array('bitmap & ?', array(2)),
$this->_conn->buildClause('bitmap', '&', 2, true));
$this->assertEquals(
'bitmap | 2',
$this->_conn->buildClause('bitmap', '|', 2));
$this->assertEquals(
array('bitmap | ?', array(2)),
$this->_conn->buildClause('bitmap', '|', 2, true));
$this->assertEquals(
"LOWER(name) LIKE LOWER('%search%')",
$this->_conn->buildClause('name', 'LIKE', "search"));
$this->assertEquals(
array("LOWER(name) LIKE LOWER(?)", array('%search%')),
$this->_conn->buildClause('name', 'LIKE', "search", true));
$this->assertEquals(
"LOWER(name) LIKE LOWER('%search\&replace\?%')",
$this->_conn->buildClause('name', 'LIKE', "search&replace?"));
$this->assertEquals(
array("LOWER(name) LIKE LOWER(?)", array('%search&replace?%')),
$this->_conn->buildClause('name', 'LIKE', "search&replace?", true));
$this->assertEquals(
"(LOWER(name) LIKE LOWER('search\&replace\?%') OR LOWER(name) LIKE LOWER('% search\&replace\?%'))",
$this->_conn->buildClause('name', 'LIKE', "search&replace?", false, array('begin' => true)));
$this->assertEquals(
array("(LOWER(name) LIKE LOWER(?) OR LOWER(name) LIKE LOWER(?))",
array('search&replace?%', '% search&replace?%')),
$this->_conn->buildClause('name', 'LIKE', "search&replace?", true, array('begin' => true)));
$this->assertEquals(
'value = 2',
$this->_conn->buildClause('value', '=', 2));
$this->assertEquals(
array('value = ?', array(2)),
$this->_conn->buildClause('value', '=', 2, true));
$this->assertEquals(
"value = 'foo'",
$this->_conn->buildClause('value', '=', 'foo'));
$this->assertEquals(
array('value = ?', array('foo')),
$this->_conn->buildClause('value', '=', 'foo', true));
$this->assertEquals(
"value = 'foo\?bar'",
$this->_conn->buildClause('value', '=', 'foo?bar'));
$this->assertEquals(
array('value = ?', array('foo?bar')),
$this->_conn->buildClause('value', '=', 'foo?bar', true));
}
public function testInsertAndReadInCp1257()
{
list($conn,) = static::_getConnection(array('charset' => 'cp1257'));
$table = $conn->createTable('charset_cp1257');
$table->column('text', 'string');
$table->end();
$input = file_get_contents(__DIR__ . '/../fixtures/charsets/cp1257.txt');
$conn->insert('INSERT INTO charset_cp1257 (text) VALUES (?)', array($input));
$output = $conn->selectValue('SELECT text FROM charset_cp1257');
$this->assertEquals($input, $output);
}
/*##########################################################################
# Protected
##########################################################################*/
/**
* Create table to perform tests on
*/
protected function _createTestTable($name, $options = array())
{
parent::_createTestTable($name, $options = array());
try {
// make sure table was created
$sql = "INSERT INTO $name
VALUES (1, 'mlb', 0)";
$this->_conn->insert($sql);
} catch (Exception $e) {
}
}
public function testColumnToSqlUnsigned()
{
self::$_columnTest->testToSqlUnsigned();
}
}
Horde_Db-2.4.0/test/Horde/Db/Adapter/MysqliTest.php 0000664 0001750 0001750 00000004436 13054774261 020063 0 ustar jan jan
* @author Derek DeVries
* @author Chuck Hagenbuch