From c8812542b123db685073286db5366f7405ae3220 Mon Sep 17 00:00:00 2001 From: samw3 Date: Tue, 16 Oct 2007 00:28:39 +0000 Subject: [PATCH] fixed newlines (I had my editor in windoze mode) --- lib/Doctrine/Export/Firebird.php | 1078 ++++++++++++------------ lib/Doctrine/Export/Frontbase.php | 618 +++++++------- lib/Doctrine/Export/Mssql.php | 504 ++++++------ lib/Doctrine/Export/Mysql.php | 1276 ++++++++++++++--------------- lib/Doctrine/Export/Oracle.php | 984 +++++++++++----------- lib/Doctrine/Export/Sqlite.php | 820 +++++++++--------- 6 files changed, 2640 insertions(+), 2640 deletions(-) diff --git a/lib/Doctrine/Export/Firebird.php b/lib/Doctrine/Export/Firebird.php index f25ca6a74..6deaf2557 100644 --- a/lib/Doctrine/Export/Firebird.php +++ b/lib/Doctrine/Export/Firebird.php @@ -1,540 +1,540 @@ -. - */ -Doctrine::autoload('Doctrine_Export'); -/** - * Doctrine_Export_Sqlite - * - * @package Doctrine - * @subpackage Export - * @author Konsta Vesterinen - * @author Lukas Smith (PEAR MDB2 library) - * @author Lorenzo Alberton (PEAR MDB2 Interbase driver) - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.phpdoctrine.com - * @since 1.0 - * @version $Revision$ - */ -class Doctrine_Export_Firebird extends Doctrine_Export -{ - /** - * create a new database - * - * @param string $name name of the database that should be created - * @return void - */ - public function createDatabase($name) - { - throw new Doctrine_Export_Exception( - 'PHP Interbase API does not support direct queries. You have to ' . - 'create the db manually by using isql command or a similar program'); - } - /** - * drop an existing database - * - * @param string $name name of the database that should be dropped - * @return void - */ - public function dropDatabase($name) - { - throw new Doctrine_Export_Exception( - 'PHP Interbase API does not support direct queries. You have ' . - 'to drop the db manually by using isql command or a similar program'); - } - /** - * add an autoincrement sequence + trigger - * - * @param string $name name of the PK field - * @param string $table name of the table - * @param string $start start value for the sequence - * @return void - */ - public function _makeAutoincrement($name, $table, $start = null) - { - if (is_null($start)) { - $this->conn->beginTransaction(); - $query = 'SELECT MAX(' . $this->conn->quoteIdentifier($name, true) . ') FROM ' . $this->conn->quoteIdentifier($table, true); - $start = $this->conn->fetchOne($query, 'integer'); - - ++$start; - $result = $this->createSequence($table, $start); - $this->conn->commit(); - } else { - $result = $this->createSequence($table, $start); - } - - $sequence_name = $this->conn->formatter->getSequenceName($table); - $trigger_name = $this->conn->quoteIdentifier($table . '_AUTOINCREMENT_PK', true); - - $table = $this->conn->quoteIdentifier($table, true); - $name = $this->conn->quoteIdentifier($name, true); - - $triggerSql = 'CREATE TRIGGER ' . $trigger_name . ' FOR ' . $table . ' - ACTIVE BEFORE INSERT POSITION 0 - AS - BEGIN - IF (NEW.' . $name . ' IS NULL OR NEW.' . $name . ' = 0) THEN - NEW.' . $name . ' = GEN_ID('.$sequence_name.', 1); - END'; - $result = $this->conn->exec($triggerSql); - - // TODO ? $this->_silentCommit(); - - return $result; - } - /** - * drop an existing autoincrement sequence + trigger - * - * @param string $table name of the table - * @return void - */ - public function _dropAutoincrement($table) - { - - $result = $this->dropSequence($table); - - //remove autoincrement trigger associated with the table - $table = $this->conn->quote(strtoupper($table)); - $triggerName = $this->conn->quote(strtoupper($table) . '_AUTOINCREMENT_PK'); - - return $this->conn->exec("DELETE FROM RDB\$TRIGGERS WHERE UPPER(RDB\$RELATION_NAME)=" . $table . " AND UPPER(RDB\$TRIGGER_NAME)=" . $triggerName); - } - /** - * create a new table - * - * @param string $name Name of the database that should be created - * @param array $fields Associative array that contains the definition of each field of the new table - * The indexes of the array entries are the names of the fields of the table an - * the array entry values are associative arrays like those that are meant to be - * passed with the field definitions to get[Type]Declaration() functions. - * - * Example - * array( - * - * 'id' => array( - * 'type' => 'integer', - * 'unsigned' => 1, - * 'notnull' => 1, - * 'default' => 0, - * ), - * 'name' => array( - * 'type' => 'text', - * 'length' => 12, - * ), - * 'description' => array( - * 'type' => 'text', - * 'length' => 12, - * ) - * ); - * @param array $options An associative array of table options: - * - * @return void - */ - public function createTable($name, array $fields, array $options = array()) { - parent::createTable($name, $fields, $options); - - // TODO ? $this->_silentCommit(); - foreach ($fields as $field_name => $field) { - if ( ! empty($field['autoincrement'])) { - //create PK constraint - $pk_definition = array( - 'fields' => array($field_name => array()), - 'primary' => true, - ); - //$pk_name = $name.'_PK'; - $pk_name = null; - $result = $this->createConstraint($name, $pk_name, $pk_definition); - - //create autoincrement sequence + trigger - return $this->_makeAutoincrement($field_name, $name, 1); - } - } - } - /** - * Check if planned changes are supported - * - * @param string $name name of the database that should be dropped - * @return void - */ - public function checkSupportedChanges(&$changes) - { - foreach ($changes as $change_name => $change) { - switch ($change_name) { - case 'notnull': - throw new Doctrine_DataDict_Exception('it is not supported changes to field not null constraint'); - case 'default': - throw new Doctrine_DataDict_Exception('it is not supported changes to field default value'); - case 'length': - /* - return throw new Doctrine_DataDict_Firebird_Exception('it is not supported changes to field default length'); - */ - case 'unsigned': - case 'type': - case 'declaration': - case 'definition': - break; - default: - throw new Doctrine_DataDict_Exception('it is not supported change of type' . $change_name); - } - } - return true; - } - /** - * drop an existing table - * - * @param string $name name of the table that should be dropped - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - public function dropTable($name) - { - $result = $this->_dropAutoincrement($name); - $result = parent::dropTable($name); - - //$this->_silentCommit(); - - return $result; - } - /** - * alter an existing table - * - * @param string $name name of the table that is intended to be changed. - * @param array $changes associative array that contains the details of each type - * of change that is intended to be performed. The types of - * changes that are currently supported are defined as follows: - * - * name - * - * New name for the table. - * - * add - * - * Associative array with the names of fields to be added as - * indexes of the array. The value of each entry of the array - * should be set to another associative array with the properties - * of the fields to be added. The properties of the fields should - * be the same as defined by the Metabase parser. - * - * - * remove - * - * Associative array with the names of fields to be removed as indexes - * of the array. Currently the values assigned to each entry are ignored. - * An empty array should be used for future compatibility. - * - * rename - * - * Associative array with the names of fields to be renamed as indexes - * of the array. The value of each entry of the array should be set to - * another associative array with the entry named name with the new - * field name and the entry named Declaration that is expected to contain - * the portion of the field declaration already in DBMS specific SQL code - * as it is used in the CREATE TABLE statement. - * - * change - * - * Associative array with the names of the fields to be changed as indexes - * of the array. Keep in mind that if it is intended to change either the - * name of a field and any other properties, the change array entries - * should have the new names of the fields as array indexes. - * - * The value of each entry of the array should be set to another associative - * array with the properties of the fields to that are meant to be changed as - * array entries. These entries should be assigned to the new values of the - * respective properties. The properties of the fields should be the same - * as defined by the Metabase parser. - * - * Example - * array( - * 'name' => 'userlist', - * 'add' => array( - * 'quota' => array( - * 'type' => 'integer', - * 'unsigned' => 1 - * ) - * ), - * 'remove' => array( - * 'file_limit' => array(), - * 'time_limit' => array() - * ), - * 'change' => array( - * 'name' => array( - * 'length' => '20', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 20, - * ), - * ) - * ), - * 'rename' => array( - * 'sex' => array( - * 'name' => 'gender', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 1, - * 'default' => 'M', - * ), - * ) - * ) - * ) - * - * @param boolean $check indicates whether the function should just check if the DBMS driver - * can perform the requested table alterations if the value is true or - * actually perform them otherwise. - * @return void - */ - public function alterTable($name, array $changes, $check = false) - { - foreach ($changes as $changeName => $change) { - switch ($changeName) { - case 'add': - case 'remove': - case 'rename': - break; - case 'change': - foreach ($changes['change'] as $field) { - $this->checkSupportedChanges($field); - } - break; - default: - throw new Doctrine_DataDict_Exception('change type ' . $changeName . ' not yet supported'); - } - } - if ($check) { - return true; - } - $query = ''; - if ( ! empty($changes['add']) && is_array($changes['add'])) { - foreach ($changes['add'] as $fieldName => $field) { - if ($query) { - $query.= ', '; - } - $query.= 'ADD ' . $this->getDeclaration($fieldName, $field); - } - } - - if ( ! empty($changes['remove']) && is_array($changes['remove'])) { - foreach ($changes['remove'] as $field_name => $field) { - if ($query) { - $query.= ', '; - } - $field_name = $this->conn->quoteIdentifier($field_name, true); - $query.= 'DROP ' . $field_name; - } - } - - if ( ! empty($changes['rename']) && is_array($changes['rename'])) { - foreach ($changes['rename'] as $field_name => $field) { - if ($query) { - $query.= ', '; - } - $field_name = $this->conn->quoteIdentifier($field_name, true); - $query.= 'ALTER ' . $field_name . ' TO ' . $this->conn->quoteIdentifier($field['name'], true); - } - } - - if ( ! empty($changes['change']) && is_array($changes['change'])) { - // missing support to change DEFAULT and NULLability - foreach ($changes['change'] as $fieldName => $field) { - $this->checkSupportedChanges($field); - if ($query) { - $query.= ', '; - } - $this->conn->loadModule('Datatype', null, true); - $field_name = $this->conn->quoteIdentifier($fieldName, true); - $query.= 'ALTER ' . $field_name.' TYPE ' . $this->getTypeDeclaration($field['definition']); - } - } - - if ( ! strlen($query)) { - return false; - } - - $name = $this->conn->quoteIdentifier($name, true); - $result = $this->conn->exec('ALTER TABLE ' . $name . ' ' . $query); - $this->_silentCommit(); - return $result; - } - /** - * Get the stucture of a field into an array - * - * @param string $table name of the table on which the index is to be created - * @param string $name name of the index to be created - * @param array $definition associative array that defines properties of the index to be created. - * Currently, only one property named FIELDS is supported. This property - * is also an associative with the names of the index fields as array - * indexes. Each entry of this array is set to another type of associative - * array that specifies properties of the index that are specific to - * each field. - * - * Currently, only the sorting property is supported. It should be used - * to define the sorting direction of the index. It may be set to either - * ascending or descending. - * - * Not all DBMS support index sorting direction configuration. The DBMS - * drivers of those that do not support it ignore this property. Use the - * function support() to determine whether the DBMS driver can manage indexes. - - * Example - * array( - * 'fields' => array( - * 'user_name' => array( - * 'sorting' => 'ascending' - * ), - * 'last_login' => array() - * ) - * ) - * @return void - */ - public function createIndexSql($table, $name, array $definition) - { - $query = 'CREATE'; - - $query_sort = ''; - foreach ($definition['fields'] as $field) { - if ( ! strcmp($query_sort, '') && isset($field['sorting'])) { - switch ($field['sorting']) { - case 'ascending': - $query_sort = ' ASC'; - break; - case 'descending': - $query_sort = ' DESC'; - break; - } - } - } - $table = $this->conn->quoteIdentifier($table, true); - $name = $this->conn->quoteIdentifier($this->conn->formatter->getIndexName($name), true); - $query .= $query_sort. ' INDEX ' . $name . ' ON ' . $table; - $fields = array(); - foreach (array_keys($definition['fields']) as $field) { - $fields[] = $this->conn->quoteIdentifier($field, true); - } - $query .= ' ('.implode(', ', $fields) . ')'; - - return $query; - } - /** - * create a constraint on a table - * - * @param string $table name of the table on which the constraint is to be created - * @param string $name name of the constraint to be created - * @param array $definition associative array that defines properties of the constraint to be created. - * Currently, only one property named FIELDS is supported. This property - * is also an associative with the names of the constraint fields as array - * constraints. Each entry of this array is set to another type of associative - * array that specifies properties of the constraint that are specific to - * each field. - * - * Example - * array( - * 'fields' => array( - * 'user_name' => array(), - * 'last_login' => array(), - * ) - * ) - * @return void - */ - public function createConstraint($table, $name, $definition) - { - $table = $this->conn->quoteIdentifier($table, true); - - if ( ! empty($name)) { - $name = $this->conn->quoteIdentifier($this->conn->formatter->getIndexName($name), true); - } - $query = "ALTER TABLE $table ADD"; - if ( ! empty($definition['primary'])) { - if ( ! empty($name)) { - $query.= ' CONSTRAINT '.$name; - } - $query.= ' PRIMARY KEY'; - } else { - $query.= ' CONSTRAINT '. $name; - if ( ! empty($definition['unique'])) { - $query.= ' UNIQUE'; - } - } - $fields = array(); - foreach (array_keys($definition['fields']) as $field) { - $fields[] = $this->conn->quoteIdentifier($field, true); - } - $query .= ' ('. implode(', ', $fields) . ')'; - $result = $this->conn->exec($query); - // TODO ? $this->_silentCommit(); - return $result; - } - /** - * A method to return the required SQL string that fits between CREATE ... TABLE - * to create the table as a temporary table. - * - * @return string The string required to be placed between "CREATE" and "TABLE" - * to generate a temporary table, if possible. - */ - public function getTemporaryTableQuery() - { - return 'GLOBAL TEMPORARY'; - } - /** - * create sequence - * - * @param string $seqName name of the sequence to be created - * @param string $start start value of the sequence; default is 1 - * @param array $options An associative array of table options: - * array( - * 'comment' => 'Foo', - * 'charset' => 'utf8', - * 'collate' => 'utf8_unicode_ci', - * ); - * @return boolean - */ - public function createSequence($seqName, $start = 1, array $options = array()) - { - $sequenceName = $this->conn->formatter->getSequenceName($seqName); - - $this->conn->exec('CREATE GENERATOR ' . $sequenceName); - - try { - $this->conn->exec('SET GENERATOR ' . $sequenceName . ' TO ' . ($start-1)); - - return true; - } catch (Doctrine_Connection_Exception $e) { - try { - $this->dropSequence($seqName); - } catch(Doctrine_Connection_Exception $e) { - throw new Doctrine_Export_Exception('Could not drop inconsistent sequence table'); - } - } - throw new Doctrine_Export_Exception('could not create sequence table'); - } - /** - * drop existing sequence - * - * @param string $seqName name of the sequence to be dropped - * @return void - */ - public function dropSequenceSql($seqName) - { - $sequenceName = $this->conn->formatter->getSequenceName($seqName); - $sequenceName = $this->conn->quote($sequenceName); - $query = "DELETE FROM RDB\$GENERATORS WHERE UPPER(RDB\$GENERATOR_NAME)=" . $sequenceName; - - return $query; - } +. + */ +Doctrine::autoload('Doctrine_Export'); +/** + * Doctrine_Export_Sqlite + * + * @package Doctrine + * @subpackage Export + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Lorenzo Alberton (PEAR MDB2 Interbase driver) + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.phpdoctrine.com + * @since 1.0 + * @version $Revision$ + */ +class Doctrine_Export_Firebird extends Doctrine_Export +{ + /** + * create a new database + * + * @param string $name name of the database that should be created + * @return void + */ + public function createDatabase($name) + { + throw new Doctrine_Export_Exception( + 'PHP Interbase API does not support direct queries. You have to ' . + 'create the db manually by using isql command or a similar program'); + } + /** + * drop an existing database + * + * @param string $name name of the database that should be dropped + * @return void + */ + public function dropDatabase($name) + { + throw new Doctrine_Export_Exception( + 'PHP Interbase API does not support direct queries. You have ' . + 'to drop the db manually by using isql command or a similar program'); + } + /** + * add an autoincrement sequence + trigger + * + * @param string $name name of the PK field + * @param string $table name of the table + * @param string $start start value for the sequence + * @return void + */ + public function _makeAutoincrement($name, $table, $start = null) + { + if (is_null($start)) { + $this->conn->beginTransaction(); + $query = 'SELECT MAX(' . $this->conn->quoteIdentifier($name, true) . ') FROM ' . $this->conn->quoteIdentifier($table, true); + $start = $this->conn->fetchOne($query, 'integer'); + + ++$start; + $result = $this->createSequence($table, $start); + $this->conn->commit(); + } else { + $result = $this->createSequence($table, $start); + } + + $sequence_name = $this->conn->formatter->getSequenceName($table); + $trigger_name = $this->conn->quoteIdentifier($table . '_AUTOINCREMENT_PK', true); + + $table = $this->conn->quoteIdentifier($table, true); + $name = $this->conn->quoteIdentifier($name, true); + + $triggerSql = 'CREATE TRIGGER ' . $trigger_name . ' FOR ' . $table . ' + ACTIVE BEFORE INSERT POSITION 0 + AS + BEGIN + IF (NEW.' . $name . ' IS NULL OR NEW.' . $name . ' = 0) THEN + NEW.' . $name . ' = GEN_ID('.$sequence_name.', 1); + END'; + $result = $this->conn->exec($triggerSql); + + // TODO ? $this->_silentCommit(); + + return $result; + } + /** + * drop an existing autoincrement sequence + trigger + * + * @param string $table name of the table + * @return void + */ + public function _dropAutoincrement($table) + { + + $result = $this->dropSequence($table); + + //remove autoincrement trigger associated with the table + $table = $this->conn->quote(strtoupper($table)); + $triggerName = $this->conn->quote(strtoupper($table) . '_AUTOINCREMENT_PK'); + + return $this->conn->exec("DELETE FROM RDB\$TRIGGERS WHERE UPPER(RDB\$RELATION_NAME)=" . $table . " AND UPPER(RDB\$TRIGGER_NAME)=" . $triggerName); + } + /** + * create a new table + * + * @param string $name Name of the database that should be created + * @param array $fields Associative array that contains the definition of each field of the new table + * The indexes of the array entries are the names of the fields of the table an + * the array entry values are associative arrays like those that are meant to be + * passed with the field definitions to get[Type]Declaration() functions. + * + * Example + * array( + * + * 'id' => array( + * 'type' => 'integer', + * 'unsigned' => 1, + * 'notnull' => 1, + * 'default' => 0, + * ), + * 'name' => array( + * 'type' => 'text', + * 'length' => 12, + * ), + * 'description' => array( + * 'type' => 'text', + * 'length' => 12, + * ) + * ); + * @param array $options An associative array of table options: + * + * @return void + */ + public function createTable($name, array $fields, array $options = array()) { + parent::createTable($name, $fields, $options); + + // TODO ? $this->_silentCommit(); + foreach ($fields as $field_name => $field) { + if ( ! empty($field['autoincrement'])) { + //create PK constraint + $pk_definition = array( + 'fields' => array($field_name => array()), + 'primary' => true, + ); + //$pk_name = $name.'_PK'; + $pk_name = null; + $result = $this->createConstraint($name, $pk_name, $pk_definition); + + //create autoincrement sequence + trigger + return $this->_makeAutoincrement($field_name, $name, 1); + } + } + } + /** + * Check if planned changes are supported + * + * @param string $name name of the database that should be dropped + * @return void + */ + public function checkSupportedChanges(&$changes) + { + foreach ($changes as $change_name => $change) { + switch ($change_name) { + case 'notnull': + throw new Doctrine_DataDict_Exception('it is not supported changes to field not null constraint'); + case 'default': + throw new Doctrine_DataDict_Exception('it is not supported changes to field default value'); + case 'length': + /* + return throw new Doctrine_DataDict_Firebird_Exception('it is not supported changes to field default length'); + */ + case 'unsigned': + case 'type': + case 'declaration': + case 'definition': + break; + default: + throw new Doctrine_DataDict_Exception('it is not supported change of type' . $change_name); + } + } + return true; + } + /** + * drop an existing table + * + * @param string $name name of the table that should be dropped + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + public function dropTable($name) + { + $result = $this->_dropAutoincrement($name); + $result = parent::dropTable($name); + + //$this->_silentCommit(); + + return $result; + } + /** + * alter an existing table + * + * @param string $name name of the table that is intended to be changed. + * @param array $changes associative array that contains the details of each type + * of change that is intended to be performed. The types of + * changes that are currently supported are defined as follows: + * + * name + * + * New name for the table. + * + * add + * + * Associative array with the names of fields to be added as + * indexes of the array. The value of each entry of the array + * should be set to another associative array with the properties + * of the fields to be added. The properties of the fields should + * be the same as defined by the Metabase parser. + * + * + * remove + * + * Associative array with the names of fields to be removed as indexes + * of the array. Currently the values assigned to each entry are ignored. + * An empty array should be used for future compatibility. + * + * rename + * + * Associative array with the names of fields to be renamed as indexes + * of the array. The value of each entry of the array should be set to + * another associative array with the entry named name with the new + * field name and the entry named Declaration that is expected to contain + * the portion of the field declaration already in DBMS specific SQL code + * as it is used in the CREATE TABLE statement. + * + * change + * + * Associative array with the names of the fields to be changed as indexes + * of the array. Keep in mind that if it is intended to change either the + * name of a field and any other properties, the change array entries + * should have the new names of the fields as array indexes. + * + * The value of each entry of the array should be set to another associative + * array with the properties of the fields to that are meant to be changed as + * array entries. These entries should be assigned to the new values of the + * respective properties. The properties of the fields should be the same + * as defined by the Metabase parser. + * + * Example + * array( + * 'name' => 'userlist', + * 'add' => array( + * 'quota' => array( + * 'type' => 'integer', + * 'unsigned' => 1 + * ) + * ), + * 'remove' => array( + * 'file_limit' => array(), + * 'time_limit' => array() + * ), + * 'change' => array( + * 'name' => array( + * 'length' => '20', + * 'definition' => array( + * 'type' => 'text', + * 'length' => 20, + * ), + * ) + * ), + * 'rename' => array( + * 'sex' => array( + * 'name' => 'gender', + * 'definition' => array( + * 'type' => 'text', + * 'length' => 1, + * 'default' => 'M', + * ), + * ) + * ) + * ) + * + * @param boolean $check indicates whether the function should just check if the DBMS driver + * can perform the requested table alterations if the value is true or + * actually perform them otherwise. + * @return void + */ + public function alterTable($name, array $changes, $check = false) + { + foreach ($changes as $changeName => $change) { + switch ($changeName) { + case 'add': + case 'remove': + case 'rename': + break; + case 'change': + foreach ($changes['change'] as $field) { + $this->checkSupportedChanges($field); + } + break; + default: + throw new Doctrine_DataDict_Exception('change type ' . $changeName . ' not yet supported'); + } + } + if ($check) { + return true; + } + $query = ''; + if ( ! empty($changes['add']) && is_array($changes['add'])) { + foreach ($changes['add'] as $fieldName => $field) { + if ($query) { + $query.= ', '; + } + $query.= 'ADD ' . $this->getDeclaration($fieldName, $field); + } + } + + if ( ! empty($changes['remove']) && is_array($changes['remove'])) { + foreach ($changes['remove'] as $field_name => $field) { + if ($query) { + $query.= ', '; + } + $field_name = $this->conn->quoteIdentifier($field_name, true); + $query.= 'DROP ' . $field_name; + } + } + + if ( ! empty($changes['rename']) && is_array($changes['rename'])) { + foreach ($changes['rename'] as $field_name => $field) { + if ($query) { + $query.= ', '; + } + $field_name = $this->conn->quoteIdentifier($field_name, true); + $query.= 'ALTER ' . $field_name . ' TO ' . $this->conn->quoteIdentifier($field['name'], true); + } + } + + if ( ! empty($changes['change']) && is_array($changes['change'])) { + // missing support to change DEFAULT and NULLability + foreach ($changes['change'] as $fieldName => $field) { + $this->checkSupportedChanges($field); + if ($query) { + $query.= ', '; + } + $this->conn->loadModule('Datatype', null, true); + $field_name = $this->conn->quoteIdentifier($fieldName, true); + $query.= 'ALTER ' . $field_name.' TYPE ' . $this->getTypeDeclaration($field['definition']); + } + } + + if ( ! strlen($query)) { + return false; + } + + $name = $this->conn->quoteIdentifier($name, true); + $result = $this->conn->exec('ALTER TABLE ' . $name . ' ' . $query); + $this->_silentCommit(); + return $result; + } + /** + * Get the stucture of a field into an array + * + * @param string $table name of the table on which the index is to be created + * @param string $name name of the index to be created + * @param array $definition associative array that defines properties of the index to be created. + * Currently, only one property named FIELDS is supported. This property + * is also an associative with the names of the index fields as array + * indexes. Each entry of this array is set to another type of associative + * array that specifies properties of the index that are specific to + * each field. + * + * Currently, only the sorting property is supported. It should be used + * to define the sorting direction of the index. It may be set to either + * ascending or descending. + * + * Not all DBMS support index sorting direction configuration. The DBMS + * drivers of those that do not support it ignore this property. Use the + * function support() to determine whether the DBMS driver can manage indexes. + + * Example + * array( + * 'fields' => array( + * 'user_name' => array( + * 'sorting' => 'ascending' + * ), + * 'last_login' => array() + * ) + * ) + * @return void + */ + public function createIndexSql($table, $name, array $definition) + { + $query = 'CREATE'; + + $query_sort = ''; + foreach ($definition['fields'] as $field) { + if ( ! strcmp($query_sort, '') && isset($field['sorting'])) { + switch ($field['sorting']) { + case 'ascending': + $query_sort = ' ASC'; + break; + case 'descending': + $query_sort = ' DESC'; + break; + } + } + } + $table = $this->conn->quoteIdentifier($table, true); + $name = $this->conn->quoteIdentifier($this->conn->formatter->getIndexName($name), true); + $query .= $query_sort. ' INDEX ' . $name . ' ON ' . $table; + $fields = array(); + foreach (array_keys($definition['fields']) as $field) { + $fields[] = $this->conn->quoteIdentifier($field, true); + } + $query .= ' ('.implode(', ', $fields) . ')'; + + return $query; + } + /** + * create a constraint on a table + * + * @param string $table name of the table on which the constraint is to be created + * @param string $name name of the constraint to be created + * @param array $definition associative array that defines properties of the constraint to be created. + * Currently, only one property named FIELDS is supported. This property + * is also an associative with the names of the constraint fields as array + * constraints. Each entry of this array is set to another type of associative + * array that specifies properties of the constraint that are specific to + * each field. + * + * Example + * array( + * 'fields' => array( + * 'user_name' => array(), + * 'last_login' => array(), + * ) + * ) + * @return void + */ + public function createConstraint($table, $name, $definition) + { + $table = $this->conn->quoteIdentifier($table, true); + + if ( ! empty($name)) { + $name = $this->conn->quoteIdentifier($this->conn->formatter->getIndexName($name), true); + } + $query = "ALTER TABLE $table ADD"; + if ( ! empty($definition['primary'])) { + if ( ! empty($name)) { + $query.= ' CONSTRAINT '.$name; + } + $query.= ' PRIMARY KEY'; + } else { + $query.= ' CONSTRAINT '. $name; + if ( ! empty($definition['unique'])) { + $query.= ' UNIQUE'; + } + } + $fields = array(); + foreach (array_keys($definition['fields']) as $field) { + $fields[] = $this->conn->quoteIdentifier($field, true); + } + $query .= ' ('. implode(', ', $fields) . ')'; + $result = $this->conn->exec($query); + // TODO ? $this->_silentCommit(); + return $result; + } + /** + * A method to return the required SQL string that fits between CREATE ... TABLE + * to create the table as a temporary table. + * + * @return string The string required to be placed between "CREATE" and "TABLE" + * to generate a temporary table, if possible. + */ + public function getTemporaryTableQuery() + { + return 'GLOBAL TEMPORARY'; + } + /** + * create sequence + * + * @param string $seqName name of the sequence to be created + * @param string $start start value of the sequence; default is 1 + * @param array $options An associative array of table options: + * array( + * 'comment' => 'Foo', + * 'charset' => 'utf8', + * 'collate' => 'utf8_unicode_ci', + * ); + * @return boolean + */ + public function createSequence($seqName, $start = 1, array $options = array()) + { + $sequenceName = $this->conn->formatter->getSequenceName($seqName); + + $this->conn->exec('CREATE GENERATOR ' . $sequenceName); + + try { + $this->conn->exec('SET GENERATOR ' . $sequenceName . ' TO ' . ($start-1)); + + return true; + } catch (Doctrine_Connection_Exception $e) { + try { + $this->dropSequence($seqName); + } catch(Doctrine_Connection_Exception $e) { + throw new Doctrine_Export_Exception('Could not drop inconsistent sequence table'); + } + } + throw new Doctrine_Export_Exception('could not create sequence table'); + } + /** + * drop existing sequence + * + * @param string $seqName name of the sequence to be dropped + * @return void + */ + public function dropSequenceSql($seqName) + { + $sequenceName = $this->conn->formatter->getSequenceName($seqName); + $sequenceName = $this->conn->quote($sequenceName); + $query = "DELETE FROM RDB\$GENERATORS WHERE UPPER(RDB\$GENERATOR_NAME)=" . $sequenceName; + + return $query; + } } \ No newline at end of file diff --git a/lib/Doctrine/Export/Frontbase.php b/lib/Doctrine/Export/Frontbase.php index 7e5f40704..8c040a26a 100644 --- a/lib/Doctrine/Export/Frontbase.php +++ b/lib/Doctrine/Export/Frontbase.php @@ -1,310 +1,310 @@ -. - */ -Doctrine::autoload('Doctrine_Export'); -/** - * Doctrine_Export_Frontbase - * - * @package Doctrine - * @subpackage Export - * @author Konsta Vesterinen - * @author Lukas Smith (PEAR MDB2 library) - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.phpdoctrine.com - * @since 1.0 - * @version $Revision$ - */ -class Doctrine_Export_Frontbase extends Doctrine_Export -{ - /** - * create a new database - * - * @param string $name name of the database that should be created - * @return string - */ - public function createDatabaseSql($name) - { - $name = $this->conn->quoteIdentifier($name, true); - return 'CREATE DATABASE ' . $name; - } - /** - * drop an existing database - * - * @param string $name name of the database that should be dropped - * @return string - */ - public function dropDatabaseSql($name) - { - $name = $this->conn->quoteIdentifier($name, true); - return 'DELETE DATABASE ' . $name; - } - /** - * drop an existing table - * - * @param object $this->conns database object that is extended by this class - * @param string $name name of the table that should be dropped - * @return string - */ - public function dropTableSql($name) - { - $name = $this->conn->quoteIdentifier($name, true); - return 'DROP TABLE ' . $name . ' CASCADE'; - } - /** - * alter an existing table - * - * @param string $name name of the table that is intended to be changed. - * @param array $changes associative array that contains the details of each type - * of change that is intended to be performed. The types of - * changes that are currently supported are defined as follows: - * - * name - * - * New name for the table. - * - * add - * - * Associative array with the names of fields to be added as - * indexes of the array. The value of each entry of the array - * should be set to another associative array with the properties - * of the fields to be added. The properties of the fields should - * be the same as defined by the MDB2 parser. - * - * - * remove - * - * Associative array with the names of fields to be removed as indexes - * of the array. Currently the values assigned to each entry are ignored. - * An empty array should be used for future compatibility. - * - * rename - * - * Associative array with the names of fields to be renamed as indexes - * of the array. The value of each entry of the array should be set to - * another associative array with the entry named name with the new - * field name and the entry named Declaration that is expected to contain - * the portion of the field declaration already in DBMS specific SQL code - * as it is used in the CREATE TABLE statement. - * - * change - * - * Associative array with the names of the fields to be changed as indexes - * of the array. Keep in mind that if it is intended to change either the - * name of a field and any other properties, the change array entries - * should have the new names of the fields as array indexes. - * - * The value of each entry of the array should be set to another associative - * array with the properties of the fields to that are meant to be changed as - * array entries. These entries should be assigned to the new values of the - * respective properties. The properties of the fields should be the same - * as defined by the MDB2 parser. - * - * Example - * array( - * 'name' => 'userlist', - * 'add' => array( - * 'quota' => array( - * 'type' => 'integer', - * 'unsigned' => 1 - * ) - * ), - * 'remove' => array( - * 'file_limit' => array(), - * 'time_limit' => array() - * ), - * 'change' => array( - * 'name' => array( - * 'length' => '20', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 20, - * ), - * ) - * ), - * 'rename' => array( - * 'sex' => array( - * 'name' => 'gender', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 1, - * 'default' => 'M', - * ), - * ) - * ) - * ) - * - * @param boolean $check indicates whether the function should just check if the DBMS driver - * can perform the requested table alterations if the value is true or - * actually perform them otherwise. - * @access public - * - * @return boolean - */ - public function alterTable($name, array $changes, $check = false) - { - foreach ($changes as $changeName => $change) { - switch ($changeName) { - case 'add': - case 'remove': - case 'change': - case 'rename': - case 'name': - break; - default: - throw new Doctrine_Export_Exception('change type "'.$changeName.'" not yet supported'); - } - } - - if ($check) { - return true; - } - - $query = ''; - if ( ! empty($changes['name'])) { - $changeName = $this->conn->quoteIdentifier($changes['name'], true); - $query .= 'RENAME TO ' . $changeName; - } - - if ( ! empty($changes['add']) && is_array($changes['add'])) { - foreach ($changes['add'] as $fieldName => $field) { - if ($query) { - $query.= ', '; - } - $query.= 'ADD ' . $this->conn->getDeclaration($fieldName, $field); - } - } - - if ( ! empty($changes['remove']) && is_array($changes['remove'])) { - foreach ($changes['remove'] as $fieldName => $field) { - if ($query) { - $query.= ', '; - } - $fieldName = $this->conn->quoteIdentifier($fieldName, true); - $query.= 'DROP ' . $fieldName; - } - } - - $rename = array(); - if ( ! empty($changes['rename']) && is_array($changes['rename'])) { - foreach ($changes['rename'] as $fieldName => $field) { - $rename[$field['name']] = $fieldName; - } - } - - if ( ! empty($changes['change']) && is_array($changes['change'])) { - foreach ($changes['change'] as $fieldName => $field) { - if ($query) { - $query.= ', '; - } - if (isset($rename[$fieldName])) { - $oldFieldName = $rename[$fieldName]; - unset($rename[$fieldName]); - } else { - $oldFieldName = $fieldName; - } - $oldFieldName = $this->conn->quoteIdentifier($oldFieldName, true); - $query.= 'CHANGE ' . $oldFieldName . ' ' . $this->conn->getDeclaration($oldFieldName, $field['definition']); - } - } - - if ( ! empty($rename) && is_array($rename)) { - foreach ($rename as $renamedFieldName => $renamed_field) { - if ($query) { - $query.= ', '; - } - $oldFieldName = $rename[$renamedFieldName]; - $field = $changes['rename'][$oldFieldName]; - $query.= 'CHANGE ' . $this->conn->getDeclaration($oldFieldName, $field['definition']); - } - } - - if ( ! $query) { - return true; - } - - $name = $this->conn->quoteIdentifier($name, true); - return $this->conn->exec('ALTER TABLE ' . $name . ' ' . $query); - } - /** - * create sequence - * - * @param string $seqName name of the sequence to be created - * @param string $start start value of the sequence; default is 1 - * @param array $options An associative array of table options: - * array( - * 'comment' => 'Foo', - * 'charset' => 'utf8', - * 'collate' => 'utf8_unicode_ci', - * ); - * @return void - */ - public function createSequence($sequenceName, $start = 1, array $options = array()) - { - $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($sequenceName), true); - $seqcolName = $this->conn->quoteIdentifier($this->conn->getAttribute(Doctrine::ATTR_SEQCOL_NAME), true); - - $query = 'CREATE TABLE ' . $sequenceName . ' (' . $seqcolName . ' INTEGER DEFAULT UNIQUE, PRIMARY KEY(' . $seqcolName . '))'; - $res = $this->conn->exec($query); - $res = $this->conn->exec('SET UNIQUE = 1 FOR ' . $sequenceName); - - if ($start == 1) { - return true; - } - - try { - $this->conn->exec('INSERT INTO ' . $sequenceName . ' (' . $seqcolName . ') VALUES (' . ($start-1) . ')'); - } catch(Doctrine_Connection_Exception $e) { - // Handle error - try { - $this->conn->exec('DROP TABLE ' . $sequenceName); - } catch(Doctrine_Connection_Exception $e) { - throw new Doctrine_Export_Exception('could not drop inconsistent sequence table'); - } - - throw new Doctrine_Export_Exception('could not create sequence table'); - } - } - /** - * drop existing sequence - * - * @param string $seqName name of the sequence to be dropped - * @return string - */ - public function dropSequenceSql($seqName) - { - $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($seqName), true); - - return 'DROP TABLE ' . $sequenceName . ' CASCADE'; - } - /** - * drop existing index - * - * @param string $table name of table that should be used in method - * @param string $name name of the index to be dropped - * @return boolean - */ - public function dropIndexSql($table, $name) - { - $table = $this->conn->quoteIdentifier($table, true); - $name = $this->conn->quoteIdentifier($this->conn->getIndexName($name), true); - - return 'ALTER TABLE ' . $table . ' DROP INDEX ' . $name; - } +. + */ +Doctrine::autoload('Doctrine_Export'); +/** + * Doctrine_Export_Frontbase + * + * @package Doctrine + * @subpackage Export + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.phpdoctrine.com + * @since 1.0 + * @version $Revision$ + */ +class Doctrine_Export_Frontbase extends Doctrine_Export +{ + /** + * create a new database + * + * @param string $name name of the database that should be created + * @return string + */ + public function createDatabaseSql($name) + { + $name = $this->conn->quoteIdentifier($name, true); + return 'CREATE DATABASE ' . $name; + } + /** + * drop an existing database + * + * @param string $name name of the database that should be dropped + * @return string + */ + public function dropDatabaseSql($name) + { + $name = $this->conn->quoteIdentifier($name, true); + return 'DELETE DATABASE ' . $name; + } + /** + * drop an existing table + * + * @param object $this->conns database object that is extended by this class + * @param string $name name of the table that should be dropped + * @return string + */ + public function dropTableSql($name) + { + $name = $this->conn->quoteIdentifier($name, true); + return 'DROP TABLE ' . $name . ' CASCADE'; + } + /** + * alter an existing table + * + * @param string $name name of the table that is intended to be changed. + * @param array $changes associative array that contains the details of each type + * of change that is intended to be performed. The types of + * changes that are currently supported are defined as follows: + * + * name + * + * New name for the table. + * + * add + * + * Associative array with the names of fields to be added as + * indexes of the array. The value of each entry of the array + * should be set to another associative array with the properties + * of the fields to be added. The properties of the fields should + * be the same as defined by the MDB2 parser. + * + * + * remove + * + * Associative array with the names of fields to be removed as indexes + * of the array. Currently the values assigned to each entry are ignored. + * An empty array should be used for future compatibility. + * + * rename + * + * Associative array with the names of fields to be renamed as indexes + * of the array. The value of each entry of the array should be set to + * another associative array with the entry named name with the new + * field name and the entry named Declaration that is expected to contain + * the portion of the field declaration already in DBMS specific SQL code + * as it is used in the CREATE TABLE statement. + * + * change + * + * Associative array with the names of the fields to be changed as indexes + * of the array. Keep in mind that if it is intended to change either the + * name of a field and any other properties, the change array entries + * should have the new names of the fields as array indexes. + * + * The value of each entry of the array should be set to another associative + * array with the properties of the fields to that are meant to be changed as + * array entries. These entries should be assigned to the new values of the + * respective properties. The properties of the fields should be the same + * as defined by the MDB2 parser. + * + * Example + * array( + * 'name' => 'userlist', + * 'add' => array( + * 'quota' => array( + * 'type' => 'integer', + * 'unsigned' => 1 + * ) + * ), + * 'remove' => array( + * 'file_limit' => array(), + * 'time_limit' => array() + * ), + * 'change' => array( + * 'name' => array( + * 'length' => '20', + * 'definition' => array( + * 'type' => 'text', + * 'length' => 20, + * ), + * ) + * ), + * 'rename' => array( + * 'sex' => array( + * 'name' => 'gender', + * 'definition' => array( + * 'type' => 'text', + * 'length' => 1, + * 'default' => 'M', + * ), + * ) + * ) + * ) + * + * @param boolean $check indicates whether the function should just check if the DBMS driver + * can perform the requested table alterations if the value is true or + * actually perform them otherwise. + * @access public + * + * @return boolean + */ + public function alterTable($name, array $changes, $check = false) + { + foreach ($changes as $changeName => $change) { + switch ($changeName) { + case 'add': + case 'remove': + case 'change': + case 'rename': + case 'name': + break; + default: + throw new Doctrine_Export_Exception('change type "'.$changeName.'" not yet supported'); + } + } + + if ($check) { + return true; + } + + $query = ''; + if ( ! empty($changes['name'])) { + $changeName = $this->conn->quoteIdentifier($changes['name'], true); + $query .= 'RENAME TO ' . $changeName; + } + + if ( ! empty($changes['add']) && is_array($changes['add'])) { + foreach ($changes['add'] as $fieldName => $field) { + if ($query) { + $query.= ', '; + } + $query.= 'ADD ' . $this->conn->getDeclaration($fieldName, $field); + } + } + + if ( ! empty($changes['remove']) && is_array($changes['remove'])) { + foreach ($changes['remove'] as $fieldName => $field) { + if ($query) { + $query.= ', '; + } + $fieldName = $this->conn->quoteIdentifier($fieldName, true); + $query.= 'DROP ' . $fieldName; + } + } + + $rename = array(); + if ( ! empty($changes['rename']) && is_array($changes['rename'])) { + foreach ($changes['rename'] as $fieldName => $field) { + $rename[$field['name']] = $fieldName; + } + } + + if ( ! empty($changes['change']) && is_array($changes['change'])) { + foreach ($changes['change'] as $fieldName => $field) { + if ($query) { + $query.= ', '; + } + if (isset($rename[$fieldName])) { + $oldFieldName = $rename[$fieldName]; + unset($rename[$fieldName]); + } else { + $oldFieldName = $fieldName; + } + $oldFieldName = $this->conn->quoteIdentifier($oldFieldName, true); + $query.= 'CHANGE ' . $oldFieldName . ' ' . $this->conn->getDeclaration($oldFieldName, $field['definition']); + } + } + + if ( ! empty($rename) && is_array($rename)) { + foreach ($rename as $renamedFieldName => $renamed_field) { + if ($query) { + $query.= ', '; + } + $oldFieldName = $rename[$renamedFieldName]; + $field = $changes['rename'][$oldFieldName]; + $query.= 'CHANGE ' . $this->conn->getDeclaration($oldFieldName, $field['definition']); + } + } + + if ( ! $query) { + return true; + } + + $name = $this->conn->quoteIdentifier($name, true); + return $this->conn->exec('ALTER TABLE ' . $name . ' ' . $query); + } + /** + * create sequence + * + * @param string $seqName name of the sequence to be created + * @param string $start start value of the sequence; default is 1 + * @param array $options An associative array of table options: + * array( + * 'comment' => 'Foo', + * 'charset' => 'utf8', + * 'collate' => 'utf8_unicode_ci', + * ); + * @return void + */ + public function createSequence($sequenceName, $start = 1, array $options = array()) + { + $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($sequenceName), true); + $seqcolName = $this->conn->quoteIdentifier($this->conn->getAttribute(Doctrine::ATTR_SEQCOL_NAME), true); + + $query = 'CREATE TABLE ' . $sequenceName . ' (' . $seqcolName . ' INTEGER DEFAULT UNIQUE, PRIMARY KEY(' . $seqcolName . '))'; + $res = $this->conn->exec($query); + $res = $this->conn->exec('SET UNIQUE = 1 FOR ' . $sequenceName); + + if ($start == 1) { + return true; + } + + try { + $this->conn->exec('INSERT INTO ' . $sequenceName . ' (' . $seqcolName . ') VALUES (' . ($start-1) . ')'); + } catch(Doctrine_Connection_Exception $e) { + // Handle error + try { + $this->conn->exec('DROP TABLE ' . $sequenceName); + } catch(Doctrine_Connection_Exception $e) { + throw new Doctrine_Export_Exception('could not drop inconsistent sequence table'); + } + + throw new Doctrine_Export_Exception('could not create sequence table'); + } + } + /** + * drop existing sequence + * + * @param string $seqName name of the sequence to be dropped + * @return string + */ + public function dropSequenceSql($seqName) + { + $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($seqName), true); + + return 'DROP TABLE ' . $sequenceName . ' CASCADE'; + } + /** + * drop existing index + * + * @param string $table name of table that should be used in method + * @param string $name name of the index to be dropped + * @return boolean + */ + public function dropIndexSql($table, $name) + { + $table = $this->conn->quoteIdentifier($table, true); + $name = $this->conn->quoteIdentifier($this->conn->getIndexName($name), true); + + return 'ALTER TABLE ' . $table . ' DROP INDEX ' . $name; + } } \ No newline at end of file diff --git a/lib/Doctrine/Export/Mssql.php b/lib/Doctrine/Export/Mssql.php index 548b78a71..f9afdaa5b 100644 --- a/lib/Doctrine/Export/Mssql.php +++ b/lib/Doctrine/Export/Mssql.php @@ -1,253 +1,253 @@ -. - */ -Doctrine::autoload('Doctrine_Export'); -/** - * Doctrine_Export_Mssql - * - * @package Doctrine - * @subpackage Export - * @author Konsta Vesterinen - * @author Lukas Smith (PEAR MDB2 library) - * @author Frank M. Kromann (PEAR MDB2 Mssql driver) - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.phpdoctrine.com - * @since 1.0 - * @version $Revision$ - */ -class Doctrine_Export_Mssql extends Doctrine_Export -{ - /** - * create a new database - * - * @param string $name name of the database that should be created - * @return void - */ - public function createDatabase($name) - { - $name = $this->conn->quoteIdentifier($name, true); - $query = "CREATE DATABASE $name"; - if ($this->conn->options['database_device']) { - $query.= ' ON '.$this->conn->options['database_device']; - $query.= $this->conn->options['database_size'] ? '=' . - $this->conn->options['database_size'] : ''; - } - return $this->conn->standaloneQuery($query, null, true); - } - /** - * drop an existing database - * - * @param string $name name of the database that should be dropped - * @return void - */ - public function dropDatabase($name) - { - $name = $this->conn->quoteIdentifier($name, true); - return $this->conn->standaloneQuery('DROP DATABASE ' . $name, null, true); - } - - /** - * Override the parent method. - * - * @return string The string required to be placed between "CREATE" and "TABLE" - * to generate a temporary table, if possible. - */ - public function getTemporaryTableQuery() - { - return ''; - } - /** - * alter an existing table - * - * @param string $name name of the table that is intended to be changed. - * @param array $changes associative array that contains the details of each type - * of change that is intended to be performed. The types of - * changes that are currently supported are defined as follows: - * - * name - * - * New name for the table. - * - * add - * - * Associative array with the names of fields to be added as - * indexes of the array. The value of each entry of the array - * should be set to another associative array with the properties - * of the fields to be added. The properties of the fields should - * be the same as defined by the Metabase parser. - * - * - * remove - * - * Associative array with the names of fields to be removed as indexes - * of the array. Currently the values assigned to each entry are ignored. - * An empty array should be used for future compatibility. - * - * rename - * - * Associative array with the names of fields to be renamed as indexes - * of the array. The value of each entry of the array should be set to - * another associative array with the entry named name with the new - * field name and the entry named Declaration that is expected to contain - * the portion of the field declaration already in DBMS specific SQL code - * as it is used in the CREATE TABLE statement. - * - * change - * - * Associative array with the names of the fields to be changed as indexes - * of the array. Keep in mind that if it is intended to change either the - * name of a field and any other properties, the change array entries - * should have the new names of the fields as array indexes. - * - * The value of each entry of the array should be set to another associative - * array with the properties of the fields to that are meant to be changed as - * array entries. These entries should be assigned to the new values of the - * respective properties. The properties of the fields should be the same - * as defined by the Metabase parser. - * - * Example - * array( - * 'name' => 'userlist', - * 'add' => array( - * 'quota' => array( - * 'type' => 'integer', - * 'unsigned' => 1 - * ) - * ), - * 'remove' => array( - * 'file_limit' => array(), - * 'time_limit' => array() - * ), - * 'change' => array( - * 'name' => array( - * 'length' => '20', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 20, - * ), - * ) - * ), - * 'rename' => array( - * 'sex' => array( - * 'name' => 'gender', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 1, - * 'default' => 'M', - * ), - * ) - * ) - * ) - * - * @param boolean $check indicates whether the function should just check if the DBMS driver - * can perform the requested table alterations if the value is true or - * actually perform them otherwise. - * @return void - */ - public function alterTable($name, array $changes, $check = false) - { - foreach ($changes as $changeName => $change) { - switch ($changeName) { - case 'add': - break; - case 'remove': - break; - case 'name': - case 'rename': - case 'change': - default: - throw new Doctrine_Export_Exception('alterTable: change type "' . $changeName . '" not yet supported'); - } - } - - $query = ''; - if ( ! empty($changes['add']) && is_array($changes['add'])) { - foreach ($changes['add'] as $fieldName => $field) { - if ($query) { - $query .= ', '; - } - $query .= 'ADD ' . $this->conn->getDeclaration($fieldName, $field); - } - } - - if ( ! empty($changes['remove']) && is_array($changes['remove'])) { - foreach ($changes['remove'] as $fieldName => $field) { - if ($query) { - $query .= ', '; - } - $field_name = $this->conn->quoteIdentifier($fieldName, true); - $query .= 'DROP COLUMN ' . $fieldName; - } - } - - if ( ! $query) { - return false; - } - - $name = $this->conn->quoteIdentifier($name, true); - return $this->conn->exec('ALTER TABLE ' . $name . ' ' . $query); - } - /** - * create sequence - * - * @param string $seqName name of the sequence to be created - * @param string $start start value of the sequence; default is 1 - * @param array $options An associative array of table options: - * array( - * 'comment' => 'Foo', - * 'charset' => 'utf8', - * 'collate' => 'utf8_unicode_ci', - * ); - * @return string - */ - public function createSequence($seqName, $start = 1, array $options = array()) - { - $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($seqName), true); - $seqcolName = $this->conn->quoteIdentifier($this->conn->options['seqcol_name'], true); - $query = 'CREATE TABLE ' . $sequenceName . ' (' . $seqcolName . - ' INT PRIMARY KEY CLUSTERED IDENTITY(' . $start . ', 1) NOT NULL)'; - - $res = $this->conn->exec($query); - - if ($start == 1) { - return true; - } - - try { - $query = 'SET IDENTITY_INSERT ' . $sequenceName . ' ON ' . - 'INSERT INTO ' . $sequenceName . ' (' . $seqcolName . ') VALUES ( ' . $start . ')'; - $res = $this->conn->exec($query); - } catch (Exception $e) { - $result = $this->conn->exec('DROP TABLE ' . $sequenceName); - } - return true; - } - /** - * This function drops an existing sequence - * - * @param string $seqName name of the sequence to be dropped - * @return void - */ - public function dropSequenceSql($seqName) - { - $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($seqName), true); - return 'DROP TABLE ' . $sequenceName; - } +. + */ +Doctrine::autoload('Doctrine_Export'); +/** + * Doctrine_Export_Mssql + * + * @package Doctrine + * @subpackage Export + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Frank M. Kromann (PEAR MDB2 Mssql driver) + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.phpdoctrine.com + * @since 1.0 + * @version $Revision$ + */ +class Doctrine_Export_Mssql extends Doctrine_Export +{ + /** + * create a new database + * + * @param string $name name of the database that should be created + * @return void + */ + public function createDatabase($name) + { + $name = $this->conn->quoteIdentifier($name, true); + $query = "CREATE DATABASE $name"; + if ($this->conn->options['database_device']) { + $query.= ' ON '.$this->conn->options['database_device']; + $query.= $this->conn->options['database_size'] ? '=' . + $this->conn->options['database_size'] : ''; + } + return $this->conn->standaloneQuery($query, null, true); + } + /** + * drop an existing database + * + * @param string $name name of the database that should be dropped + * @return void + */ + public function dropDatabase($name) + { + $name = $this->conn->quoteIdentifier($name, true); + return $this->conn->standaloneQuery('DROP DATABASE ' . $name, null, true); + } + + /** + * Override the parent method. + * + * @return string The string required to be placed between "CREATE" and "TABLE" + * to generate a temporary table, if possible. + */ + public function getTemporaryTableQuery() + { + return ''; + } + /** + * alter an existing table + * + * @param string $name name of the table that is intended to be changed. + * @param array $changes associative array that contains the details of each type + * of change that is intended to be performed. The types of + * changes that are currently supported are defined as follows: + * + * name + * + * New name for the table. + * + * add + * + * Associative array with the names of fields to be added as + * indexes of the array. The value of each entry of the array + * should be set to another associative array with the properties + * of the fields to be added. The properties of the fields should + * be the same as defined by the Metabase parser. + * + * + * remove + * + * Associative array with the names of fields to be removed as indexes + * of the array. Currently the values assigned to each entry are ignored. + * An empty array should be used for future compatibility. + * + * rename + * + * Associative array with the names of fields to be renamed as indexes + * of the array. The value of each entry of the array should be set to + * another associative array with the entry named name with the new + * field name and the entry named Declaration that is expected to contain + * the portion of the field declaration already in DBMS specific SQL code + * as it is used in the CREATE TABLE statement. + * + * change + * + * Associative array with the names of the fields to be changed as indexes + * of the array. Keep in mind that if it is intended to change either the + * name of a field and any other properties, the change array entries + * should have the new names of the fields as array indexes. + * + * The value of each entry of the array should be set to another associative + * array with the properties of the fields to that are meant to be changed as + * array entries. These entries should be assigned to the new values of the + * respective properties. The properties of the fields should be the same + * as defined by the Metabase parser. + * + * Example + * array( + * 'name' => 'userlist', + * 'add' => array( + * 'quota' => array( + * 'type' => 'integer', + * 'unsigned' => 1 + * ) + * ), + * 'remove' => array( + * 'file_limit' => array(), + * 'time_limit' => array() + * ), + * 'change' => array( + * 'name' => array( + * 'length' => '20', + * 'definition' => array( + * 'type' => 'text', + * 'length' => 20, + * ), + * ) + * ), + * 'rename' => array( + * 'sex' => array( + * 'name' => 'gender', + * 'definition' => array( + * 'type' => 'text', + * 'length' => 1, + * 'default' => 'M', + * ), + * ) + * ) + * ) + * + * @param boolean $check indicates whether the function should just check if the DBMS driver + * can perform the requested table alterations if the value is true or + * actually perform them otherwise. + * @return void + */ + public function alterTable($name, array $changes, $check = false) + { + foreach ($changes as $changeName => $change) { + switch ($changeName) { + case 'add': + break; + case 'remove': + break; + case 'name': + case 'rename': + case 'change': + default: + throw new Doctrine_Export_Exception('alterTable: change type "' . $changeName . '" not yet supported'); + } + } + + $query = ''; + if ( ! empty($changes['add']) && is_array($changes['add'])) { + foreach ($changes['add'] as $fieldName => $field) { + if ($query) { + $query .= ', '; + } + $query .= 'ADD ' . $this->conn->getDeclaration($fieldName, $field); + } + } + + if ( ! empty($changes['remove']) && is_array($changes['remove'])) { + foreach ($changes['remove'] as $fieldName => $field) { + if ($query) { + $query .= ', '; + } + $field_name = $this->conn->quoteIdentifier($fieldName, true); + $query .= 'DROP COLUMN ' . $fieldName; + } + } + + if ( ! $query) { + return false; + } + + $name = $this->conn->quoteIdentifier($name, true); + return $this->conn->exec('ALTER TABLE ' . $name . ' ' . $query); + } + /** + * create sequence + * + * @param string $seqName name of the sequence to be created + * @param string $start start value of the sequence; default is 1 + * @param array $options An associative array of table options: + * array( + * 'comment' => 'Foo', + * 'charset' => 'utf8', + * 'collate' => 'utf8_unicode_ci', + * ); + * @return string + */ + public function createSequence($seqName, $start = 1, array $options = array()) + { + $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($seqName), true); + $seqcolName = $this->conn->quoteIdentifier($this->conn->options['seqcol_name'], true); + $query = 'CREATE TABLE ' . $sequenceName . ' (' . $seqcolName . + ' INT PRIMARY KEY CLUSTERED IDENTITY(' . $start . ', 1) NOT NULL)'; + + $res = $this->conn->exec($query); + + if ($start == 1) { + return true; + } + + try { + $query = 'SET IDENTITY_INSERT ' . $sequenceName . ' ON ' . + 'INSERT INTO ' . $sequenceName . ' (' . $seqcolName . ') VALUES ( ' . $start . ')'; + $res = $this->conn->exec($query); + } catch (Exception $e) { + $result = $this->conn->exec('DROP TABLE ' . $sequenceName); + } + return true; + } + /** + * This function drops an existing sequence + * + * @param string $seqName name of the sequence to be dropped + * @return void + */ + public function dropSequenceSql($seqName) + { + $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($seqName), true); + return 'DROP TABLE ' . $sequenceName; + } } \ No newline at end of file diff --git a/lib/Doctrine/Export/Mysql.php b/lib/Doctrine/Export/Mysql.php index 0089deb7c..b3e47906b 100644 --- a/lib/Doctrine/Export/Mysql.php +++ b/lib/Doctrine/Export/Mysql.php @@ -1,639 +1,639 @@ -. - */ -Doctrine::autoload('Doctrine_Export'); -/** - * Doctrine_Export_Mysql - * - * @package Doctrine - * @subpackage Export - * @author Konsta Vesterinen - * @author Lukas Smith (PEAR MDB2 library) - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.phpdoctrine.com - * @since 1.0 - * @version $Revision$ - */ -class Doctrine_Export_Mysql extends Doctrine_Export -{ - /** - * create a new database - * - * @param string $name name of the database that should be created - * @return string - */ - public function createDatabaseSql($name) - { - return 'CREATE DATABASE ' . $this->conn->quoteIdentifier($name, true); - } - /** - * drop an existing database - * - * @param string $name name of the database that should be dropped - * @return string - */ - public function dropDatabaseSql($name) - { - return 'DROP DATABASE ' . $this->conn->quoteIdentifier($name); - } - /** - * create a new table - * - * @param string $name Name of the database that should be created - * @param array $fields Associative array that contains the definition of each field of the new table - * The indexes of the array entries are the names of the fields of the table an - * the array entry values are associative arrays like those that are meant to be - * passed with the field definitions to get[Type]Declaration() functions. - * array( - * 'id' => array( - * 'type' => 'integer', - * 'unsigned' => 1 - * 'notnull' => 1 - * 'default' => 0 - * ), - * 'name' => array( - * 'type' => 'text', - * 'length' => 12 - * ), - * 'password' => array( - * 'type' => 'text', - * 'length' => 12 - * ) - * ); - * @param array $options An associative array of table options: - * array( - * 'comment' => 'Foo', - * 'charset' => 'utf8', - * 'collate' => 'utf8_unicode_ci', - * 'type' => 'innodb', - * ); - * - * @return void - */ - public function createTableSql($name, array $fields, array $options = array()) - { - if ( ! $name) - throw new Doctrine_Export_Exception('no valid table name specified'); - - if (empty($fields)) { - throw new Doctrine_Export_Exception('no fields specified for table "'.$name.'"'); - } - $queryFields = $this->getFieldDeclarationList($fields); - - // build indexes for all foreign key fields (needed in MySQL!!) - if (isset($options['foreignKeys'])) { - foreach ($options['foreignKeys'] as $fk) { - $local = $fk['local']; - $found = false; - if (isset($options['indexes'])) { - foreach ($options['indexes'] as $definition) { - if (is_string($definition['fields'])) { - // Check if index already exists on the column - $found = ($local == $definition['fields']); - } else if (in_array($local, $definition['fields']) && count($definition['fields']) === 1) { - // Index already exists on the column - $found = true; - } - } - } - if (isset($options['primary']) && !empty($options['primary']) && - in_array($local, $options['primary'])) { - // field is part of the PK and therefore already indexed - $found = true; - } - - if ( ! $found) { - $options['indexes'][$local] = array('fields' => array($local => array())); - } - } - } - - // add all indexes - if (isset($options['indexes']) && ! empty($options['indexes'])) { - foreach($options['indexes'] as $index => $definition) { - $queryFields .= ', ' . $this->getIndexDeclaration($index, $definition); - } - } - - // attach all primary keys - if (isset($options['primary']) && ! empty($options['primary'])) { - $keyColumns = array_values($options['primary']); - $keyColumns = array_map(array($this->conn, 'quoteIdentifier'), $keyColumns); - $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; - } - - $query = 'CREATE TABLE ' . $this->conn->quoteIdentifier($name, true) . ' (' . $queryFields . ')'; - - $optionStrings = array(); - - if (isset($options['comment'])) { - $optionStrings['comment'] = 'COMMENT = ' . $this->dbh->quote($options['comment'], 'text'); - } - if (isset($options['charset'])) { - $optionStrings['charset'] = 'DEFAULT CHARACTER SET ' . $options['charset']; - if (isset($options['collate'])) { - $optionStrings['charset'] .= ' COLLATE ' . $options['collate']; - } - } - - $type = false; - - // get the type of the table - if (isset($options['type'])) { - $type = $options['type']; - } else { - $type = $this->conn->getAttribute(Doctrine::ATTR_DEFAULT_TABLE_TYPE); - } - - if ($type) { - $optionStrings[] = 'ENGINE = ' . $type; - } - - if ( ! empty($optionStrings)) { - $query.= ' '.implode(' ', $optionStrings); - } - $sql[] = $query; - - if (isset($options['foreignKeys'])) { - - foreach ((array) $options['foreignKeys'] as $k => $definition) { - if (is_array($definition)) { - $sql[] = $this->createForeignKeySql($name, $definition); - } - } - } - return $sql; - } - /** - * alter an existing table - * - * @param string $name name of the table that is intended to be changed. - * @param array $changes associative array that contains the details of each type - * of change that is intended to be performed. The types of - * changes that are currently supported are defined as follows: - * - * name - * - * New name for the table. - * - * add - * - * Associative array with the names of fields to be added as - * indexes of the array. The value of each entry of the array - * should be set to another associative array with the properties - * of the fields to be added. The properties of the fields should - * be the same as defined by the Metabase parser. - * - * - * remove - * - * Associative array with the names of fields to be removed as indexes - * of the array. Currently the values assigned to each entry are ignored. - * An empty array should be used for future compatibility. - * - * rename - * - * Associative array with the names of fields to be renamed as indexes - * of the array. The value of each entry of the array should be set to - * another associative array with the entry named name with the new - * field name and the entry named Declaration that is expected to contain - * the portion of the field declaration already in DBMS specific SQL code - * as it is used in the CREATE TABLE statement. - * - * change - * - * Associative array with the names of the fields to be changed as indexes - * of the array. Keep in mind that if it is intended to change either the - * name of a field and any other properties, the change array entries - * should have the new names of the fields as array indexes. - * - * The value of each entry of the array should be set to another associative - * array with the properties of the fields to that are meant to be changed as - * array entries. These entries should be assigned to the new values of the - * respective properties. The properties of the fields should be the same - * as defined by the Metabase parser. - * - * Example - * array( - * 'name' => 'userlist', - * 'add' => array( - * 'quota' => array( - * 'type' => 'integer', - * 'unsigned' => 1 - * ) - * ), - * 'remove' => array( - * 'file_limit' => array(), - * 'time_limit' => array() - * ), - * 'change' => array( - * 'name' => array( - * 'length' => '20', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 20, - * ), - * ) - * ), - * 'rename' => array( - * 'sex' => array( - * 'name' => 'gender', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 1, - * 'default' => 'M', - * ), - * ) - * ) - * ) - * - * @param boolean $check indicates whether the function should just check if the DBMS driver - * can perform the requested table alterations if the value is true or - * actually perform them otherwise. - * @return boolean - */ - public function alterTableSql($name, array $changes, $check = false) - { - if ( ! $name) { - throw new Doctrine_Export_Exception('no valid table name specified'); - } - foreach ($changes as $changeName => $change) { - switch ($changeName) { - case 'add': - case 'remove': - case 'change': - case 'rename': - case 'name': - break; - default: - throw new Doctrine_Export_Exception('change type "' . $changeName . '" not yet supported'); - } - } - - if ($check) { - return true; - } - - $query = ''; - if ( ! empty($changes['name'])) { - $change_name = $this->conn->quoteIdentifier($changes['name']); - $query .= 'RENAME TO ' . $change_name; - } - - if ( ! empty($changes['add']) && is_array($changes['add'])) { - foreach ($changes['add'] as $fieldName => $field) { - if ($query) { - $query.= ', '; - } - $query.= 'ADD ' . $this->getDeclaration($fieldName, $field); - } - } - - if ( ! empty($changes['remove']) && is_array($changes['remove'])) { - foreach ($changes['remove'] as $fieldName => $field) { - if ($query) { - $query .= ', '; - } - $fieldName = $this->conn->quoteIdentifier($fieldName); - $query .= 'DROP ' . $fieldName; - } - } - - $rename = array(); - if ( ! empty($changes['rename']) && is_array($changes['rename'])) { - foreach ($changes['rename'] as $fieldName => $field) { - $rename[$field['name']] = $fieldName; - } - } - - if ( ! empty($changes['change']) && is_array($changes['change'])) { - foreach ($changes['change'] as $fieldName => $field) { - if ($query) { - $query.= ', '; - } - if (isset($rename[$fieldName])) { - $oldFieldName = $rename[$fieldName]; - unset($rename[$fieldName]); - } else { - $oldFieldName = $fieldName; - } - $oldFieldName = $this->conn->quoteIdentifier($oldFieldName, true); - $query .= 'CHANGE ' . $oldFieldName . ' ' - . $this->getDeclaration($fieldName, $field['definition']); - } - } - - if ( ! empty($rename) && is_array($rename)) { - foreach ($rename as $renameName => $renamedField) { - if ($query) { - $query.= ', '; - } - $field = $changes['rename'][$renamedField]; - $renamedField = $this->conn->quoteIdentifier($renamedField, true); - $query .= 'CHANGE ' . $renamedField . ' ' - . $this->getDeclaration($field['name'], $field['definition']); - } - } - - if ( ! $query) { - return false; - } - - $name = $this->conn->quoteIdentifier($name, true); - - return 'ALTER TABLE ' . $name . ' ' . $query; - } - /** - * create sequence - * - * @param string $sequenceName name of the sequence to be created - * @param string $start start value of the sequence; default is 1 - * @param array $options An associative array of table options: - * array( - * 'comment' => 'Foo', - * 'charset' => 'utf8', - * 'collate' => 'utf8_unicode_ci', - * 'type' => 'innodb', - * ); - * @return boolean - */ - public function createSequence($sequenceName, $start = 1, array $options = array()) - { - $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($sequenceName), true); - $seqcolName = $this->conn->quoteIdentifier($this->conn->getAttribute(Doctrine::ATTR_SEQCOL_NAME), true); - - $optionsStrings = array(); - - if (isset($options['comment']) && ! empty($options['comment'])) { - $optionsStrings['comment'] = 'COMMENT = ' . $this->conn->quote($options['comment'], 'string'); - } - - if (isset($options['charset']) && ! empty($options['charset'])) { - $optionsStrings['charset'] = 'DEFAULT CHARACTER SET ' . $options['charset']; - - if (isset($options['collate'])) { - $optionsStrings['collate'] .= ' COLLATE ' . $options['collate']; - } - } - - $type = false; - - if (isset($options['type'])) { - $type = $options['type']; - } else { - $type = $this->conn->default_table_type; - } - if ($type) { - $optionsStrings[] = 'ENGINE = ' . $type; - } - - - try { - $query = 'CREATE TABLE ' . $sequenceName - . ' (' . $seqcolName . ' INT NOT NULL AUTO_INCREMENT, PRIMARY KEY (' - . $seqcolName . '))' - . strlen($this->conn->default_table_type) ? ' TYPE = ' - . $this->conn->default_table_type : ''; - - $res = $this->conn->exec($query); - } catch(Doctrine_Connection_Exception $e) { - throw new Doctrine_Export_Exception('could not create sequence table'); - } - - if ($start == 1) - return true; - - $query = 'INSERT INTO ' . $sequenceName - . ' (' . $seqcolName . ') VALUES (' . ($start - 1) . ')'; - - $res = $this->conn->exec($query); - - // Handle error - try { - $result = $this->conn->exec('DROP TABLE ' . $sequenceName); - } catch(Doctrine_Connection_Exception $e) { - throw new Doctrine_Export_Exception('could not drop inconsistent sequence table'); - } - - - } - /** - * Get the stucture of a field into an array - * - * @author Leoncx - * @param string $table name of the table on which the index is to be created - * @param string $name name of the index to be created - * @param array $definition associative array that defines properties of the index to be created. - * Currently, only one property named FIELDS is supported. This property - * is also an associative with the names of the index fields as array - * indexes. Each entry of this array is set to another type of associative - * array that specifies properties of the index that are specific to - * each field. - * - * Currently, only the sorting property is supported. It should be used - * to define the sorting direction of the index. It may be set to either - * ascending or descending. - * - * Not all DBMS support index sorting direction configuration. The DBMS - * drivers of those that do not support it ignore this property. Use the - * function supports() to determine whether the DBMS driver can manage indexes. - * - * Example - * array( - * 'fields' => array( - * 'user_name' => array( - * 'sorting' => 'ASC' - * 'length' => 10 - * ), - * 'last_login' => array() - * ) - * ) - * @throws PDOException - * @return void - */ - public function createIndexSql($table, $name, array $definition) - { - $table = $table; - $name = $this->conn->getIndexName($name); - $name = $this->conn->quoteIdentifier($name); - $type = ''; - if (isset($definition['type'])) { - switch (strtolower($definition['type'])) { - case 'fulltext': - case 'unique': - $type = strtoupper($definition['type']) . ' '; - break; - default: - throw new Doctrine_Export_Exception('Unknown index type ' . $definition['type']); - } - } - $query = 'CREATE ' . $type . 'INDEX ' . $name . ' ON ' . $table; - $query .= ' (' . $this->getIndexFieldDeclarationList() . ')'; - - return $query; - } - /** - * getDefaultDeclaration - * Obtain DBMS specific SQL code portion needed to set a default value - * declaration to be used in statements like CREATE TABLE. - * - * @param array $field field definition array - * @return string DBMS specific SQL code portion needed to set a default value - */ - public function getDefaultFieldDeclaration($field) - { - $default = ''; - if (isset($field['default']) && $field['length'] <= 255) { - if ($field['default'] === '') { - $field['default'] = empty($field['notnull']) - ? null : $this->valid_default_values[$field['type']]; - - if ($field['default'] === '' - && ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_EMPTY_TO_NULL) - ) { - $field['default'] = ' '; - } - } - - $default = ' DEFAULT ' . $this->conn->quote($field['default'], $field['type']); - } - return $default; - } - /** - * Obtain DBMS specific SQL code portion needed to set an index - * declaration to be used in statements like CREATE TABLE. - * - * @param string $charset name of the index - * @param array $definition index definition - * @return string DBMS specific SQL code portion needed to set an index - */ - public function getIndexDeclaration($name, array $definition) - { - $name = $this->conn->formatter->getIndexName($name); - $type = ''; - if (isset($definition['type'])) { - switch (strtolower($definition['type'])) { - case 'fulltext': - case 'unique': - $type = strtoupper($definition['type']) . ' '; - break; - default: - throw new Doctrine_Export_Exception('Unknown index type ' . $definition['type']); - } - } - - if ( ! isset($definition['fields'])) { - throw new Doctrine_Export_Exception('No index columns given.'); - } - if ( ! is_array($definition['fields'])) { - $definition['fields'] = array($definition['fields']); - } - - $query = $type . 'INDEX ' . $this->conn->quoteIdentifier($name); - - $query .= ' (' . $this->getIndexFieldDeclarationList($definition['fields']) . ')'; - - return $query; - } - /** - * getIndexFieldDeclarationList - * Obtain DBMS specific SQL code portion needed to set an index - * declaration to be used in statements like CREATE TABLE. - * - * @return string - */ - public function getIndexFieldDeclarationList(array $fields) - { - $declFields = array(); - - foreach ($fields as $fieldName => $field) { - $fieldString = $this->conn->quoteIdentifier($fieldName); - - if (is_array($field)) { - if (isset($field['length'])) { - $fieldString .= '(' . $field['length'] . ')'; - } - - if (isset($field['sorting'])) { - $sort = strtoupper($field['sorting']); - switch ($sort) { - case 'ASC': - case 'DESC': - $fieldString .= ' ' . $sort; - break; - default: - throw new Doctrine_Export_Exception('Unknown index sorting option given.'); - } - } - } else { - $fieldString = $this->conn->quoteIdentifier($field); - } - $declFields[] = $fieldString; - } - return implode(', ', $declFields); - } - /** - * getAdvancedForeignKeyOptions - * Return the FOREIGN KEY query section dealing with non-standard options - * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... - * - * @param array $definition - * @return string - */ - public function getAdvancedForeignKeyOptions(array $definition) - { - $query = ''; - if ( ! empty($definition['match'])) { - $query .= ' MATCH ' . $definition['match']; - } - if ( ! empty($definition['onUpdate'])) { - $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialAction($definition['onUpdate']); - } - if ( ! empty($definition['onDelete'])) { - $query .= ' ON DELETE ' . $this->getForeignKeyReferentialAction($definition['onDelete']); - } - return $query; - } - /** - * drop existing index - * - * @param string $table name of table that should be used in method - * @param string $name name of the index to be dropped - * @return void - */ - public function dropIndexSql($table, $name) - { - $table = $this->conn->quoteIdentifier($table, true); - $name = $this->conn->quoteIdentifier($this->conn->formatter->getIndexName($name), true); - return 'DROP INDEX ' . $name . ' ON ' . $table; - } - /** - * dropTable - * - * @param string $table name of table that should be dropped from the database - * @throws PDOException - * @return void - */ - public function dropTableSql($table) - { - $table = $this->conn->quoteIdentifier($table, true); - return 'DROP TABLE ' . $table; - } +. + */ +Doctrine::autoload('Doctrine_Export'); +/** + * Doctrine_Export_Mysql + * + * @package Doctrine + * @subpackage Export + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.phpdoctrine.com + * @since 1.0 + * @version $Revision$ + */ +class Doctrine_Export_Mysql extends Doctrine_Export +{ + /** + * create a new database + * + * @param string $name name of the database that should be created + * @return string + */ + public function createDatabaseSql($name) + { + return 'CREATE DATABASE ' . $this->conn->quoteIdentifier($name, true); + } + /** + * drop an existing database + * + * @param string $name name of the database that should be dropped + * @return string + */ + public function dropDatabaseSql($name) + { + return 'DROP DATABASE ' . $this->conn->quoteIdentifier($name); + } + /** + * create a new table + * + * @param string $name Name of the database that should be created + * @param array $fields Associative array that contains the definition of each field of the new table + * The indexes of the array entries are the names of the fields of the table an + * the array entry values are associative arrays like those that are meant to be + * passed with the field definitions to get[Type]Declaration() functions. + * array( + * 'id' => array( + * 'type' => 'integer', + * 'unsigned' => 1 + * 'notnull' => 1 + * 'default' => 0 + * ), + * 'name' => array( + * 'type' => 'text', + * 'length' => 12 + * ), + * 'password' => array( + * 'type' => 'text', + * 'length' => 12 + * ) + * ); + * @param array $options An associative array of table options: + * array( + * 'comment' => 'Foo', + * 'charset' => 'utf8', + * 'collate' => 'utf8_unicode_ci', + * 'type' => 'innodb', + * ); + * + * @return void + */ + public function createTableSql($name, array $fields, array $options = array()) + { + if ( ! $name) + throw new Doctrine_Export_Exception('no valid table name specified'); + + if (empty($fields)) { + throw new Doctrine_Export_Exception('no fields specified for table "'.$name.'"'); + } + $queryFields = $this->getFieldDeclarationList($fields); + + // build indexes for all foreign key fields (needed in MySQL!!) + if (isset($options['foreignKeys'])) { + foreach ($options['foreignKeys'] as $fk) { + $local = $fk['local']; + $found = false; + if (isset($options['indexes'])) { + foreach ($options['indexes'] as $definition) { + if (is_string($definition['fields'])) { + // Check if index already exists on the column + $found = ($local == $definition['fields']); + } else if (in_array($local, $definition['fields']) && count($definition['fields']) === 1) { + // Index already exists on the column + $found = true; + } + } + } + if (isset($options['primary']) && !empty($options['primary']) && + in_array($local, $options['primary'])) { + // field is part of the PK and therefore already indexed + $found = true; + } + + if ( ! $found) { + $options['indexes'][$local] = array('fields' => array($local => array())); + } + } + } + + // add all indexes + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach($options['indexes'] as $index => $definition) { + $queryFields .= ', ' . $this->getIndexDeclaration($index, $definition); + } + } + + // attach all primary keys + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_values($options['primary']); + $keyColumns = array_map(array($this->conn, 'quoteIdentifier'), $keyColumns); + $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + $query = 'CREATE TABLE ' . $this->conn->quoteIdentifier($name, true) . ' (' . $queryFields . ')'; + + $optionStrings = array(); + + if (isset($options['comment'])) { + $optionStrings['comment'] = 'COMMENT = ' . $this->dbh->quote($options['comment'], 'text'); + } + if (isset($options['charset'])) { + $optionStrings['charset'] = 'DEFAULT CHARACTER SET ' . $options['charset']; + if (isset($options['collate'])) { + $optionStrings['charset'] .= ' COLLATE ' . $options['collate']; + } + } + + $type = false; + + // get the type of the table + if (isset($options['type'])) { + $type = $options['type']; + } else { + $type = $this->conn->getAttribute(Doctrine::ATTR_DEFAULT_TABLE_TYPE); + } + + if ($type) { + $optionStrings[] = 'ENGINE = ' . $type; + } + + if ( ! empty($optionStrings)) { + $query.= ' '.implode(' ', $optionStrings); + } + $sql[] = $query; + + if (isset($options['foreignKeys'])) { + + foreach ((array) $options['foreignKeys'] as $k => $definition) { + if (is_array($definition)) { + $sql[] = $this->createForeignKeySql($name, $definition); + } + } + } + return $sql; + } + /** + * alter an existing table + * + * @param string $name name of the table that is intended to be changed. + * @param array $changes associative array that contains the details of each type + * of change that is intended to be performed. The types of + * changes that are currently supported are defined as follows: + * + * name + * + * New name for the table. + * + * add + * + * Associative array with the names of fields to be added as + * indexes of the array. The value of each entry of the array + * should be set to another associative array with the properties + * of the fields to be added. The properties of the fields should + * be the same as defined by the Metabase parser. + * + * + * remove + * + * Associative array with the names of fields to be removed as indexes + * of the array. Currently the values assigned to each entry are ignored. + * An empty array should be used for future compatibility. + * + * rename + * + * Associative array with the names of fields to be renamed as indexes + * of the array. The value of each entry of the array should be set to + * another associative array with the entry named name with the new + * field name and the entry named Declaration that is expected to contain + * the portion of the field declaration already in DBMS specific SQL code + * as it is used in the CREATE TABLE statement. + * + * change + * + * Associative array with the names of the fields to be changed as indexes + * of the array. Keep in mind that if it is intended to change either the + * name of a field and any other properties, the change array entries + * should have the new names of the fields as array indexes. + * + * The value of each entry of the array should be set to another associative + * array with the properties of the fields to that are meant to be changed as + * array entries. These entries should be assigned to the new values of the + * respective properties. The properties of the fields should be the same + * as defined by the Metabase parser. + * + * Example + * array( + * 'name' => 'userlist', + * 'add' => array( + * 'quota' => array( + * 'type' => 'integer', + * 'unsigned' => 1 + * ) + * ), + * 'remove' => array( + * 'file_limit' => array(), + * 'time_limit' => array() + * ), + * 'change' => array( + * 'name' => array( + * 'length' => '20', + * 'definition' => array( + * 'type' => 'text', + * 'length' => 20, + * ), + * ) + * ), + * 'rename' => array( + * 'sex' => array( + * 'name' => 'gender', + * 'definition' => array( + * 'type' => 'text', + * 'length' => 1, + * 'default' => 'M', + * ), + * ) + * ) + * ) + * + * @param boolean $check indicates whether the function should just check if the DBMS driver + * can perform the requested table alterations if the value is true or + * actually perform them otherwise. + * @return boolean + */ + public function alterTableSql($name, array $changes, $check = false) + { + if ( ! $name) { + throw new Doctrine_Export_Exception('no valid table name specified'); + } + foreach ($changes as $changeName => $change) { + switch ($changeName) { + case 'add': + case 'remove': + case 'change': + case 'rename': + case 'name': + break; + default: + throw new Doctrine_Export_Exception('change type "' . $changeName . '" not yet supported'); + } + } + + if ($check) { + return true; + } + + $query = ''; + if ( ! empty($changes['name'])) { + $change_name = $this->conn->quoteIdentifier($changes['name']); + $query .= 'RENAME TO ' . $change_name; + } + + if ( ! empty($changes['add']) && is_array($changes['add'])) { + foreach ($changes['add'] as $fieldName => $field) { + if ($query) { + $query.= ', '; + } + $query.= 'ADD ' . $this->getDeclaration($fieldName, $field); + } + } + + if ( ! empty($changes['remove']) && is_array($changes['remove'])) { + foreach ($changes['remove'] as $fieldName => $field) { + if ($query) { + $query .= ', '; + } + $fieldName = $this->conn->quoteIdentifier($fieldName); + $query .= 'DROP ' . $fieldName; + } + } + + $rename = array(); + if ( ! empty($changes['rename']) && is_array($changes['rename'])) { + foreach ($changes['rename'] as $fieldName => $field) { + $rename[$field['name']] = $fieldName; + } + } + + if ( ! empty($changes['change']) && is_array($changes['change'])) { + foreach ($changes['change'] as $fieldName => $field) { + if ($query) { + $query.= ', '; + } + if (isset($rename[$fieldName])) { + $oldFieldName = $rename[$fieldName]; + unset($rename[$fieldName]); + } else { + $oldFieldName = $fieldName; + } + $oldFieldName = $this->conn->quoteIdentifier($oldFieldName, true); + $query .= 'CHANGE ' . $oldFieldName . ' ' + . $this->getDeclaration($fieldName, $field['definition']); + } + } + + if ( ! empty($rename) && is_array($rename)) { + foreach ($rename as $renameName => $renamedField) { + if ($query) { + $query.= ', '; + } + $field = $changes['rename'][$renamedField]; + $renamedField = $this->conn->quoteIdentifier($renamedField, true); + $query .= 'CHANGE ' . $renamedField . ' ' + . $this->getDeclaration($field['name'], $field['definition']); + } + } + + if ( ! $query) { + return false; + } + + $name = $this->conn->quoteIdentifier($name, true); + + return 'ALTER TABLE ' . $name . ' ' . $query; + } + /** + * create sequence + * + * @param string $sequenceName name of the sequence to be created + * @param string $start start value of the sequence; default is 1 + * @param array $options An associative array of table options: + * array( + * 'comment' => 'Foo', + * 'charset' => 'utf8', + * 'collate' => 'utf8_unicode_ci', + * 'type' => 'innodb', + * ); + * @return boolean + */ + public function createSequence($sequenceName, $start = 1, array $options = array()) + { + $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($sequenceName), true); + $seqcolName = $this->conn->quoteIdentifier($this->conn->getAttribute(Doctrine::ATTR_SEQCOL_NAME), true); + + $optionsStrings = array(); + + if (isset($options['comment']) && ! empty($options['comment'])) { + $optionsStrings['comment'] = 'COMMENT = ' . $this->conn->quote($options['comment'], 'string'); + } + + if (isset($options['charset']) && ! empty($options['charset'])) { + $optionsStrings['charset'] = 'DEFAULT CHARACTER SET ' . $options['charset']; + + if (isset($options['collate'])) { + $optionsStrings['collate'] .= ' COLLATE ' . $options['collate']; + } + } + + $type = false; + + if (isset($options['type'])) { + $type = $options['type']; + } else { + $type = $this->conn->default_table_type; + } + if ($type) { + $optionsStrings[] = 'ENGINE = ' . $type; + } + + + try { + $query = 'CREATE TABLE ' . $sequenceName + . ' (' . $seqcolName . ' INT NOT NULL AUTO_INCREMENT, PRIMARY KEY (' + . $seqcolName . '))' + . strlen($this->conn->default_table_type) ? ' TYPE = ' + . $this->conn->default_table_type : ''; + + $res = $this->conn->exec($query); + } catch(Doctrine_Connection_Exception $e) { + throw new Doctrine_Export_Exception('could not create sequence table'); + } + + if ($start == 1) + return true; + + $query = 'INSERT INTO ' . $sequenceName + . ' (' . $seqcolName . ') VALUES (' . ($start - 1) . ')'; + + $res = $this->conn->exec($query); + + // Handle error + try { + $result = $this->conn->exec('DROP TABLE ' . $sequenceName); + } catch(Doctrine_Connection_Exception $e) { + throw new Doctrine_Export_Exception('could not drop inconsistent sequence table'); + } + + + } + /** + * Get the stucture of a field into an array + * + * @author Leoncx + * @param string $table name of the table on which the index is to be created + * @param string $name name of the index to be created + * @param array $definition associative array that defines properties of the index to be created. + * Currently, only one property named FIELDS is supported. This property + * is also an associative with the names of the index fields as array + * indexes. Each entry of this array is set to another type of associative + * array that specifies properties of the index that are specific to + * each field. + * + * Currently, only the sorting property is supported. It should be used + * to define the sorting direction of the index. It may be set to either + * ascending or descending. + * + * Not all DBMS support index sorting direction configuration. The DBMS + * drivers of those that do not support it ignore this property. Use the + * function supports() to determine whether the DBMS driver can manage indexes. + * + * Example + * array( + * 'fields' => array( + * 'user_name' => array( + * 'sorting' => 'ASC' + * 'length' => 10 + * ), + * 'last_login' => array() + * ) + * ) + * @throws PDOException + * @return void + */ + public function createIndexSql($table, $name, array $definition) + { + $table = $table; + $name = $this->conn->getIndexName($name); + $name = $this->conn->quoteIdentifier($name); + $type = ''; + if (isset($definition['type'])) { + switch (strtolower($definition['type'])) { + case 'fulltext': + case 'unique': + $type = strtoupper($definition['type']) . ' '; + break; + default: + throw new Doctrine_Export_Exception('Unknown index type ' . $definition['type']); + } + } + $query = 'CREATE ' . $type . 'INDEX ' . $name . ' ON ' . $table; + $query .= ' (' . $this->getIndexFieldDeclarationList() . ')'; + + return $query; + } + /** + * getDefaultDeclaration + * Obtain DBMS specific SQL code portion needed to set a default value + * declaration to be used in statements like CREATE TABLE. + * + * @param array $field field definition array + * @return string DBMS specific SQL code portion needed to set a default value + */ + public function getDefaultFieldDeclaration($field) + { + $default = ''; + if (isset($field['default']) && $field['length'] <= 255) { + if ($field['default'] === '') { + $field['default'] = empty($field['notnull']) + ? null : $this->valid_default_values[$field['type']]; + + if ($field['default'] === '' + && ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_EMPTY_TO_NULL) + ) { + $field['default'] = ' '; + } + } + + $default = ' DEFAULT ' . $this->conn->quote($field['default'], $field['type']); + } + return $default; + } + /** + * Obtain DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @param string $charset name of the index + * @param array $definition index definition + * @return string DBMS specific SQL code portion needed to set an index + */ + public function getIndexDeclaration($name, array $definition) + { + $name = $this->conn->formatter->getIndexName($name); + $type = ''; + if (isset($definition['type'])) { + switch (strtolower($definition['type'])) { + case 'fulltext': + case 'unique': + $type = strtoupper($definition['type']) . ' '; + break; + default: + throw new Doctrine_Export_Exception('Unknown index type ' . $definition['type']); + } + } + + if ( ! isset($definition['fields'])) { + throw new Doctrine_Export_Exception('No index columns given.'); + } + if ( ! is_array($definition['fields'])) { + $definition['fields'] = array($definition['fields']); + } + + $query = $type . 'INDEX ' . $this->conn->quoteIdentifier($name); + + $query .= ' (' . $this->getIndexFieldDeclarationList($definition['fields']) . ')'; + + return $query; + } + /** + * getIndexFieldDeclarationList + * Obtain DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @return string + */ + public function getIndexFieldDeclarationList(array $fields) + { + $declFields = array(); + + foreach ($fields as $fieldName => $field) { + $fieldString = $this->conn->quoteIdentifier($fieldName); + + if (is_array($field)) { + if (isset($field['length'])) { + $fieldString .= '(' . $field['length'] . ')'; + } + + if (isset($field['sorting'])) { + $sort = strtoupper($field['sorting']); + switch ($sort) { + case 'ASC': + case 'DESC': + $fieldString .= ' ' . $sort; + break; + default: + throw new Doctrine_Export_Exception('Unknown index sorting option given.'); + } + } + } else { + $fieldString = $this->conn->quoteIdentifier($field); + } + $declFields[] = $fieldString; + } + return implode(', ', $declFields); + } + /** + * getAdvancedForeignKeyOptions + * Return the FOREIGN KEY query section dealing with non-standard options + * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... + * + * @param array $definition + * @return string + */ + public function getAdvancedForeignKeyOptions(array $definition) + { + $query = ''; + if ( ! empty($definition['match'])) { + $query .= ' MATCH ' . $definition['match']; + } + if ( ! empty($definition['onUpdate'])) { + $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialAction($definition['onUpdate']); + } + if ( ! empty($definition['onDelete'])) { + $query .= ' ON DELETE ' . $this->getForeignKeyReferentialAction($definition['onDelete']); + } + return $query; + } + /** + * drop existing index + * + * @param string $table name of table that should be used in method + * @param string $name name of the index to be dropped + * @return void + */ + public function dropIndexSql($table, $name) + { + $table = $this->conn->quoteIdentifier($table, true); + $name = $this->conn->quoteIdentifier($this->conn->formatter->getIndexName($name), true); + return 'DROP INDEX ' . $name . ' ON ' . $table; + } + /** + * dropTable + * + * @param string $table name of table that should be dropped from the database + * @throws PDOException + * @return void + */ + public function dropTableSql($table) + { + $table = $this->conn->quoteIdentifier($table, true); + return 'DROP TABLE ' . $table; + } } \ No newline at end of file diff --git a/lib/Doctrine/Export/Oracle.php b/lib/Doctrine/Export/Oracle.php index b42404931..53a8c00af 100644 --- a/lib/Doctrine/Export/Oracle.php +++ b/lib/Doctrine/Export/Oracle.php @@ -1,493 +1,493 @@ -. - */ -Doctrine::autoload('Doctrine_Export'); -/** - * Doctrine_Export_Oracle - * - * @package Doctrine - * @subpackage Export - * @author Konsta Vesterinen - * @author Lukas Smith (PEAR MDB2 library) - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.phpdoctrine.com - * @since 1.0 - * @version $Revision$ - */ -class Doctrine_Export_Oracle extends Doctrine_Export -{ - /** - * create a new database - * - * @param object $db database object that is extended by this class - * @param string $name name of the database that should be created - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - public function createDatabase($name) - { - if ( ! $this->conn->getAttribute(Doctrine::ATTR_EMULATE_DATABASE)) - throw new Doctrine_Export_Exception('database creation is only supported if the "emulate_database" attribute is enabled'); - - $username = sprintf($this->conn->getAttribute(Doctrine::ATTR_DB_NAME_FORMAT), $name); - $password = $this->conn->dsn['password'] ? $this->conn->dsn['password'] : $name; - - $tablespace = $this->conn->getAttribute(Doctrine::ATTR_DB_NAME_FORMAT) - ? ' DEFAULT TABLESPACE '.$this->conn->options['default_tablespace'] : ''; - - $query = 'CREATE USER ' . $username . ' IDENTIFIED BY ' . $password . $tablespace; - $result = $this->conn->exec($query); - - try { - $query = 'GRANT CREATE SESSION, CREATE TABLE, UNLIMITED TABLESPACE, CREATE SEQUENCE, CREATE TRIGGER TO ' . $username; - $result = $this->conn->exec($query); - } catch (Exception $e) { - $query = 'DROP USER '.$username.' CASCADE'; - $result2 = $this->conn->exec($query); - } - return true; - } - /** - * drop an existing database - * - * @param object $this->conn database object that is extended by this class - * @param string $name name of the database that should be dropped - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - public function dropDatabase($name) - { - if ( ! $this->conn->getAttribute(Doctrine::ATTR_EMULATE_DATABASE)) - throw new Doctrine_Export_Exception('database dropping is only supported if the - "emulate_database" option is enabled'); - - $username = sprintf($this->conn->getAttribute(Doctrine::ATTR_DB_NAME_FORMAT), $name); - - return $this->conn->exec('DROP USER ' . $username . ' CASCADE'); - } - /** - * add an autoincrement sequence + trigger - * - * @param string $name name of the PK field - * @param string $table name of the table - * @param string $start start value for the sequence - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access private - */ - public function _makeAutoincrement($name, $table, $start = 1) - { - $sql = array(); - $table = strtoupper($table); - $indexName = $table . '_AI_PK'; - $definition = array( - 'primary' => true, - 'fields' => array($name => true), - ); - - $sql[] = $this->createConstraintSql($table, $indexName, $definition); - - if (is_null($start)) { - $query = 'SELECT MAX(' . $this->conn->quoteIdentifier($name, true) . ') FROM ' . $this->conn->quoteIdentifier($table, true); - $start = $this->conn->fetchOne($query); - - ++$start; - } - - $sql[] = $this->createSequenceSql($table, $start); - - $sequenceName = $this->conn->formatter->getSequenceName($table); - $triggerName = $this->conn->quoteIdentifier($table . '_AI_PK', true); - $table = $this->conn->quoteIdentifier($table, true); - $name = $this->conn->quoteIdentifier($name, true); - $sql[] = 'CREATE TRIGGER ' . $triggerName . ' - BEFORE INSERT - ON '.$table.' - FOR EACH ROW -DECLARE - last_Sequence NUMBER; - last_InsertID NUMBER; -BEGIN - SELECT '.$sequenceName.'.NEXTVAL INTO :NEW.'.$name.' FROM DUAL; - IF (:NEW.'.$name.' IS NULL OR :NEW.'.$name.' = 0) THEN - SELECT '.$sequenceName.'.NEXTVAL INTO :NEW.'.$name.' FROM DUAL; - ELSE - SELECT NVL(Last_Number, 0) INTO last_Sequence - FROM User_Sequences - WHERE UPPER(Sequence_Name) = UPPER(\''.$sequenceName.'\'); - SELECT :NEW.id INTO last_InsertID FROM DUAL; - WHILE (last_InsertID > last_Sequence) LOOP - SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL; - END LOOP; - END IF; -END; -'; - return $sql; - } - /** - * drop an existing autoincrement sequence + trigger - * - * @param string $table name of the table - * @return void - */ - public function dropAutoincrement($table) - { - $table = strtoupper($table); - $triggerName = $table . '_AI_PK'; - $trigger_name_quoted = $this->conn->quote($triggerName); - $query = 'SELECT trigger_name FROM user_triggers'; - $query.= ' WHERE trigger_name='.$trigger_name_quoted.' OR trigger_name='.strtoupper($trigger_name_quoted); - $trigger = $this->conn->fetchOne($query); - - if ($trigger) { - $trigger_name = $this->conn->quoteIdentifier($table . '_AI_PK', true); - $trigger_sql = 'DROP TRIGGER ' . $trigger_name; - - // if throws exception, trigger for autoincrement PK could not be dropped - $this->conn->exec($trigger_sql); - - // if throws exception, sequence for autoincrement PK could not be dropped - $this->dropSequence($table); - - $indexName = $table . '_AI_PK'; - - // if throws exception, primary key for autoincrement PK could not be dropped - $this->dropConstraint($table, $indexName); - } - } - /** - * A method to return the required SQL string that fits between CREATE ... TABLE - * to create the table as a temporary table. - * - * @return string The string required to be placed between "CREATE" and "TABLE" - * to generate a temporary table, if possible. - */ - public function getTemporaryTableQuery() - { - return 'GLOBAL TEMPORARY'; - } - /** - * getAdvancedForeignKeyOptions - * Return the FOREIGN KEY query section dealing with non-standard options - * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... - * - * @param array $definition foreign key definition - * @return string - * @access protected - */ - public function getAdvancedForeignKeyOptions(array $definition) - { - $query = ''; - if (isset($definition['onDelete'])) { - $query .= ' ON DELETE ' . $definition['onDelete']; - } - if (isset($definition['deferrable'])) { - $query .= ' DEFERRABLE'; - } else { - $query .= ' NOT DEFERRABLE'; - } - if (isset($definition['feferred'])) { - $query .= ' INITIALLY DEFERRED'; - } else { - $query .= ' INITIALLY IMMEDIATE'; - } - return $query; - } - - /** - * create a new table - * - * @param string $name Name of the database that should be created - * @param array $fields Associative array that contains the definition of each field of the new table - * The indexes of the array entries are the names of the fields of the table an - * the array entry values are associative arrays like those that are meant to be - * passed with the field definitions to get[Type]Declaration() functions. - * - * Example - * array( - * - * 'id' => array( - * 'type' => 'integer', - * 'unsigned' => 1 - * 'notnull' => 1 - * 'default' => 0 - * ), - * 'name' => array( - * 'type' => 'text', - * 'length' => 12 - * ), - * 'password' => array( - * 'type' => 'text', - * 'length' => 12 - * ) - * ); - * @param array $options An associative array of table options: - * - * @return void - */ - public function createTable($name, array $fields, array $options = array()) - { - $this->conn->beginTransaction(); - - foreach ($this->createTableSql($name, $fields, $options) as $sql) { - $this->conn->exec($sql); - } - - $this->conn->commit(); - } - - /** - * create a new table - * - * @param string $name Name of the database that should be created - * @param array $fields Associative array that contains the definition of each field of the new table - * The indexes of the array entries are the names of the fields of the table an - * the array entry values are associative arrays like those that are meant to be - * passed with the field definitions to get[Type]Declaration() functions. - * - * Example - * array( - * - * 'id' => array( - * 'type' => 'integer', - * 'unsigned' => 1 - * 'notnull' => 1 - * 'default' => 0 - * ), - * 'name' => array( - * 'type' => 'text', - * 'length' => 12 - * ), - * 'password' => array( - * 'type' => 'text', - * 'length' => 12 - * ) - * ); - * @param array $options An associative array of table options: - * - * @return void - */ - public function createTableSql($name, array $fields, array $options = array()) - { - $sql = parent::createTableSql($name, $fields, $options); - - foreach ($fields as $fieldName => $field) { - if (isset($field['autoincrement']) && $field['autoincrement'] || - (isset($field['autoinc']) && $fields['autoinc'])) { - $sql = array_merge($sql, $this->_makeAutoincrement($fieldName, $name)); - } - } - - return $sql; - } - /** - * drop an existing table - * - * @param string $name name of the table that should be dropped - * @return void - */ - public function dropTable($name) - { - //$this->conn->beginNestedTransaction(); - $result = $this->dropAutoincrement($name); - $result = parent::dropTable($name); - //$this->conn->completeNestedTransaction(); - return $result; - } - /** - * alter an existing table - * - * @param string $name name of the table that is intended to be changed. - * @param array $changes associative array that contains the details of each type - * of change that is intended to be performed. The types of - * changes that are currently supported are defined as follows: - * - * name - * - * New name for the table. - * - * add - * - * Associative array with the names of fields to be added as - * indexes of the array. The value of each entry of the array - * should be set to another associative array with the properties - * of the fields to be added. The properties of the fields should - * be the same as defined by the MDB2 parser. - * - * - * remove - * - * Associative array with the names of fields to be removed as indexes - * of the array. Currently the values assigned to each entry are ignored. - * An empty array should be used for future compatibility. - * - * rename - * - * Associative array with the names of fields to be renamed as indexes - * of the array. The value of each entry of the array should be set to - * another associative array with the entry named name with the new - * field name and the entry named Declaration that is expected to contain - * the portion of the field declaration already in DBMS specific SQL code - * as it is used in the CREATE TABLE statement. - * - * change - * - * Associative array with the names of the fields to be changed as indexes - * of the array. Keep in mind that if it is intended to change either the - * name of a field and any other properties, the change array entries - * should have the new names of the fields as array indexes. - * - * The value of each entry of the array should be set to another associative - * array with the properties of the fields to that are meant to be changed as - * array entries. These entries should be assigned to the new values of the - * respective properties. The properties of the fields should be the same - * as defined by the MDB2 parser. - * - * Example - * array( - * 'name' => 'userlist', - * 'add' => array( - * 'quota' => array( - * 'type' => 'integer', - * 'unsigned' => 1 - * ) - * ), - * 'remove' => array( - * 'file_limit' => array(), - * 'time_limit' => array() - * ), - * 'change' => array( - * 'name' => array( - * 'length' => '20', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 20, - * ), - * ) - * ), - * 'rename' => array( - * 'sex' => array( - * 'name' => 'gender', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 1, - * 'default' => 'M', - * ), - * ) - * ) - * ) - * - * @param boolean $check indicates whether the function should just check if the DBMS driver - * can perform the requested table alterations if the value is true or - * actually perform them otherwise. - * @return void - */ - public function alterTable($name, array $changes, $check = false) - { - - foreach ($changes as $changeName => $change) { - switch ($changeName) { - case 'add': - case 'remove': - case 'change': - case 'name': - case 'rename': - break; - default: - throw new Doctrine_Export_Exception('change type "' . $changeName . '" not yet supported'); - } - } - - if ($check) { - return false; - } - - $name = $this->conn->quoteIdentifier($name, true); - - if ( ! empty($changes['add']) && is_array($changes['add'])) { - $fields = array(); - foreach ($changes['add'] as $fieldName => $field) { - $fields[] = $this->conn->getDeclaration($fieldName, $field); - } - $result = $this->conn->exec('ALTER TABLE ' . $name . ' ADD (' . implode(', ', $fields) . ')'); - } - - if ( ! empty($changes['change']) && is_array($changes['change'])) { - $fields = array(); - foreach ($changes['change'] as $fieldName => $field) { - $fields[] = $fieldName. ' ' . $this->conn->getDeclaration('', $field['definition']); - } - $result = $this->conn->exec('ALTER TABLE ' . $name . ' MODIFY (' . implode(', ', $fields) . ')'); - } - - if ( ! empty($changes['rename']) && is_array($changes['rename'])) { - foreach ($changes['rename'] as $fieldName => $field) { - $query = 'ALTER TABLE ' . $name . ' RENAME COLUMN ' . $this->conn->quoteIdentifier($fieldName, true) - . ' TO ' . $this->conn->quoteIdentifier($field['name']); - - $result = $this->conn->exec($query); - } - } - - if ( ! empty($changes['remove']) && is_array($changes['remove'])) { - $fields = array(); - foreach ($changes['remove'] as $fieldName => $field) { - $fields[] = $this->conn->quoteIdentifier($fieldName, true); - } - $result = $this->conn->exec('ALTER TABLE ' . $name . ' DROP COLUMN ' . implode(', ', $fields)); - } - - if ( ! empty($changes['name'])) { - $changeName = $this->conn->quoteIdentifier($changes['name'], true); - $result = $this->conn->exec('ALTER TABLE ' . $name . ' RENAME TO ' . $changeName); - } - } - /** - * create sequence - * - * @param string $seqName name of the sequence to be created - * @param string $start start value of the sequence; default is 1 - * @param array $options An associative array of table options: - * array( - * 'comment' => 'Foo', - * 'charset' => 'utf8', - * 'collate' => 'utf8_unicode_ci', - * ); - * @return string - */ - public function createSequenceSql($seqName, $start = 1, array $options = array()) - { - $sequenceName = $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($seqName), true); - $query = 'CREATE SEQUENCE ' . $sequenceName . ' START WITH ' . $start . ' INCREMENT BY 1 NOCACHE'; - $query .= ($start < 1 ? ' MINVALUE ' . $start : ''); - return $query; - } - /** - * drop existing sequence - * - * @param object $this->conn database object that is extended by this class - * @param string $seqName name of the sequence to be dropped - * @return string - */ - public function dropSequenceSql($seqName) - { - $sequenceName = $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($seqName), true); - return 'DROP SEQUENCE ' . $sequenceName; - } +. + */ +Doctrine::autoload('Doctrine_Export'); +/** + * Doctrine_Export_Oracle + * + * @package Doctrine + * @subpackage Export + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.phpdoctrine.com + * @since 1.0 + * @version $Revision$ + */ +class Doctrine_Export_Oracle extends Doctrine_Export +{ + /** + * create a new database + * + * @param object $db database object that is extended by this class + * @param string $name name of the database that should be created + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + public function createDatabase($name) + { + if ( ! $this->conn->getAttribute(Doctrine::ATTR_EMULATE_DATABASE)) + throw new Doctrine_Export_Exception('database creation is only supported if the "emulate_database" attribute is enabled'); + + $username = sprintf($this->conn->getAttribute(Doctrine::ATTR_DB_NAME_FORMAT), $name); + $password = $this->conn->dsn['password'] ? $this->conn->dsn['password'] : $name; + + $tablespace = $this->conn->getAttribute(Doctrine::ATTR_DB_NAME_FORMAT) + ? ' DEFAULT TABLESPACE '.$this->conn->options['default_tablespace'] : ''; + + $query = 'CREATE USER ' . $username . ' IDENTIFIED BY ' . $password . $tablespace; + $result = $this->conn->exec($query); + + try { + $query = 'GRANT CREATE SESSION, CREATE TABLE, UNLIMITED TABLESPACE, CREATE SEQUENCE, CREATE TRIGGER TO ' . $username; + $result = $this->conn->exec($query); + } catch (Exception $e) { + $query = 'DROP USER '.$username.' CASCADE'; + $result2 = $this->conn->exec($query); + } + return true; + } + /** + * drop an existing database + * + * @param object $this->conn database object that is extended by this class + * @param string $name name of the database that should be dropped + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + public function dropDatabase($name) + { + if ( ! $this->conn->getAttribute(Doctrine::ATTR_EMULATE_DATABASE)) + throw new Doctrine_Export_Exception('database dropping is only supported if the + "emulate_database" option is enabled'); + + $username = sprintf($this->conn->getAttribute(Doctrine::ATTR_DB_NAME_FORMAT), $name); + + return $this->conn->exec('DROP USER ' . $username . ' CASCADE'); + } + /** + * add an autoincrement sequence + trigger + * + * @param string $name name of the PK field + * @param string $table name of the table + * @param string $start start value for the sequence + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access private + */ + public function _makeAutoincrement($name, $table, $start = 1) + { + $sql = array(); + $table = strtoupper($table); + $indexName = $table . '_AI_PK'; + $definition = array( + 'primary' => true, + 'fields' => array($name => true), + ); + + $sql[] = $this->createConstraintSql($table, $indexName, $definition); + + if (is_null($start)) { + $query = 'SELECT MAX(' . $this->conn->quoteIdentifier($name, true) . ') FROM ' . $this->conn->quoteIdentifier($table, true); + $start = $this->conn->fetchOne($query); + + ++$start; + } + + $sql[] = $this->createSequenceSql($table, $start); + + $sequenceName = $this->conn->formatter->getSequenceName($table); + $triggerName = $this->conn->quoteIdentifier($table . '_AI_PK', true); + $table = $this->conn->quoteIdentifier($table, true); + $name = $this->conn->quoteIdentifier($name, true); + $sql[] = 'CREATE TRIGGER ' . $triggerName . ' + BEFORE INSERT + ON '.$table.' + FOR EACH ROW +DECLARE + last_Sequence NUMBER; + last_InsertID NUMBER; +BEGIN + SELECT '.$sequenceName.'.NEXTVAL INTO :NEW.'.$name.' FROM DUAL; + IF (:NEW.'.$name.' IS NULL OR :NEW.'.$name.' = 0) THEN + SELECT '.$sequenceName.'.NEXTVAL INTO :NEW.'.$name.' FROM DUAL; + ELSE + SELECT NVL(Last_Number, 0) INTO last_Sequence + FROM User_Sequences + WHERE UPPER(Sequence_Name) = UPPER(\''.$sequenceName.'\'); + SELECT :NEW.id INTO last_InsertID FROM DUAL; + WHILE (last_InsertID > last_Sequence) LOOP + SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL; + END LOOP; + END IF; +END; +'; + return $sql; + } + /** + * drop an existing autoincrement sequence + trigger + * + * @param string $table name of the table + * @return void + */ + public function dropAutoincrement($table) + { + $table = strtoupper($table); + $triggerName = $table . '_AI_PK'; + $trigger_name_quoted = $this->conn->quote($triggerName); + $query = 'SELECT trigger_name FROM user_triggers'; + $query.= ' WHERE trigger_name='.$trigger_name_quoted.' OR trigger_name='.strtoupper($trigger_name_quoted); + $trigger = $this->conn->fetchOne($query); + + if ($trigger) { + $trigger_name = $this->conn->quoteIdentifier($table . '_AI_PK', true); + $trigger_sql = 'DROP TRIGGER ' . $trigger_name; + + // if throws exception, trigger for autoincrement PK could not be dropped + $this->conn->exec($trigger_sql); + + // if throws exception, sequence for autoincrement PK could not be dropped + $this->dropSequence($table); + + $indexName = $table . '_AI_PK'; + + // if throws exception, primary key for autoincrement PK could not be dropped + $this->dropConstraint($table, $indexName); + } + } + /** + * A method to return the required SQL string that fits between CREATE ... TABLE + * to create the table as a temporary table. + * + * @return string The string required to be placed between "CREATE" and "TABLE" + * to generate a temporary table, if possible. + */ + public function getTemporaryTableQuery() + { + return 'GLOBAL TEMPORARY'; + } + /** + * getAdvancedForeignKeyOptions + * Return the FOREIGN KEY query section dealing with non-standard options + * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... + * + * @param array $definition foreign key definition + * @return string + * @access protected + */ + public function getAdvancedForeignKeyOptions(array $definition) + { + $query = ''; + if (isset($definition['onDelete'])) { + $query .= ' ON DELETE ' . $definition['onDelete']; + } + if (isset($definition['deferrable'])) { + $query .= ' DEFERRABLE'; + } else { + $query .= ' NOT DEFERRABLE'; + } + if (isset($definition['feferred'])) { + $query .= ' INITIALLY DEFERRED'; + } else { + $query .= ' INITIALLY IMMEDIATE'; + } + return $query; + } + + /** + * create a new table + * + * @param string $name Name of the database that should be created + * @param array $fields Associative array that contains the definition of each field of the new table + * The indexes of the array entries are the names of the fields of the table an + * the array entry values are associative arrays like those that are meant to be + * passed with the field definitions to get[Type]Declaration() functions. + * + * Example + * array( + * + * 'id' => array( + * 'type' => 'integer', + * 'unsigned' => 1 + * 'notnull' => 1 + * 'default' => 0 + * ), + * 'name' => array( + * 'type' => 'text', + * 'length' => 12 + * ), + * 'password' => array( + * 'type' => 'text', + * 'length' => 12 + * ) + * ); + * @param array $options An associative array of table options: + * + * @return void + */ + public function createTable($name, array $fields, array $options = array()) + { + $this->conn->beginTransaction(); + + foreach ($this->createTableSql($name, $fields, $options) as $sql) { + $this->conn->exec($sql); + } + + $this->conn->commit(); + } + + /** + * create a new table + * + * @param string $name Name of the database that should be created + * @param array $fields Associative array that contains the definition of each field of the new table + * The indexes of the array entries are the names of the fields of the table an + * the array entry values are associative arrays like those that are meant to be + * passed with the field definitions to get[Type]Declaration() functions. + * + * Example + * array( + * + * 'id' => array( + * 'type' => 'integer', + * 'unsigned' => 1 + * 'notnull' => 1 + * 'default' => 0 + * ), + * 'name' => array( + * 'type' => 'text', + * 'length' => 12 + * ), + * 'password' => array( + * 'type' => 'text', + * 'length' => 12 + * ) + * ); + * @param array $options An associative array of table options: + * + * @return void + */ + public function createTableSql($name, array $fields, array $options = array()) + { + $sql = parent::createTableSql($name, $fields, $options); + + foreach ($fields as $fieldName => $field) { + if (isset($field['autoincrement']) && $field['autoincrement'] || + (isset($field['autoinc']) && $fields['autoinc'])) { + $sql = array_merge($sql, $this->_makeAutoincrement($fieldName, $name)); + } + } + + return $sql; + } + /** + * drop an existing table + * + * @param string $name name of the table that should be dropped + * @return void + */ + public function dropTable($name) + { + //$this->conn->beginNestedTransaction(); + $result = $this->dropAutoincrement($name); + $result = parent::dropTable($name); + //$this->conn->completeNestedTransaction(); + return $result; + } + /** + * alter an existing table + * + * @param string $name name of the table that is intended to be changed. + * @param array $changes associative array that contains the details of each type + * of change that is intended to be performed. The types of + * changes that are currently supported are defined as follows: + * + * name + * + * New name for the table. + * + * add + * + * Associative array with the names of fields to be added as + * indexes of the array. The value of each entry of the array + * should be set to another associative array with the properties + * of the fields to be added. The properties of the fields should + * be the same as defined by the MDB2 parser. + * + * + * remove + * + * Associative array with the names of fields to be removed as indexes + * of the array. Currently the values assigned to each entry are ignored. + * An empty array should be used for future compatibility. + * + * rename + * + * Associative array with the names of fields to be renamed as indexes + * of the array. The value of each entry of the array should be set to + * another associative array with the entry named name with the new + * field name and the entry named Declaration that is expected to contain + * the portion of the field declaration already in DBMS specific SQL code + * as it is used in the CREATE TABLE statement. + * + * change + * + * Associative array with the names of the fields to be changed as indexes + * of the array. Keep in mind that if it is intended to change either the + * name of a field and any other properties, the change array entries + * should have the new names of the fields as array indexes. + * + * The value of each entry of the array should be set to another associative + * array with the properties of the fields to that are meant to be changed as + * array entries. These entries should be assigned to the new values of the + * respective properties. The properties of the fields should be the same + * as defined by the MDB2 parser. + * + * Example + * array( + * 'name' => 'userlist', + * 'add' => array( + * 'quota' => array( + * 'type' => 'integer', + * 'unsigned' => 1 + * ) + * ), + * 'remove' => array( + * 'file_limit' => array(), + * 'time_limit' => array() + * ), + * 'change' => array( + * 'name' => array( + * 'length' => '20', + * 'definition' => array( + * 'type' => 'text', + * 'length' => 20, + * ), + * ) + * ), + * 'rename' => array( + * 'sex' => array( + * 'name' => 'gender', + * 'definition' => array( + * 'type' => 'text', + * 'length' => 1, + * 'default' => 'M', + * ), + * ) + * ) + * ) + * + * @param boolean $check indicates whether the function should just check if the DBMS driver + * can perform the requested table alterations if the value is true or + * actually perform them otherwise. + * @return void + */ + public function alterTable($name, array $changes, $check = false) + { + + foreach ($changes as $changeName => $change) { + switch ($changeName) { + case 'add': + case 'remove': + case 'change': + case 'name': + case 'rename': + break; + default: + throw new Doctrine_Export_Exception('change type "' . $changeName . '" not yet supported'); + } + } + + if ($check) { + return false; + } + + $name = $this->conn->quoteIdentifier($name, true); + + if ( ! empty($changes['add']) && is_array($changes['add'])) { + $fields = array(); + foreach ($changes['add'] as $fieldName => $field) { + $fields[] = $this->conn->getDeclaration($fieldName, $field); + } + $result = $this->conn->exec('ALTER TABLE ' . $name . ' ADD (' . implode(', ', $fields) . ')'); + } + + if ( ! empty($changes['change']) && is_array($changes['change'])) { + $fields = array(); + foreach ($changes['change'] as $fieldName => $field) { + $fields[] = $fieldName. ' ' . $this->conn->getDeclaration('', $field['definition']); + } + $result = $this->conn->exec('ALTER TABLE ' . $name . ' MODIFY (' . implode(', ', $fields) . ')'); + } + + if ( ! empty($changes['rename']) && is_array($changes['rename'])) { + foreach ($changes['rename'] as $fieldName => $field) { + $query = 'ALTER TABLE ' . $name . ' RENAME COLUMN ' . $this->conn->quoteIdentifier($fieldName, true) + . ' TO ' . $this->conn->quoteIdentifier($field['name']); + + $result = $this->conn->exec($query); + } + } + + if ( ! empty($changes['remove']) && is_array($changes['remove'])) { + $fields = array(); + foreach ($changes['remove'] as $fieldName => $field) { + $fields[] = $this->conn->quoteIdentifier($fieldName, true); + } + $result = $this->conn->exec('ALTER TABLE ' . $name . ' DROP COLUMN ' . implode(', ', $fields)); + } + + if ( ! empty($changes['name'])) { + $changeName = $this->conn->quoteIdentifier($changes['name'], true); + $result = $this->conn->exec('ALTER TABLE ' . $name . ' RENAME TO ' . $changeName); + } + } + /** + * create sequence + * + * @param string $seqName name of the sequence to be created + * @param string $start start value of the sequence; default is 1 + * @param array $options An associative array of table options: + * array( + * 'comment' => 'Foo', + * 'charset' => 'utf8', + * 'collate' => 'utf8_unicode_ci', + * ); + * @return string + */ + public function createSequenceSql($seqName, $start = 1, array $options = array()) + { + $sequenceName = $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($seqName), true); + $query = 'CREATE SEQUENCE ' . $sequenceName . ' START WITH ' . $start . ' INCREMENT BY 1 NOCACHE'; + $query .= ($start < 1 ? ' MINVALUE ' . $start : ''); + return $query; + } + /** + * drop existing sequence + * + * @param object $this->conn database object that is extended by this class + * @param string $seqName name of the sequence to be dropped + * @return string + */ + public function dropSequenceSql($seqName) + { + $sequenceName = $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($seqName), true); + return 'DROP SEQUENCE ' . $sequenceName; + } } \ No newline at end of file diff --git a/lib/Doctrine/Export/Sqlite.php b/lib/Doctrine/Export/Sqlite.php index 683827892..ca3a95b37 100644 --- a/lib/Doctrine/Export/Sqlite.php +++ b/lib/Doctrine/Export/Sqlite.php @@ -1,411 +1,411 @@ -. - */ -Doctrine::autoload('Doctrine_Export'); -/** - * Doctrine_Export_Sqlite - * - * @package Doctrine - * @subpackage Export - * @author Konsta Vesterinen - * @author Lukas Smith (PEAR MDB2 library) - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.phpdoctrine.com - * @since 1.0 - * @version $Revision$ - */ -class Doctrine_Export_Sqlite extends Doctrine_Export -{ - /** - * drop an existing database - * - * @param string $name name of the database that should be dropped - * @throws Doctrine_Export_Exception if the database file does not exist - * @throws Doctrine_Export_Exception if something failed during the removal of the database file - * @return void - */ - public function dropDatabase($name) - { - $databaseFile = $this->conn->getDatabaseFile($name); - if ( ! @file_exists($databaseFile)) { - throw new Doctrine_Export_Exception('database does not exist'); - } - $result = @unlink($databaseFile); - if ( ! $result) { - throw new Doctrine_Export_Exception('could not remove the database file'); - } - } - - /** - * Get the stucture of a field into an array - * - * @param string $table name of the table on which the index is to be created - * @param string $name name of the index to be created - * @param array $definition associative array that defines properties of the index to be created. - * Currently, only one property named FIELDS is supported. This property - * is also an associative with the names of the index fields as array - * indexes. Each entry of this array is set to another type of associative - * array that specifies properties of the index that are specific to - * each field. - * - * Currently, only the sorting property is supported. It should be used - * to define the sorting direction of the index. It may be set to either - * ascending or descending. - * - * Not all DBMS support index sorting direction configuration. The DBMS - * drivers of those that do not support it ignore this property. Use the - * function support() to determine whether the DBMS driver can manage indexes. - - * Example - * array( - * 'fields' => array( - * 'user_name' => array( - * 'sorting' => 'ascending' - * ), - * 'last_login' => array() - * ) - * ) - * @throws PDOException - * @return void - */ - public function createIndexSql($table, $name, array $definition) - { - $name = $this->conn->formatter->getIndexName($name); - $name = $this->conn->quoteIdentifier($name); - $query = 'CREATE INDEX ' . $name . ' ON ' . $table; - $query .= ' (' . $this->getIndexFieldDeclarationList($definition['fields']) . ')'; - - return $query; - } - /** - * getIndexFieldDeclarationList - * Obtain DBMS specific SQL code portion needed to set an index - * declaration to be used in statements like CREATE TABLE. - * - * @return string - */ - public function getIndexFieldDeclarationList(array $fields) - { - $declFields = array(); - - foreach ($fields as $fieldName => $field) { - $fieldString = $this->conn->quoteIdentifier($fieldName); - - if (is_array($field)) { - if (isset($field['sorting'])) { - $sort = strtoupper($field['sorting']); - switch ($sort) { - case 'ASC': - case 'DESC': - $fieldString .= ' ' . $sort; - break; - default: - throw new Doctrine_Export_Exception('Unknown index sorting option given.'); - } - } - } else { - $fieldString = $this->conn->quoteIdentifier($field); - } - $declFields[] = $fieldString; - } - return implode(', ', $declFields); - } - /** - * create a new table - * - * @param string $name Name of the database that should be created - * @param array $fields Associative array that contains the definition of each field of the new table - * The indexes of the array entries are the names of the fields of the table an - * the array entry values are associative arrays like those that are meant to be - * passed with the field definitions to get[Type]Declaration() functions. - * array( - * 'id' => array( - * 'type' => 'integer', - * 'unsigned' => 1 - * 'notnull' => 1 - * 'default' => 0 - * ), - * 'name' => array( - * 'type' => 'text', - * 'length' => 12 - * ), - * 'password' => array( - * 'type' => 'text', - * 'length' => 12 - * ) - * ); - * @param array $options An associative array of table options: - * - * @return void - */ - public function createTableSql($name, array $fields, array $options = array()) - { - if ( ! $name) { - throw new Doctrine_Export_Exception('no valid table name specified'); - } - - if (empty($fields)) { - throw new Doctrine_Export_Exception('no fields specified for table '.$name); - } - $queryFields = $this->getFieldDeclarationList($fields); - - $autoinc = false; - foreach($fields as $field) { - if (isset($field['autoincrement']) && $field['autoincrement'] || - (isset($field['autoinc']) && $field['autoinc'])) { - $autoinc = true; - break; - } - } - - if ( ! $autoinc && isset($options['primary']) && ! empty($options['primary'])) { - $keyColumns = array_values($options['primary']); - $keyColumns = array_map(array($this->conn, 'quoteIdentifier'), $keyColumns); - $queryFields.= ', PRIMARY KEY('.implode(', ', $keyColumns).')'; - } - - $name = $this->conn->quoteIdentifier($name, true); - $sql = 'CREATE TABLE ' . $name . ' (' . $queryFields; - - if ($check = $this->getCheckDeclaration($fields)) { - $sql .= ', ' . $check; - } - - if (isset($options['checks']) && $check = $this->getCheckDeclaration($options['checks'])) { - $sql .= ', ' . $check; - } - - $sql .= ')'; - - $query[] = $sql; - - if (isset($options['indexes']) && ! empty($options['indexes'])) { - foreach ($options['indexes'] as $index => $definition) { - $query[] = $this->createIndexSql($name, $index, $definition); - } - } - return $query; - - - /** - try { - - if ( ! empty($fk)) { - $this->conn->beginTransaction(); - } - - $ret = $this->conn->exec($query); - - if ( ! empty($fk)) { - foreach ($fk as $definition) { - - $query = 'CREATE TRIGGER doctrine_' . $name . '_cscd_delete ' - . 'AFTER DELETE ON ' . $name . ' FOR EACH ROW ' - . 'BEGIN ' - . 'DELETE FROM ' . $definition['foreignTable'] . ' WHERE '; - - $local = (array) $definition['local']; - foreach((array) $definition['foreign'] as $k => $field) { - $query .= $field . ' = old.' . $local[$k] . ';'; - } - - $query .= 'END;'; - - $this->conn->exec($query); - } - - $this->conn->commit(); - } - - - } catch(Doctrine_Exception $e) { - - $this->conn->rollback(); - - throw $e; - } - */ - } - /** - * getAdvancedForeignKeyOptions - * Return the FOREIGN KEY query section dealing with non-standard options - * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... - * - * @param array $definition foreign key definition - * @return string - * @access protected - */ - public function getAdvancedForeignKeyOptions(array $definition) - { - $query = ''; - if (isset($definition['match'])) { - $query .= ' MATCH ' . $definition['match']; - } - if (isset($definition['onUpdate'])) { - $query .= ' ON UPDATE ' . $definition['onUpdate']; - } - if (isset($definition['onDelete'])) { - $query .= ' ON DELETE ' . $definition['onDelete']; - } - if (isset($definition['deferrable'])) { - $query .= ' DEFERRABLE'; - } else { - $query .= ' NOT DEFERRABLE'; - } - if (isset($definition['feferred'])) { - $query .= ' INITIALLY DEFERRED'; - } else { - $query .= ' INITIALLY IMMEDIATE'; - } - return $query; - } - /** - * create sequence - * - * @param string $seqName name of the sequence to be created - * @param string $start start value of the sequence; default is 1 - * @param array $options An associative array of table options: - * array( - * 'comment' => 'Foo', - * 'charset' => 'utf8', - * 'collate' => 'utf8_unicode_ci', - * ); - * @return boolean - */ - public function createSequence($seqName, $start = 1, array $options = array()) - { - $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($seqName), true); - $seqcolName = $this->conn->quoteIdentifier($this->conn->getAttribute(Doctrine::ATTR_SEQCOL_NAME), true); - $query = 'CREATE TABLE ' . $sequenceName . ' (' . $seqcolName . ' INTEGER PRIMARY KEY DEFAULT 0 NOT NULL)'; - - $this->conn->exec($query); - - if ($start == 1) { - return true; - } - - try { - $this->conn->exec('INSERT INTO ' . $sequenceName . ' (' . $seqcolName . ') VALUES (' . ($start-1) . ')'); - return true; - } catch(Doctrine_Connection_Exception $e) { - // Handle error - - try { - $result = $db->exec('DROP TABLE ' . $sequenceName); - } catch(Doctrine_Connection_Exception $e) { - throw new Doctrine_Export_Exception('could not drop inconsistent sequence table'); - } - } - throw new Doctrine_Export_Exception('could not create sequence table'); - } - /** - * drop existing sequence - * - * @param string $sequenceName name of the sequence to be dropped - * @return string - */ - public function dropSequenceSql($sequenceName) - { - $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($sequenceName), true); - - return 'DROP TABLE ' . $sequenceName; - } - - public function alterTableSql($name, array $changes, $check = false) - { - if ( ! $name) { - throw new Doctrine_Export_Exception('no valid table name specified'); - } - foreach ($changes as $changeName => $change) { - switch ($changeName) { - case 'add': - case 'change': - case 'rename': - case 'name': - break; - default: - throw new Doctrine_Export_Exception('change type "' . $changeName . '" not yet supported'); - } - } - - if ($check) { - return true; - } - - $query = ''; - if ( ! empty($changes['name'])) { - $change_name = $this->conn->quoteIdentifier($changes['name']); - $query .= 'RENAME TO ' . $change_name; - } - - if ( ! empty($changes['add']) && is_array($changes['add'])) { - foreach ($changes['add'] as $fieldName => $field) { - if ($query) { - $query.= ', '; - } - $query.= 'ADD ' . $this->getDeclaration($fieldName, $field); - } - } - - $rename = array(); - if ( ! empty($changes['rename']) && is_array($changes['rename'])) { - foreach ($changes['rename'] as $fieldName => $field) { - $rename[$field['name']] = $fieldName; - } - } - - if ( ! empty($changes['change']) && is_array($changes['change'])) { - foreach ($changes['change'] as $fieldName => $field) { - if ($query) { - $query.= ', '; - } - if (isset($rename[$fieldName])) { - $oldFieldName = $rename[$fieldName]; - unset($rename[$fieldName]); - } else { - $oldFieldName = $fieldName; - } - $oldFieldName = $this->conn->quoteIdentifier($oldFieldName, true); - $query .= 'CHANGE ' . $oldFieldName . ' ' - . $this->getDeclaration($fieldName, $field['definition']); - } - } - - if ( ! empty($rename) && is_array($rename)) { - foreach ($rename as $renameName => $renamedField) { - if ($query) { - $query.= ', '; - } - $field = $changes['rename'][$renamedField]; - $renamedField = $this->conn->quoteIdentifier($renamedField, true); - $query .= 'CHANGE ' . $renamedField . ' ' - . $this->getDeclaration($field['name'], $field['definition']); - } - } - - if ( ! $query) { - return false; - } - - $name = $this->conn->quoteIdentifier($name, true); - - return 'ALTER TABLE ' . $name . ' ' . $query; - } +. + */ +Doctrine::autoload('Doctrine_Export'); +/** + * Doctrine_Export_Sqlite + * + * @package Doctrine + * @subpackage Export + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.phpdoctrine.com + * @since 1.0 + * @version $Revision$ + */ +class Doctrine_Export_Sqlite extends Doctrine_Export +{ + /** + * drop an existing database + * + * @param string $name name of the database that should be dropped + * @throws Doctrine_Export_Exception if the database file does not exist + * @throws Doctrine_Export_Exception if something failed during the removal of the database file + * @return void + */ + public function dropDatabase($name) + { + $databaseFile = $this->conn->getDatabaseFile($name); + if ( ! @file_exists($databaseFile)) { + throw new Doctrine_Export_Exception('database does not exist'); + } + $result = @unlink($databaseFile); + if ( ! $result) { + throw new Doctrine_Export_Exception('could not remove the database file'); + } + } + + /** + * Get the stucture of a field into an array + * + * @param string $table name of the table on which the index is to be created + * @param string $name name of the index to be created + * @param array $definition associative array that defines properties of the index to be created. + * Currently, only one property named FIELDS is supported. This property + * is also an associative with the names of the index fields as array + * indexes. Each entry of this array is set to another type of associative + * array that specifies properties of the index that are specific to + * each field. + * + * Currently, only the sorting property is supported. It should be used + * to define the sorting direction of the index. It may be set to either + * ascending or descending. + * + * Not all DBMS support index sorting direction configuration. The DBMS + * drivers of those that do not support it ignore this property. Use the + * function support() to determine whether the DBMS driver can manage indexes. + + * Example + * array( + * 'fields' => array( + * 'user_name' => array( + * 'sorting' => 'ascending' + * ), + * 'last_login' => array() + * ) + * ) + * @throws PDOException + * @return void + */ + public function createIndexSql($table, $name, array $definition) + { + $name = $this->conn->formatter->getIndexName($name); + $name = $this->conn->quoteIdentifier($name); + $query = 'CREATE INDEX ' . $name . ' ON ' . $table; + $query .= ' (' . $this->getIndexFieldDeclarationList($definition['fields']) . ')'; + + return $query; + } + /** + * getIndexFieldDeclarationList + * Obtain DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @return string + */ + public function getIndexFieldDeclarationList(array $fields) + { + $declFields = array(); + + foreach ($fields as $fieldName => $field) { + $fieldString = $this->conn->quoteIdentifier($fieldName); + + if (is_array($field)) { + if (isset($field['sorting'])) { + $sort = strtoupper($field['sorting']); + switch ($sort) { + case 'ASC': + case 'DESC': + $fieldString .= ' ' . $sort; + break; + default: + throw new Doctrine_Export_Exception('Unknown index sorting option given.'); + } + } + } else { + $fieldString = $this->conn->quoteIdentifier($field); + } + $declFields[] = $fieldString; + } + return implode(', ', $declFields); + } + /** + * create a new table + * + * @param string $name Name of the database that should be created + * @param array $fields Associative array that contains the definition of each field of the new table + * The indexes of the array entries are the names of the fields of the table an + * the array entry values are associative arrays like those that are meant to be + * passed with the field definitions to get[Type]Declaration() functions. + * array( + * 'id' => array( + * 'type' => 'integer', + * 'unsigned' => 1 + * 'notnull' => 1 + * 'default' => 0 + * ), + * 'name' => array( + * 'type' => 'text', + * 'length' => 12 + * ), + * 'password' => array( + * 'type' => 'text', + * 'length' => 12 + * ) + * ); + * @param array $options An associative array of table options: + * + * @return void + */ + public function createTableSql($name, array $fields, array $options = array()) + { + if ( ! $name) { + throw new Doctrine_Export_Exception('no valid table name specified'); + } + + if (empty($fields)) { + throw new Doctrine_Export_Exception('no fields specified for table '.$name); + } + $queryFields = $this->getFieldDeclarationList($fields); + + $autoinc = false; + foreach($fields as $field) { + if (isset($field['autoincrement']) && $field['autoincrement'] || + (isset($field['autoinc']) && $field['autoinc'])) { + $autoinc = true; + break; + } + } + + if ( ! $autoinc && isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_values($options['primary']); + $keyColumns = array_map(array($this->conn, 'quoteIdentifier'), $keyColumns); + $queryFields.= ', PRIMARY KEY('.implode(', ', $keyColumns).')'; + } + + $name = $this->conn->quoteIdentifier($name, true); + $sql = 'CREATE TABLE ' . $name . ' (' . $queryFields; + + if ($check = $this->getCheckDeclaration($fields)) { + $sql .= ', ' . $check; + } + + if (isset($options['checks']) && $check = $this->getCheckDeclaration($options['checks'])) { + $sql .= ', ' . $check; + } + + $sql .= ')'; + + $query[] = $sql; + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index => $definition) { + $query[] = $this->createIndexSql($name, $index, $definition); + } + } + return $query; + + + /** + try { + + if ( ! empty($fk)) { + $this->conn->beginTransaction(); + } + + $ret = $this->conn->exec($query); + + if ( ! empty($fk)) { + foreach ($fk as $definition) { + + $query = 'CREATE TRIGGER doctrine_' . $name . '_cscd_delete ' + . 'AFTER DELETE ON ' . $name . ' FOR EACH ROW ' + . 'BEGIN ' + . 'DELETE FROM ' . $definition['foreignTable'] . ' WHERE '; + + $local = (array) $definition['local']; + foreach((array) $definition['foreign'] as $k => $field) { + $query .= $field . ' = old.' . $local[$k] . ';'; + } + + $query .= 'END;'; + + $this->conn->exec($query); + } + + $this->conn->commit(); + } + + + } catch(Doctrine_Exception $e) { + + $this->conn->rollback(); + + throw $e; + } + */ + } + /** + * getAdvancedForeignKeyOptions + * Return the FOREIGN KEY query section dealing with non-standard options + * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... + * + * @param array $definition foreign key definition + * @return string + * @access protected + */ + public function getAdvancedForeignKeyOptions(array $definition) + { + $query = ''; + if (isset($definition['match'])) { + $query .= ' MATCH ' . $definition['match']; + } + if (isset($definition['onUpdate'])) { + $query .= ' ON UPDATE ' . $definition['onUpdate']; + } + if (isset($definition['onDelete'])) { + $query .= ' ON DELETE ' . $definition['onDelete']; + } + if (isset($definition['deferrable'])) { + $query .= ' DEFERRABLE'; + } else { + $query .= ' NOT DEFERRABLE'; + } + if (isset($definition['feferred'])) { + $query .= ' INITIALLY DEFERRED'; + } else { + $query .= ' INITIALLY IMMEDIATE'; + } + return $query; + } + /** + * create sequence + * + * @param string $seqName name of the sequence to be created + * @param string $start start value of the sequence; default is 1 + * @param array $options An associative array of table options: + * array( + * 'comment' => 'Foo', + * 'charset' => 'utf8', + * 'collate' => 'utf8_unicode_ci', + * ); + * @return boolean + */ + public function createSequence($seqName, $start = 1, array $options = array()) + { + $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($seqName), true); + $seqcolName = $this->conn->quoteIdentifier($this->conn->getAttribute(Doctrine::ATTR_SEQCOL_NAME), true); + $query = 'CREATE TABLE ' . $sequenceName . ' (' . $seqcolName . ' INTEGER PRIMARY KEY DEFAULT 0 NOT NULL)'; + + $this->conn->exec($query); + + if ($start == 1) { + return true; + } + + try { + $this->conn->exec('INSERT INTO ' . $sequenceName . ' (' . $seqcolName . ') VALUES (' . ($start-1) . ')'); + return true; + } catch(Doctrine_Connection_Exception $e) { + // Handle error + + try { + $result = $db->exec('DROP TABLE ' . $sequenceName); + } catch(Doctrine_Connection_Exception $e) { + throw new Doctrine_Export_Exception('could not drop inconsistent sequence table'); + } + } + throw new Doctrine_Export_Exception('could not create sequence table'); + } + /** + * drop existing sequence + * + * @param string $sequenceName name of the sequence to be dropped + * @return string + */ + public function dropSequenceSql($sequenceName) + { + $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($sequenceName), true); + + return 'DROP TABLE ' . $sequenceName; + } + + public function alterTableSql($name, array $changes, $check = false) + { + if ( ! $name) { + throw new Doctrine_Export_Exception('no valid table name specified'); + } + foreach ($changes as $changeName => $change) { + switch ($changeName) { + case 'add': + case 'change': + case 'rename': + case 'name': + break; + default: + throw new Doctrine_Export_Exception('change type "' . $changeName . '" not yet supported'); + } + } + + if ($check) { + return true; + } + + $query = ''; + if ( ! empty($changes['name'])) { + $change_name = $this->conn->quoteIdentifier($changes['name']); + $query .= 'RENAME TO ' . $change_name; + } + + if ( ! empty($changes['add']) && is_array($changes['add'])) { + foreach ($changes['add'] as $fieldName => $field) { + if ($query) { + $query.= ', '; + } + $query.= 'ADD ' . $this->getDeclaration($fieldName, $field); + } + } + + $rename = array(); + if ( ! empty($changes['rename']) && is_array($changes['rename'])) { + foreach ($changes['rename'] as $fieldName => $field) { + $rename[$field['name']] = $fieldName; + } + } + + if ( ! empty($changes['change']) && is_array($changes['change'])) { + foreach ($changes['change'] as $fieldName => $field) { + if ($query) { + $query.= ', '; + } + if (isset($rename[$fieldName])) { + $oldFieldName = $rename[$fieldName]; + unset($rename[$fieldName]); + } else { + $oldFieldName = $fieldName; + } + $oldFieldName = $this->conn->quoteIdentifier($oldFieldName, true); + $query .= 'CHANGE ' . $oldFieldName . ' ' + . $this->getDeclaration($fieldName, $field['definition']); + } + } + + if ( ! empty($rename) && is_array($rename)) { + foreach ($rename as $renameName => $renamedField) { + if ($query) { + $query.= ', '; + } + $field = $changes['rename'][$renamedField]; + $renamedField = $this->conn->quoteIdentifier($renamedField, true); + $query .= 'CHANGE ' . $renamedField . ' ' + . $this->getDeclaration($field['name'], $field['definition']); + } + } + + if ( ! $query) { + return false; + } + + $name = $this->conn->quoteIdentifier($name, true); + + return 'ALTER TABLE ' . $name . ' ' . $query; + } } \ No newline at end of file