diff --git a/lib/Doctrine/ClassMetadata/Factory.php b/lib/Doctrine/ClassMetadata/Factory.php index 9e247f93b..6a21f310e 100644 --- a/lib/Doctrine/ClassMetadata/Factory.php +++ b/lib/Doctrine/ClassMetadata/Factory.php @@ -27,8 +27,6 @@ * * @author Konsta Vesterinen * @author Roman Borschel - * @package Doctrine - * @subpackage ClassMetadata * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @version $Revision$ * @link www.phpdoctrine.org @@ -188,116 +186,11 @@ class Doctrine_ClassMetadata_Factory $class->setTableName(Doctrine::tableize($class->getClassName())); } - // complete identifier mapping - $this->_initIdentifier($class); + $class->completeIdentifierMapping(); return $class; } - /** - * Initializes the class identifier(s)/primary key(s). - * - * @param Doctrine_Metadata The metadata container of the class in question. - */ - protected function _initIdentifier(Doctrine_ClassMetadata $class) - { - /*switch (count($class->getIdentifier())) { - case 0: // No identifier in the class mapping yet - - // If its a subclass, inherit the identifier from the parent. - if ($class->getInheritanceType() == Doctrine::INHERITANCE_TYPE_JOINED && - count($class->getParentClasses()) > 0) { - $parents = $class->getParentClasses(); - $root = end($parents); - $rootClass = $class->getConnection()->getMetadata($root); - $class->setIdentifier($rootClass->getIdentifier()); - - if ($class->getIdentifierType() !== Doctrine::IDENTIFIER_AUTOINC) { - $class->setIdentifierType($rootClass->getIdentifierType()); - } else { - $class->setIdentifierType(Doctrine::IDENTIFIER_NATURAL); - } - - // add all inherited primary keys - foreach ($class->getIdentifier() as $id) { - $definition = $rootClass->getDefinitionOf($id); - - // inherited primary keys shouldn't contain autoinc - // and sequence definitions - unset($definition['autoincrement']); - unset($definition['sequence']); - - // add the inherited primary key column - $fullName = $rootClass->getColumnName($id) . ' as ' . $id; - $class->setColumn($fullName, $definition['type'], $definition['length'], - $definition, true); - } - } else { - throw Doctrine_MappingException::identifierRequired($class->getClassName()); - } - break; - case 1: // A single identifier is in the mapping - foreach ($class->getIdentifier() as $pk) { - $columnName = $class->getColumnName($pk); - $thisColumns = $class->getFieldMappings(); - $e = $thisColumns[$columnName]; - - $found = false; - - foreach ($e as $option => $value) { - if ($found) { - break; - } - - $e2 = explode(':', $option); - - switch (strtolower($e2[0])) { - case 'autoincrement': - case 'autoinc': - $class->setIdentifierType(Doctrine::IDENTIFIER_AUTOINC); - $found = true; - break; - case 'seq': - case 'sequence': - $class->setIdentifierType(Doctrine::IDENTIFIER_SEQUENCE); - $found = true; - - if ($value) { - $class->setTableOption('sequenceName', $value); - } else { - if (($sequence = $class->getAttribute(Doctrine::ATTR_DEFAULT_SEQUENCE)) !== null) { - $class->setTableOption('sequenceName', $sequence); - } else { - $class->setTableOption('sequenceName', $class->getConnection() - ->getSequenceName($class->getTableName())); - } - } - break; - } - } - $identifierType = $class->getIdentifierType(); - if ( ! isset($identifierType)) { - $class->setIdentifierType(Doctrine::IDENTIFIER_NATURAL); - } - } - - $class->setIdentifier(array($pk)); - - break; - default: // Multiple identifiers are in the mapping so its a composite id - $class->setIdentifierType(Doctrine::IDENTIFIER_COMPOSITE); - }*/ - - // If the chosen generator type is "auto", then pick the one appropriate for - // the database. - - // FIXME: This is very ugly here. Such switch()es on the database driver - // are unnecessary as we can easily replace them with polymorphic calls on - // the connection (or another) object. We just need to decide where to put - // the id generation types. - $class->completeIdentifierMapping(); - } - } diff --git a/lib/Doctrine/Connection.php b/lib/Doctrine/Connection.php index 7d9281911..b80910b55 100644 --- a/lib/Doctrine/Connection.php +++ b/lib/Doctrine/Connection.php @@ -166,6 +166,13 @@ abstract class Doctrine_Connection * @var Doctrine::DBAL::Sequencing::SequenceManager */ protected $_sequenceManager; + + /** + * The schema manager. + * + * @var Doctrine::DBAL::Schema::SchemaManager + */ + protected $_schemaManager; /** * Constructor. @@ -1168,4 +1175,19 @@ abstract class Doctrine_Connection } return $this->_sequenceManager; } + + /** + * Gets the SchemaManager that can be used to inspect or change the + * database schema through the connection. + * + * @return Doctrine::DBAL::Schema::SchemaManager + */ + public function getSchemaManager() + { + if ( ! $this->_schemaManager) { + $class = "Doctrine_Schema_" . $this->_driverName . "SchemaManager"; + $this->_schemaManager = new $class($this); + } + return $this->_schemaManager; + } } diff --git a/lib/Doctrine/DatabasePlatform.php b/lib/Doctrine/DatabasePlatform.php index babf50705..642173765 100644 --- a/lib/Doctrine/DatabasePlatform.php +++ b/lib/Doctrine/DatabasePlatform.php @@ -5,6 +5,7 @@ /** * Base class for all DatabasePlatforms. The DatabasePlatforms are the central * point of abstraction of platform-specific behaviors, features and SQL dialects. + * They are a passive source of information. * * @since 2.0 * @author Roman Borschel diff --git a/lib/Doctrine/EntityManager.php b/lib/Doctrine/EntityManager.php index ac803fdbc..98cd71511 100644 --- a/lib/Doctrine/EntityManager.php +++ b/lib/Doctrine/EntityManager.php @@ -298,8 +298,6 @@ class Doctrine_EntityManager /** * Flushes all changes to objects that have been queued up to now to the database. - * - * @todo package:orm */ public function flush() { diff --git a/lib/Doctrine/Export.php b/lib/Doctrine/Export.php index 03c487a5f..6f4eeddfc 100644 --- a/lib/Doctrine/Export.php +++ b/lib/Doctrine/Export.php @@ -36,1000 +36,6 @@ */ class Doctrine_Export extends Doctrine_Connection_Module { - protected $valid_default_values = array( - 'text' => '', - 'boolean' => true, - 'integer' => 0, - 'decimal' => 0.0, - 'float' => 0.0, - 'timestamp' => '1970-01-01 00:00:00', - 'time' => '00:00:00', - 'date' => '1970-01-01', - 'clob' => '', - 'blob' => '', - 'string' => '', - ); - - /** - * drop an existing database - * (this method is implemented by the drivers) - * - * @param string $name name of the database that should be dropped - * @return void - */ - public function dropDatabase($database) - { - $this->conn->execute($this->dropDatabaseSql($database)); - } - - /** - * drop an existing database - * (this method is implemented by the drivers) - * - * @param string $name name of the database that should be dropped - * @return void - */ - public function dropDatabaseSql($database) - { - throw new Doctrine_Export_Exception('Drop database not supported by this driver.'); - } - - /** - * dropTableSql - * drop an existing table - * - * @param string $table name of table that should be dropped from the database - * @return string - */ - public function dropTableSql($table) - { - return 'DROP TABLE ' . $this->conn->quoteIdentifier($table); - } - - /** - * dropTable - * drop an existing table - * - * @param string $table name of table that should be dropped from the database - * @return void - */ - public function dropTable($table) - { - $this->conn->execute($this->dropTableSql($table)); - } - - /** - * 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 dropIndex($table, $name) - { - return $this->conn->exec($this->dropIndexSql($table, $name)); - } - - /** - * dropIndexSql - * - * @param string $table name of table that should be used in method - * @param string $name name of the index to be dropped - * @return string SQL that is used for dropping an index - */ - public function dropIndexSql($table, $name) - { - $name = $this->conn->quoteIdentifier($this->conn->formatter->getIndexName($name)); - - return 'DROP INDEX ' . $name; - } - - /** - * drop existing constraint - * - * @param string $table name of table that should be used in method - * @param string $name name of the constraint to be dropped - * @param string $primary hint if the constraint is primary - * @return void - */ - public function dropConstraint($table, $name, $primary = false) - { - $table = $this->conn->quoteIdentifier($table); - $name = $this->conn->quoteIdentifier($name); - - return $this->conn->exec('ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $name); - } - - /** - * drop existing foreign key - * - * @param string $table name of table that should be used in method - * @param string $name name of the foreign key to be dropped - * @return void - */ - public function dropForeignKey($table, $name) - { - return $this->dropConstraint($table, $name); - } - - /** - * dropSequenceSql - * drop existing sequence - * (this method is implemented by the drivers) - * - * @throws Doctrine_Connection_Exception if something fails at database level - * @param string $sequenceName name of the sequence to be dropped - * @return void - */ - public function dropSequence($sequenceName) - { - $this->conn->exec($this->dropSequenceSql($sequenceName)); - } - - /** - * dropSequenceSql - * drop existing sequence - * - * @throws Doctrine_Connection_Exception if something fails at database level - * @param string $sequenceName name of the sequence to be dropped - * @return void - */ - public function dropSequenceSql($sequenceName) - { - throw new Doctrine_Export_Exception('Drop sequence not supported by this driver.'); - } - - /** - * create a new database - * (this method is implemented by the drivers) - * - * @param string $name name of the database that should be created - * @return void - */ - public function createDatabase($database) - { - $this->conn->execute($this->createDatabaseSql($database)); - } - - /** - * create a new database - * (this method is implemented by the drivers) - * - * @param string $name name of the database that should be created - * @return string - */ - public function createDatabaseSql($database) - { - throw new Doctrine_Export_Exception('Create database not supported by this driver.'); - } - - /** - * 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 string - */ - 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); - - - if (isset($options['primary']) && ! empty($options['primary'])) { - $queryFields .= ', PRIMARY KEY(' . implode(', ', array_values($options['primary'])) . ')'; - } - - if (isset($options['indexes']) && ! empty($options['indexes'])) { - foreach($options['indexes'] as $index => $definition) { - $queryFields .= ', ' . $this->getIndexDeclaration($index, $definition); - } - } - - $query = 'CREATE TABLE ' . $this->conn->quoteIdentifier($name, true) . ' (' . $queryFields; - - $check = $this->getCheckDeclaration($fields); - - if ( ! empty($check)) { - $query .= ', ' . $check; - } - - $query .= ')'; - - - - $sql[] = $query; - - if (isset($options['foreignKeys'])) { - - foreach ((array) $options['foreignKeys'] as $k => $definition) { - if (is_array($definition)) { - $sql[] = $this->createForeignKeySql($name, $definition); - } - } - } - return $sql; - } - - /** - * 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 - * @param array $options An associative array of table options: - * @see Doctrine_Export::createTableSql() - * - * @return void - */ - public function createTable($name, array $fields, array $options = array()) - { - $sql = (array) $this->createTableSql($name, $fields, $options); - - foreach ($sql as $query) { - $this->conn->execute($query); - } - } - - /** - * create sequence - * - * @throws Doctrine_Connection_Exception if something fails at database level - * @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($seqName, $start = 1, array $options = array()) - { - return $this->conn->execute($this->createSequenceSql($seqName, $start = 1, $options)); - } - - /** - * return RDBMS specific create sequence statement - * (this method is implemented by the drivers) - * - * @throws Doctrine_Connection_Exception if something fails at database level - * @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()) - { - throw new Doctrine_Export_Exception('Create sequence not supported by this driver.'); - } - - /** - * 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) - { - $sql = $this->createConstraintSql($table, $name, $definition); - - return $this->conn->exec($sql); - } - - /** - * 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 createConstraintSql($table, $name, $definition) - { - $table = $this->conn->quoteIdentifier($table); - $name = $this->conn->quoteIdentifier($this->conn->formatter->getIndexName($name)); - $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $name; - - if (isset($definition['primary']) && $definition['primary']) { - $query .= ' PRIMARY KEY'; - } elseif (isset($definition['unique']) && $definition['unique']) { - $query .= ' UNIQUE'; - } - - $fields = array(); - foreach (array_keys($definition['fields']) as $field) { - $fields[] = $this->conn->quoteIdentifier($field, true); - } - $query .= ' ('. implode(', ', $fields) . ')'; - - return $query; - } - - /** - * 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 supports() 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 createIndex($table, $name, array $definition) - { - return $this->conn->execute($this->createIndexSql($table, $name, $definition)); - } - - /** - * 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. - * @see Doctrine_Export::createIndex() - * @return string - */ - public function createIndexSql($table, $name, array $definition) - { - $table = $this->conn->quoteIdentifier($table); - $name = $this->conn->quoteIdentifier($name); - $type = ''; - - if (isset($definition['type'])) { - switch (strtolower($definition['type'])) { - 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; - - $fields = array(); - foreach ($definition['fields'] as $field) { - $fields[] = $this->conn->quoteIdentifier($field); - } - $query .= ' (' . implode(', ', $fields) . ')'; - - return $query; - } - /** - * createForeignKeySql - * - * @param string $table name of the table on which the foreign key is to be created - * @param array $definition associative array that defines properties of the foreign key to be created. - * @return string - */ - public function createForeignKeySql($table, array $definition) - { - $table = $this->conn->quoteIdentifier($table); - - $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclaration($definition); - - return $query; - } - - /** - * createForeignKey - * - * @param string $table name of the table on which the foreign key is to be created - * @param array $definition associative array that defines properties of the foreign key to be created. - * @return string - */ - public function createForeignKey($table, array $definition) - { - $sql = $this->createForeignKeySql($table, $definition); - - return $this->conn->execute($sql); - } - - /** - * alter an existing table - * (this method is implemented by the drivers) - * - * @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) - { - $sql = $this->alterTableSql($name, $changes, $check); - - if (is_string($sql) && $sql) { - $this->conn->execute($sql); - } - } - - /** - * generates the sql for altering an existing table - * (this method is implemented by the drivers) - * - * @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 * - * @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. - * @see Doctrine_Export::alterTable() - * @return string - */ - public function alterTableSql($name, array $changes, $check = false) - { - throw new Doctrine_Export_Exception('Alter table not supported by this driver.'); - } - - /** - * Get declaration of a number of field in bulk - * - * @param array $fields a multidimensional associative array. - * The first dimension determines the field name, while the second - * dimension is keyed with the name of the properties - * of the field being declared as array indexes. Currently, the types - * of supported field properties are as follows: - * - * length - * Integer value that determines the maximum length of the text - * field. If this argument is missing the field should be - * declared to have the longest length allowed by the DBMS. - * - * default - * Text value to be used as default for this field. - * - * notnull - * Boolean flag that indicates whether this field is constrained - * to not be set to null. - * charset - * Text value with the default CHARACTER SET for this field. - * collation - * Text value with the default COLLATION for this field. - * unique - * unique constraint - * - * @return string - */ - public function getFieldDeclarationList(array $fields) - { - foreach ($fields as $fieldName => $field) { - $query = $this->getDeclaration($fieldName, $field); - - $queryFields[] = $query; - } - return implode(', ', $queryFields); - } - - /** - * Obtain DBMS specific SQL code portion needed to declare a generic type - * field to be used in statements like CREATE TABLE. - * - * @param string $name name the field to be declared. - * @param array $field associative array with the name of the properties - * of the field being declared as array indexes. Currently, the types - * of supported field properties are as follows: - * - * length - * Integer value that determines the maximum length of the text - * field. If this argument is missing the field should be - * declared to have the longest length allowed by the DBMS. - * - * default - * Text value to be used as default for this field. - * - * notnull - * Boolean flag that indicates whether this field is constrained - * to not be set to null. - * charset - * Text value with the default CHARACTER SET for this field. - * collation - * Text value with the default COLLATION for this field. - * unique - * unique constraint - * check - * column check constraint - * - * @return string DBMS specific SQL code portion that should be used to - * declare the specified field. - */ - public function getDeclaration($name, array $field) - { - $method = 'get' . $field['type'] . 'Declaration'; - - if (method_exists($this, $method)) { - return $this->$method($name, $field); - } - - $default = $this->getDefaultFieldDeclaration($field); - - $charset = (isset($field['charset']) && $field['charset']) ? - ' ' . $this->getCharsetFieldDeclaration($field['charset']) : ''; - - $collation = (isset($field['collation']) && $field['collation']) ? - ' ' . $this->getCollationFieldDeclaration($field['collation']) : ''; - - $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : ''; - - $unique = (isset($field['unique']) && $field['unique']) ? - ' ' . $this->getUniqueFieldDeclaration() : ''; - - $check = (isset($field['check']) && $field['check']) ? - ' ' . $field['check'] : ''; - - $dec = $this->conn->dataDict->getNativeDeclaration($field); - - return $this->conn->quoteIdentifier($name, true) . ' ' . $dec . $charset . $default . $notnull . $unique . $check . $collation; - } - - /** - * 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 = empty($field['notnull']) && !in_array($field['type'], array('clob', 'blob')) - ? ' DEFAULT NULL' : ''; - - if (isset($field['default'])) { - if ($field['default'] === '') { - $field['default'] = null; - if (! empty($field['notnull']) && array_key_exists($field['type'], $this->valid_default_values)) { - $field['default'] = $this->valid_default_values[$field['type']]; - } - - if ($field['default'] === '' - && ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_EMPTY_TO_NULL) - ) { - $field['default'] = ' '; - } - } - - if ($field['type'] === 'boolean') { - $field['default'] = $this->conn->convertBooleans($field['default']); - } - $default = ' DEFAULT ' . $this->conn->quote($field['default'], $field['type']); - } - return $default; - } - - /** - * Obtain DBMS specific SQL code portion needed to set a CHECK constraint - * declaration to be used in statements like CREATE TABLE. - * - * @param array $definition check definition - * @return string DBMS specific SQL code portion needed to set a CHECK constraint - */ - public function getCheckDeclaration(array $definition) - { - $constraints = array(); - foreach ($definition as $field => $def) { - if (is_string($def)) { - $constraints[] = 'CHECK (' . $def . ')'; - } else { - if (isset($def['min'])) { - $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')'; - } - - if (isset($def['max'])) { - $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')'; - } - } - } - - return implode(', ', $constraints); - } - - /** - * Obtain DBMS specific SQL code portion needed to set an index - * declaration to be used in statements like CREATE TABLE. - * - * @param string $name 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->quoteIdentifier($name); - $type = ''; - - if (isset($definition['type'])) { - if (strtolower($definition['type']) == 'unique') { - $type = strtoupper($definition['type']) . ' '; - } else { - throw new Doctrine_Export_Exception('Unknown index type ' . $definition['type']); - } - } - - if ( ! isset($definition['fields']) || ! is_array($definition['fields'])) { - throw new Doctrine_Export_Exception('No index columns given.'); - } - - $query = $type . 'INDEX ' . $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) - { - $ret = array(); - foreach ($fields as $field => $definition) { - if (is_array($definition)) { - $ret[] = $this->conn->quoteIdentifier($field); - } else { - $ret[] = $this->conn->quoteIdentifier($definition); - } - } - return implode(', ', $ret); - } - - /** - * A method to return the required SQL string that fits between CREATE ... TABLE - * to create the table as a temporary table. - * - * Should be overridden in driver classes to return the correct string for the - * specific database type. - * - * The default is to return the string "TEMPORARY" - this will result in a - * SQL error for any database that does not support temporary tables, or that - * requires a different SQL command from "CREATE 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 'TEMPORARY'; - } - - /** - * getForeignKeyDeclaration - * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint - * of a field declaration to be used in statements like CREATE TABLE. - * - * @param array $definition an associative array with the following structure: - * name optional constraint name - * - * local the local field(s) - * - * foreign the foreign reference field(s) - * - * foreignTable the name of the foreign table - * - * onDelete referential delete action - * - * onUpdate referential update action - * - * deferred deferred constraint checking - * - * The onDelete and onUpdate keys accept the following values: - * - * CASCADE: Delete or update the row from the parent table and automatically delete or - * update the matching rows in the child table. Both ON DELETE CASCADE and ON UPDATE CASCADE are supported. - * Between two tables, you should not define several ON UPDATE CASCADE clauses that act on the same column - * in the parent table or in the child table. - * - * SET NULL: Delete or update the row from the parent table and set the foreign key column or columns in the - * child table to NULL. This is valid only if the foreign key columns do not have the NOT NULL qualifier - * specified. Both ON DELETE SET NULL and ON UPDATE SET NULL clauses are supported. - * - * NO ACTION: In standard SQL, NO ACTION means no action in the sense that an attempt to delete or update a primary - * key value is not allowed to proceed if there is a related foreign key value in the referenced table. - * - * RESTRICT: Rejects the delete or update operation for the parent table. NO ACTION and RESTRICT are the same as - * omitting the ON DELETE or ON UPDATE clause. - * - * SET DEFAULT - * - * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint - * of a field declaration. - */ - public function getForeignKeyDeclaration(array $definition) - { - $sql = $this->getForeignKeyBaseDeclaration($definition); - $sql .= $this->getAdvancedForeignKeyOptions($definition); - - return $sql; - } - - /** - * 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 - */ - public function getAdvancedForeignKeyOptions(array $definition) - { - $query = ''; - if ( ! empty($definition['onUpdate'])) { - $query .= ' ON UPDATE ' . $this->getForeignKeyRefentialAction($definition['onUpdate']); - } - if ( ! empty($definition['onDelete'])) { - $query .= ' ON DELETE ' . $this->getForeignKeyRefentialAction($definition['onDelete']); - } - return $query; - } - - /** - * getForeignKeyReferentialAction - * - * returns given referential action in uppercase if valid, otherwise throws - * an exception - * - * @throws Doctrine_Exception_Exception if unknown referential action given - * @param string $action foreign key referential action - * @param string foreign key referential action in uppercase - */ - public function getForeignKeyReferentialAction($action) - { - $upper = strtoupper($action); - switch ($upper) { - case 'CASCADE': - case 'SET NULL': - case 'NO ACTION': - case 'RESTRICT': - case 'SET DEFAULT': - return $upper; - break; - default: - throw new Doctrine_Export_Exception('Unknown foreign key referential action \'' . $upper . '\' given.'); - } - } - - /** - * getForeignKeyBaseDeclaration - * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint - * of a field declaration to be used in statements like CREATE TABLE. - * - * @param array $definition - * @return string - */ - public function getForeignKeyBaseDeclaration(array $definition) - { - $sql = ''; - if (isset($definition['name'])) { - $sql .= ' CONSTRAINT ' . $this->conn->quoteIdentifier($definition['name']) . ' '; - } - $sql .= 'FOREIGN KEY ('; - - if ( ! isset($definition['local'])) { - throw new Doctrine_Export_Exception('Local reference field missing from definition.'); - } - if ( ! isset($definition['foreign'])) { - throw new Doctrine_Export_Exception('Foreign reference field missing from definition.'); - } - if ( ! isset($definition['foreignTable'])) { - throw new Doctrine_Export_Exception('Foreign reference table missing from definition.'); - } - - if ( ! is_array($definition['local'])) { - $definition['local'] = array($definition['local']); - } - if ( ! is_array($definition['foreign'])) { - $definition['foreign'] = array($definition['foreign']); - } - - $sql .= implode(', ', array_map(array($this->conn, 'quoteIdentifier'), $definition['local'])) - . ') REFERENCES ' - . $this->conn->quoteIdentifier($definition['foreignTable']) . '(' - . implode(', ', array_map(array($this->conn, 'quoteIdentifier'), $definition['foreign'])) . ')'; - - return $sql; - } - - /** - * Obtain DBMS specific SQL code portion needed to set the UNIQUE constraint - * of a field declaration to be used in statements like CREATE TABLE. - * - * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint - * of a field declaration. - */ - public function getUniqueFieldDeclaration() - { - return 'UNIQUE'; - } - - /** - * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET - * of a field declaration to be used in statements like CREATE TABLE. - * - * @param string $charset name of the charset - * @return string DBMS specific SQL code portion needed to set the CHARACTER SET - * of a field declaration. - */ - public function getCharsetFieldDeclaration($charset) - { - return ''; - } - - /** - * Obtain DBMS specific SQL code portion needed to set the COLLATION - * of a field declaration to be used in statements like CREATE TABLE. - * - * @param string $collation name of the collation - * @return string DBMS specific SQL code portion needed to set the COLLATION - * of a field declaration. - */ - public function getCollationFieldDeclaration($collation) - { - return ''; - } /** * exportSchema diff --git a/lib/Doctrine/Export/Firebird.php b/lib/Doctrine/Export/Firebird.php index 3c9015a59..c6fd12c1a 100644 --- a/lib/Doctrine/Export/Firebird.php +++ b/lib/Doctrine/Export/Firebird.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Export'); + /** * Doctrine_Export_Sqlite * @@ -31,521 +31,9 @@ Doctrine::autoload('Doctrine_Export'); * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ + * @todo Remove */ 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; - } } diff --git a/lib/Doctrine/Export/Frontbase.php b/lib/Doctrine/Export/Frontbase.php index 50b5abbd5..e10e804e8 100644 --- a/lib/Doctrine/Export/Frontbase.php +++ b/lib/Doctrine/Export/Frontbase.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Export'); + /** * Doctrine_Export_Frontbase * diff --git a/lib/Doctrine/Export/Mssql.php b/lib/Doctrine/Export/Mssql.php index fe81403c3..94f9b09c7 100644 --- a/lib/Doctrine/Export/Mssql.php +++ b/lib/Doctrine/Export/Mssql.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Export'); + /** * Doctrine_Export_Mssql * @@ -31,226 +31,9 @@ Doctrine::autoload('Doctrine_Export'); * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ + * @todo Remove */ 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 e2bc50e13..18aa4fe4d 100644 --- a/lib/Doctrine/Export/Mysql.php +++ b/lib/Doctrine/Export/Mysql.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Export'); + /** * Doctrine_Export_Mysql * @@ -30,699 +30,9 @@ Doctrine::autoload('Doctrine_Export'); * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ + * @todo Remove */ 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 '; - if (!empty($options['temporary'])) { - $query .= 'TEMPORARY '; - } - $query.= '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 . '))'; - - if (!empty($options_strings)) { - $query .= ' '.implode(' ', $options_strings); - } - - $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 { - $res = $this->conn->exec('DROP TABLE ' . $sequenceName); - } catch(Doctrine_Connection_Exception $e) { - throw new Doctrine_Export_Exception('could not drop inconsistent sequence table'); - } - - return $res; - } - - /** - * 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->formatter->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($definition['fields']) . ')'; - - return $query; - } - - /** - * Obtain DBMS specific SQL code portion needed to declare an integer type - * field to be used in statements like CREATE TABLE. - * - * @param string $name name the field to be declared. - * @param string $field associative array with the name of the properties - * of the field being declared as array indexes. - * Currently, the types of supported field - * properties are as follows: - * - * unsigned - * Boolean flag that indicates whether the field - * should be declared as unsigned integer if - * possible. - * - * default - * Integer value to be used as default for this - * field. - * - * notnull - * Boolean flag that indicates whether this field is - * constrained to not be set to null. - * @return string DBMS specific SQL code portion that should be used to - * declare the specified field. - */ - public function getIntegerDeclaration($name, $field) - { - $default = $autoinc = ''; - if ( ! empty($field['autoincrement'])) { - $autoinc = ' AUTO_INCREMENT'; - } elseif (array_key_exists('default', $field)) { - if ($field['default'] === '') { - $field['default'] = empty($field['notnull']) ? null : 0; - } - if (is_null($field['default'])) { - $default = ' DEFAULT NULL'; - } else { - $default = ' DEFAULT '.$this->conn->quote($field['default']); - } - } elseif (empty($field['notnull'])) { - $default = ' DEFAULT NULL'; - } - - $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : ''; - $unsigned = (isset($field['unsigned']) && $field['unsigned']) ? ' UNSIGNED' : ''; - - $name = $this->conn->quoteIdentifier($name, true); - - return $name . ' ' . $this->conn->dataDict->getNativeDeclaration($field) . $unsigned . $default . $notnull . $autoinc; - } - - /** - * 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 = empty($field['notnull']) && !in_array($field['type'], array('clob', 'blob')) - ? ' DEFAULT NULL' : ''; - - if (isset($field['default']) && ( ! isset($field['length']) || $field['length'] <= 255)) { - if ($field['default'] === '') { - $field['default'] = null; - if (! empty($field['notnull']) && array_key_exists($field['type'], $this->valid_default_values)) { - $field['default'] = $this->valid_default_values[$field['type']]; - } - - if ($field['default'] === '' - && ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_EMPTY_TO_NULL) - ) { - $field['default'] = ' '; - } - } - - if ($field['type'] == 'enum' && $this->conn->getAttribute(Doctrine::ATTR_USE_NATIVE_ENUM)) { - $fieldType = 'varchar'; - } else { - if ($field['type'] === 'boolean') { - $fields['default'] = $this->conn->convertBooleans($field['default']); - } - $fieldType = $field['type']; - } - - $default = ' DEFAULT ' . $this->conn->quote($field['default'], $fieldType); - } - 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; - } - - public function dropForeignKey($table, $name) - { - $table = $this->conn->quoteIdentifier($table); - $name = $this->conn->quoteIdentifier($name); - return $this->conn->exec('ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $name); - } + } diff --git a/lib/Doctrine/Export/Oracle.php b/lib/Doctrine/Export/Oracle.php index f0204e2e5..670aee4ec 100644 --- a/lib/Doctrine/Export/Oracle.php +++ b/lib/Doctrine/Export/Oracle.php @@ -30,472 +30,9 @@ * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ + * @todo Remove. */ 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/Pgsql.php b/lib/Doctrine/Export/Pgsql.php index 97929c1c4..437b0de0b 100644 --- a/lib/Doctrine/Export/Pgsql.php +++ b/lib/Doctrine/Export/Pgsql.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Export'); + /** * Doctrine_Export_Pgsql * @@ -30,383 +30,9 @@ Doctrine::autoload('Doctrine_Export'); * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ + * @todo Remove */ class Doctrine_Export_Pgsql extends Doctrine_Export { - /** - * create a new database - * - * @param string $name name of the database that should be created - * @throws PDOException - * @return void - */ - public function createDatabaseSql($name) - { - $query = 'CREATE DATABASE ' . $this->conn->quoteIdentifier($name); - - return $query; - } - - /** - * drop an existing database - * - * @param string $name name of the database that should be dropped - * @throws PDOException - * @access public - */ - public function dropDatabaseSql($name) - { - $query = 'DROP DATABASE ' . $this->conn->quoteIdentifier($name); - - return $query; - } - - /** - * 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; - } - - /** - * generates the sql for altering an existing table on postgresql - * - * @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 * - * @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. - * @see Doctrine_Export::alterTable() - * @return array - */ - public function alterTableSql($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 true; - } - - $sql = array(); - - if (isset($changes['add']) && is_array($changes['add'])) { - foreach ($changes['add'] as $fieldName => $field) { - $query = 'ADD ' . $this->getDeclaration($fieldName, $field); - $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; - } - } - - if (isset($changes['remove']) && is_array($changes['remove'])) { - foreach ($changes['remove'] as $fieldName => $field) { - $fieldName = $this->conn->quoteIdentifier($fieldName, true); - $query = 'DROP ' . $fieldName; - $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; - } - } - - if (isset($changes['change']) && is_array($changes['change'])) { - foreach ($changes['change'] as $fieldName => $field) { - $fieldName = $this->conn->quoteIdentifier($fieldName, true); - if (isset($field['type'])) { - $serverInfo = $this->conn->getServerVersion(); - - if (is_array($serverInfo) && $serverInfo['major'] < 8) { - throw new Doctrine_Export_Exception('changing column type for "'.$field['type'].'\" requires PostgreSQL 8.0 or above'); - } - $query = 'ALTER ' . $fieldName . ' TYPE ' . $this->conn->datatype->getTypeDeclaration($field['definition']); - $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; - } - if (array_key_exists('default', $field)) { - $query = 'ALTER ' . $fieldName . ' SET DEFAULT ' . $this->conn->quote($field['definition']['default'], $field['definition']['type']); - $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; - } - if ( ! empty($field['notnull'])) { - $query = 'ALTER ' . $fieldName . ' ' . ($field['definition']['notnull'] ? 'SET' : 'DROP') . ' NOT NULL'; - $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; - } - } - } - - if (isset($changes['rename']) && is_array($changes['rename'])) { - foreach ($changes['rename'] as $fieldName => $field) { - $fieldName = $this->conn->quoteIdentifier($fieldName, true); - $sql[] = 'ALTER TABLE ' . $name . ' RENAME COLUMN ' . $fieldName . ' TO ' . $this->conn->quoteIdentifier($field['name'], true); - } - } - - $name = $this->conn->quoteIdentifier($name, true); - if (isset($changes['name'])) { - $changeName = $this->conn->quoteIdentifier($changes['name'], true); - $sql[] = 'ALTER TABLE ' . $name . ' RENAME TO ' . $changeName; - } - - 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. - * @throws Doctrine_Connection_Exception - * @return boolean - */ - public function alterTable($name, array $changes, $check = false) - { - $sql = $this->alterTableSql($name, $changes, $check); - foreach ($sql as $query) { - $this->conn->exec($query); - } - return true; - } - - /** - * return RDBMS specific create sequence statement - * - * @throws Doctrine_Connection_Exception if something fails at database level - * @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($sequenceName, $start = 1, array $options = array()) - { - $sequenceName = $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($sequenceName), true); - return $this->conn->exec('CREATE SEQUENCE ' . $sequenceName . ' INCREMENT 1' . - ($start < 1 ? ' MINVALUE ' . $start : '') . ' START ' . $start); - } - - /** - * drop existing sequence - * - * @param string $sequenceName name of the sequence to be dropped - */ - public function dropSequenceSql($sequenceName) - { - $sequenceName = $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($sequenceName), true); - return 'DROP SEQUENCE ' . $sequenceName; - } - - /** - * Creates a table. - * - * @param unknown_type $name - * @param array $fields - * @param array $options - * @return unknown - */ - 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); - - - 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 . ')'; - - $sql[] = $query; - - if (isset($options['indexes']) && ! empty($options['indexes'])) { - foreach($options['indexes'] as $index => $definition) { - $sql[] = $this->createIndexSql($name, $index, $definition); - } - } - - if (isset($options['foreignKeys'])) { - - foreach ((array) $options['foreignKeys'] as $k => $definition) { - if (is_array($definition)) { - $sql[] = $this->createForeignKeySql($name, $definition); - } - } - } - - return $sql; - } - - /** - * Obtain DBMS specific SQL code portion needed to declare an integer type - * field to be used in statements like CREATE TABLE. - * - * @param string $name name the field to be declared. - * @param array $field associative array with the name of the properties - * of the field being declared as array indexes. Currently, the types - * of supported field properties are as follows: - * - * unsigned - * Boolean flag that indicates whether the field should be - * declared as unsigned integer if possible. - * - * default - * Integer value to be used as default for this field. - * - * notnull - * Boolean flag that indicates whether this field is constrained - * to not be set to null. - * @return string DBMS specific SQL code portion that should be used to - * declare the specified field. - */ - public function getIntegerDeclaration($name, $field) - { - /** - if ( ! empty($field['unsigned'])) { - $this->conn->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer"; - } - */ - - if ( ! empty($field['autoincrement'])) { - $name = $this->conn->quoteIdentifier($name, true); - return $name . ' ' . $this->conn->dataDict->getNativeDeclaration($field); - } - - $default = ''; - if (array_key_exists('default', $field)) { - if ($field['default'] === '') { - $field['default'] = empty($field['notnull']) ? null : 0; - } - $default = ' DEFAULT '.$this->conn->quote($field['default'], $field['type']); - } elseif (empty($field['notnull'])) { - $default = ' DEFAULT NULL'; - } - - $notnull = empty($field['notnull']) ? '' : ' NOT NULL'; - $name = $this->conn->quoteIdentifier($name, true); - return $name . ' ' . $this->conn->dataDict->getNativeDeclaration($field) . $default . $notnull; - } + } diff --git a/lib/Doctrine/Export/Sqlite.php b/lib/Doctrine/Export/Sqlite.php index 4a806e708..dcfb09a6c 100644 --- a/lib/Doctrine/Export/Sqlite.php +++ b/lib/Doctrine/Export/Sqlite.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Export'); + /** * Doctrine_Export_Sqlite * @@ -30,457 +30,9 @@ Doctrine::autoload('Doctrine_Export'); * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ + * @todo Remove */ class Doctrine_Export_Sqlite extends Doctrine_Export { - /** - * dropDatabase - * - * drop an existing database - * - * @param string $databaseFile Path 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($databaseFile) - { - 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'); - } - } - - /** - * createDatabase - * - * Create sqlite database file - * - * @param string $databaseFile Path of the database that should be dropped - * @return void - */ - public function createDatabase($databaseFile) - { - return new PDO('sqlite:' . $databaseFile); - } - - /** - * 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; - } - - /** - * Obtain DBMS specific SQL code portion needed to declare an integer type - * field to be used in statements like CREATE TABLE. - * - * @param string $name name the field to be declared. - * @param array $field associative array with the name of the properties - * of the field being declared as array indexes. - * Currently, the types of supported field - * properties are as follows: - * - * unsigned - * Boolean flag that indicates whether the field - * should be declared as unsigned integer if - * possible. - * - * default - * Integer value to be used as default for this - * field. - * - * notnull - * Boolean flag that indicates whether this field is - * constrained to not be set to null. - * @return string DBMS specific SQL code portion that should be used to - * declare the specified field. - * @access protected - */ - public function getIntegerDeclaration($name, array $field) - { - $default = $autoinc = ''; - $type = $this->conn->dataDict->getNativeDeclaration($field); - - $autoincrement = isset($field['autoincrement']) && $field['autoincrement']; - - if ($autoincrement) { - $autoinc = ' PRIMARY KEY AUTOINCREMENT'; - $type = 'INTEGER'; - } elseif (array_key_exists('default', $field)) { - if ($field['default'] === '') { - $field['default'] = empty($field['notnull']) ? null : 0; - } - $default = ' DEFAULT ' . $this->conn->quote($field['default'], $field['type']); - } elseif (empty($field['notnull'])) { - $default = ' DEFAULT NULL'; - } - - $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : ''; - - // sqlite does not support unsigned attribute for autoinremented fields - $unsigned = (isset($field['unsigned']) && $field['unsigned'] && !$autoincrement) ? ' UNSIGNED' : ''; - - $name = $this->conn->quoteIdentifier($name, true); - return $name . ' ' . $type . $unsigned . $default . $notnull . $autoinc; - } } diff --git a/lib/Doctrine/Import.php b/lib/Doctrine/Import.php index 8542cfe9e..4588559d3 100644 --- a/lib/Doctrine/Import.php +++ b/lib/Doctrine/Import.php @@ -38,157 +38,6 @@ */ class Doctrine_Import extends Doctrine_Connection_Module { - protected $sql = array(); - - /** - * lists all databases - * - * @return array - */ - public function listDatabases() - { - if ( ! isset($this->sql['listDatabases'])) { - throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); - } - - return $this->conn->fetchColumn($this->sql['listDatabases']); - } - - /** - * lists all availible database functions - * - * @return array - */ - public function listFunctions() - { - if ( ! isset($this->sql['listFunctions'])) { - throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); - } - - return $this->conn->fetchColumn($this->sql['listFunctions']); - } - - /** - * lists all database triggers - * - * @param string|null $database - * @return array - */ - public function listTriggers($database = null) - { - throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); - } - - /** - * lists all database sequences - * - * @param string|null $database - * @return array - */ - public function listSequences($database = null) - { - if ( ! isset($this->sql['listSequences'])) { - throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); - } - - return $this->conn->fetchColumn($this->sql['listSequences']); - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableConstraints($table) - { - throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableColumns($table) - { - throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableIndexes($table) - { - throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); - } - - /** - * lists tables - * - * @param string|null $database - * @return array - */ - public function listTables($database = null) - { - throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); - } - - /** - * lists table triggers - * - * @param string $table database table name - * @return array - */ - public function listTableTriggers($table) - { - throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); - } - - /** - * lists table views - * - * @param string $table database table name - * @return array - */ - public function listTableViews($table) - { - throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); - } - - /** - * lists database users - * - * @return array - */ - public function listUsers() - { - if ( ! isset($this->sql['listUsers'])) { - throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); - } - - return $this->conn->fetchColumn($this->sql['listUsers']); - } - - /** - * lists database views - * - * @param string|null $database - * @return array - */ - public function listViews($database = null) - { - if ( ! isset($this->sql['listViews'])) { - throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); - } - - return $this->conn->fetchColumn($this->sql['listViews']); - } - /** * importSchema * diff --git a/lib/Doctrine/Import/Firebird.php b/lib/Doctrine/Import/Firebird.php index a4c48eeaf..c92788802 100644 --- a/lib/Doctrine/Import/Firebird.php +++ b/lib/Doctrine/Import/Firebird.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Import'); + /** * @package Doctrine * @subpackage Import @@ -28,101 +28,10 @@ Doctrine::autoload('Doctrine_Import'); * @author Lukas Smith (PEAR MDB2 library) * @version $Revision$ * @link www.phpdoctrine.org - * @since 1.0 + * @since 1.0 + * @todo Remove */ class Doctrine_Import_Firebird extends Doctrine_Import { - /** - * list all tables in the current database - * - * @return array data array - */ - public function listTables($database = null) - { - $query = 'SELECT RDB$RELATION_NAME FROM RDB$RELATIONS WHERE RDB$SYSTEM_FLAG=0 AND RDB$VIEW_BLR IS NULL'; - return $this->conn->fetchColumn($query); - } - - /** - * list all fields in a tables in the current database - * - * @param string $table name of table that should be used in method - * @return mixed data array on success, a MDB2 error on failure - * @access public - */ - public function listTableFields($table) - { - $table = $this->conn->quote(strtoupper($table), 'text'); - $query = 'SELECT RDB$FIELD_NAME FROM RDB$RELATION_FIELDS WHERE UPPER(RDB$RELATION_NAME) = ' . $table; - - return $this->conn->fetchColumn($query); - } - - /** - * list all users - * - * @return array data array containing all database users - */ - public function listUsers() - { - return $this->conn->fetchColumn('SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES'); - } - - /** - * list the views in the database - * - * @return array data array containing all database views - */ - public function listViews($database = null) - { - return $this->conn->fetchColumn('SELECT DISTINCT RDB$VIEW_NAME FROM RDB$VIEW_RELATIONS'); - } - - /** - * list the views in the database that reference a given table - * - * @param string $table table for which all references views should be found - * @return array data array containing all views for given table - */ - public function listTableViews($table) - { - $query = 'SELECT DISTINCT RDB$VIEW_NAME FROM RDB$VIEW_RELATIONS'; - $table = $this->conn->quote(strtoupper($table), 'text'); - $query .= ' WHERE UPPER(RDB$RELATION_NAME) = ' . $table; - - return $this->conn->fetchColumn($query); - } - - /** - * list all functions in the current database - * - * @return array data array containing all availible functions - */ - public function listFunctions() - { - $query = 'SELECT RDB$FUNCTION_NAME FROM RDB$FUNCTIONS WHERE RDB$SYSTEM_FLAG IS NULL'; - - return $this->conn->fetchColumn($query); - } - - /** - * This function will be called to get all triggers of the - * current database ($this->conn->getDatabase()) - * - * @param string $table The name of the table from the - * previous database to query against. - * @return array data array containing all triggers for given table - */ - public function listTableTriggers($table) - { - $query = 'SELECT RDB$TRIGGER_NAME FROM RDB$TRIGGERS WHERE RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG = 0'; - - if ( ! is_null($table)) { - $table = $this->conn->quote(strtoupper($table), 'text'); - $query .= ' WHERE UPPER(RDB$RELATION_NAME) = ' . $table; - } - - return $this->conn->fetchColumn($query); - } } \ No newline at end of file diff --git a/lib/Doctrine/Import/Informix.php b/lib/Doctrine/Import/Informix.php index 2e7f34ea3..4a5c89aa8 100644 --- a/lib/Doctrine/Import/Informix.php +++ b/lib/Doctrine/Import/Informix.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Import'); + /** * @package Doctrine * @subpackage Import @@ -27,29 +27,11 @@ Doctrine::autoload('Doctrine_Import'); * @author Lukas Smith (PEAR MDB2 library) * @version $Revision$ * @link www.phpdoctrine.org - * @since 1.0 + * @since 1.0 + * @todo Remove */ class Doctrine_Import_Informix extends Doctrine_Import { - protected $sql = array( - 'listTables' => "SELECT tabname,tabtype FROM systables WHERE tabtype IN ('T','V') AND owner != 'informix'", - 'listColumns' => "SELECT c.colname, c.coltype, c.collength, d.default, c.colno - FROM syscolumns c, systables t,outer sysdefaults d - WHERE c.tabid = t.tabid AND d.tabid = t.tabid AND d.colno = c.colno - AND tabname='%s' ORDER BY c.colno", - 'listPk' => "SELECT part1, part2, part3, part4, part5, part6, part7, part8 FROM - systables t, sysconstraints s, sysindexes i WHERE t.tabname='%s' - AND s.tabid=t.tabid AND s.constrtype='P' - AND i.idxname=s.idxname", - 'listForeignKeys' => "SELECT tr.tabname,updrule,delrule, - i.part1 o1,i2.part1 d1,i.part2 o2,i2.part2 d2,i.part3 o3,i2.part3 d3,i.part4 o4,i2.part4 d4, - i.part5 o5,i2.part5 d5,i.part6 o6,i2.part6 d6,i.part7 o7,i2.part7 d7,i.part8 o8,i2.part8 d8 - from systables t,sysconstraints s,sysindexes i, - sysreferences r,systables tr,sysconstraints s2,sysindexes i2 - where t.tabname='%s' - and s.tabid=t.tabid and s.constrtype='R' and r.constrid=s.constrid - and i.idxname=s.idxname and tr.tabid=r.ptabid - and s2.constrid=r.primary and i2.idxname=s2.idxname", - ); + } \ No newline at end of file diff --git a/lib/Doctrine/Import/Mssql.php b/lib/Doctrine/Import/Mssql.php index c30b96883..271fb659a 100644 --- a/lib/Doctrine/Import/Mssql.php +++ b/lib/Doctrine/Import/Mssql.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Import'); + /** * @package Doctrine * @subpackage Import @@ -30,172 +30,9 @@ Doctrine::autoload('Doctrine_Import'); * @version $Revision$ * @link www.phpdoctrine.org * @since 1.0 + * @todo Remove */ class Doctrine_Import_Mssql extends Doctrine_Import { - /** - * lists all database sequences - * - * @param string|null $database - * @return array - */ - public function listSequences($database = null) - { - $query = "SELECT name FROM sysobjects WHERE xtype = 'U'"; - $tableNames = $this->conn->fetchColumn($query); - return array_map(array($this->conn->formatter, 'fixSequenceName'), $tableNames); - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableColumns($table) - { - $sql = 'EXEC sp_columns @table_name = ' . $this->conn->quoteIdentifier($table, true); - $result = $this->conn->fetchAssoc($sql); - $columns = array(); - - foreach ($result as $key => $val) { - $val = array_change_key_case($val, CASE_LOWER); - - if (strstr($val['type_name'], ' ')) { - list($type, $identity) = explode(' ', $val['type_name']); - } else { - $type = $val['type_name']; - $identity = ''; - } - - if ($type == 'varchar') { - $type .= '(' . $val['length'] . ')'; - } - - $val['type'] = $type; - $val['identity'] = $identity; - $decl = $this->conn->dataDict->getPortableDeclaration($val); - - $description = array( - 'name' => $val['column_name'], - 'ntype' => $type, - 'type' => $decl['type'][0], - 'alltypes' => $decl['type'], - 'length' => $decl['length'], - 'fixed' => $decl['fixed'], - 'unsigned' => $decl['unsigned'], - 'notnull' => (bool) (trim($val['is_nullable']) === 'NO'), - 'default' => $val['column_def'], - 'primary' => (strtolower($identity) == 'identity'), - ); - $columns[$val['column_name']] = $description; - } - - return $columns; - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableIndexes($table) - { - - } - - /** - * lists tables - * - * @param string|null $database - * @return array - */ - public function listTables($database = null) - { - $sql = "SELECT name FROM sysobjects WHERE type = 'U' AND name <> 'dtproperties' ORDER BY name"; - - return $this->conn->fetchColumn($sql); - } - - /** - * lists all triggers - * - * @return array - */ - public function listTriggers($database = null) - { - $query = "SELECT name FROM sysobjects WHERE xtype = 'TR'"; - - $result = $this->conn->fetchColumn($query); - - return $result; - } - - /** - * lists table triggers - * - * @param string $table database table name - * @return array - */ - public function listTableTriggers($table) - { - $table = $this->conn->quote($table, 'text'); - $query = "SELECT name FROM sysobjects WHERE xtype = 'TR' AND object_name(parent_obj) = " . $table; - - $result = $this->conn->fetchColumn($query); - - return $result; - } - - /** - * lists table views - * - * @param string $table database table name - * @return array - */ - public function listTableViews($table) - { - $keyName = 'INDEX_NAME'; - $pkName = 'PK_NAME'; - if ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { - if ($this->conn->getAttribute(Doctrine::ATTR_FIELD_CASE) == CASE_LOWER) { - $keyName = strtolower($keyName); - $pkName = strtolower($pkName); - } else { - $keyName = strtoupper($keyName); - $pkName = strtoupper($pkName); - } - } - $table = $this->conn->quote($table, 'text'); - $query = 'EXEC sp_statistics @table_name = ' . $table; - $indexes = $this->conn->fetchColumn($query, $keyName); - - $query = 'EXEC sp_pkeys @table_name = ' . $table; - $pkAll = $this->conn->fetchColumn($query, $pkName); - - $result = array(); - - foreach ($indexes as $index) { - if ( ! in_array($index, $pkAll) && $index != null) { - $result[] = $this->conn->formatter->fixIndexName($index); - } - } - - return $result; - } - - /** - * lists database views - * - * @param string|null $database - * @return array - */ - public function listViews($database = null) - { - $query = "SELECT name FROM sysobjects WHERE xtype = 'V'"; - - return $this->conn->fetchColumn($query); - } } diff --git a/lib/Doctrine/Import/Mysql.php b/lib/Doctrine/Import/Mysql.php index 0bb93b577..59e4ca5ae 100644 --- a/lib/Doctrine/Import/Mysql.php +++ b/lib/Doctrine/Import/Mysql.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Import'); + /** * @package Doctrine * @subpackage Import @@ -28,199 +28,9 @@ Doctrine::autoload('Doctrine_Import'); * @version $Revision$ * @link www.phpdoctrine.org * @since 1.0 + * @todo Remove */ class Doctrine_Import_Mysql extends Doctrine_Import { - protected $sql = array( - 'showDatabases' => 'SHOW DATABASES', - 'listTableFields' => 'DESCRIBE %s', - 'listSequences' => 'SHOW TABLES', - 'listTables' => 'SHOW TABLES', - 'listUsers' => 'SELECT DISTINCT USER FROM USER', - 'listViews' => "SHOW FULL TABLES %s WHERE Table_type = 'VIEW'", - ); - - /** - * lists all database sequences - * - * @param string|null $database - * @return array - */ - public function listSequences($database = null) - { - $query = 'SHOW TABLES'; - if ( ! is_null($database)) { - $query .= ' FROM ' . $database; - } - $tableNames = $this->conn->fetchColumn($query); - - return array_map(array($this->conn->formatter, 'fixSequenceName'), $tableNames); - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableConstraints($table) - { - $keyName = 'Key_name'; - $nonUnique = 'Non_unique'; - if ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { - if ($this->conn->getAttribute(Doctrine::ATTR_FIELD_CASE) == CASE_LOWER) { - $keyName = strtolower($keyName); - $nonUnique = strtolower($nonUnique); - } else { - $keyName = strtoupper($keyName); - $nonUnique = strtoupper($nonUnique); - } - } - - $table = $this->conn->quoteIdentifier($table, true); - $query = 'SHOW INDEX FROM ' . $table; - $indexes = $this->conn->fetchAssoc($query); - - $result = array(); - foreach ($indexes as $indexData) { - if ( ! $indexData[$nonUnique]) { - if ($indexData[$keyName] !== 'PRIMARY') { - $index = $this->conn->formatter->fixIndexName($indexData[$keyName]); - } else { - $index = 'PRIMARY'; - } - if ( ! empty($index)) { - $result[] = $index; - } - } - } - return $result; - } - - /** - * lists table foreign keys - * - * @param string $table database table name - * @return array - */ - public function listTableForeignKeys($table) - { - $sql = 'SHOW CREATE TABLE ' . $this->conn->quoteIdentifier($table, true); - $definition = $this->conn->fetchOne($sql); - if (!empty($definition)) { - $pattern = '/\bCONSTRAINT\s+([^\s]+)\s+FOREIGN KEY\b/i'; - if (preg_match_all($pattern, str_replace('`', '', $definition), $matches) > 1) { - foreach ($matches[1] as $constraint) { - $result[$constraint] = true; - } - } - } - - if ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { - $result = array_change_key_case($result, $this->conn->getAttribute(Doctrine::ATTR_FIELD_CASE)); - } - - return $result; - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableColumns($table) - { - $sql = 'DESCRIBE ' . $this->conn->quoteIdentifier($table, true); - $result = $this->conn->fetchAssoc($sql); - - $description = array(); - $columns = array(); - foreach ($result as $key => $val) { - - $val = array_change_key_case($val, CASE_LOWER); - - $decl = $this->conn->dataDict->getPortableDeclaration($val); - - $values = isset($decl['values']) ? $decl['values'] : array(); - - $description = array( - 'name' => $val['field'], - 'type' => $decl['type'][0], - 'alltypes' => $decl['type'], - 'ntype' => $val['type'], - 'length' => $decl['length'], - 'fixed' => $decl['fixed'], - 'unsigned' => $decl['unsigned'], - 'values' => $values, - 'primary' => (strtolower($val['key']) == 'pri'), - 'default' => $val['default'], - 'notnull' => (bool) ($val['null'] != 'YES'), - 'autoincrement' => (bool) (strpos($val['extra'], 'auto_increment') !== false), - ); - $columns[$val['field']] = $description; - } - - return $columns; - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableIndexes($table) - { - $keyName = 'Key_name'; - $nonUnique = 'Non_unique'; - if ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { - if ($this->conn->getAttribute(Doctrine::ATTR_FIELD_CASE) == CASE_LOWER) { - $keyName = strtolower($keyName); - $nonUnique = strtolower($nonUnique); - } else { - $keyName = strtoupper($keyName); - $nonUnique = strtoupper($nonUnique); - } - } - - $table = $this->conn->quoteIdentifier($table, true); - $query = 'SHOW INDEX FROM ' . $table; - $indexes = $this->conn->fetchAssoc($query); - - - $result = array(); - foreach ($indexes as $indexData) { - if ($indexData[$nonUnique] && ($index = $this->conn->formatter->fixIndexName($indexData[$keyName]))) { - $result[] = $index; - } - } - return $result; - } - - /** - * lists tables - * - * @param string|null $database - * @return array - */ - public function listTables($database = null) - { - return $this->conn->fetchColumn($this->sql['listTables']); - } - - /** - * lists database views - * - * @param string|null $database - * @return array - */ - public function listViews($database = null) - { - if ( ! is_null($database)) { - $query = sprintf($this->sql['listViews'], ' FROM ' . $database); - } - - return $this->conn->fetchColumn($query); - } + } diff --git a/lib/Doctrine/Import/Oracle.php b/lib/Doctrine/Import/Oracle.php index 2b2c5d19a..d5b48e17c 100644 --- a/lib/Doctrine/Import/Oracle.php +++ b/lib/Doctrine/Import/Oracle.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Import'); + /** * @package Doctrine * @subpackage Import @@ -26,214 +26,10 @@ Doctrine::autoload('Doctrine_Import'); * @author Konsta Vesterinen * @version $Revision$ * @link www.phpdoctrine.org - * @since 1.0 + * @since 1.0 + * @todo Remove */ class Doctrine_Import_Oracle extends Doctrine_Import { - /** - * lists all databases - * - * @return array - */ - public function listDatabases() - { - if ( ! $this->conn->getAttribute(Doctrine::ATTR_EMULATE_DATABASE)) { - throw new Doctrine_Import_Exception('database listing is only supported if the "emulate_database" option is enabled'); - } - /** - if ($this->conn->options['database_name_prefix']) { - $query = 'SELECT SUBSTR(username, '; - $query.= (strlen($this->conn->getAttribute(['database_name_prefix'])+1); - $query.= ") FROM sys.dba_users WHERE username LIKE '"; - $query.= $this->conn->options['database_name_prefix']."%'"; - } else { - */ - $query = 'SELECT username FROM sys.dba_users'; - $result2 = $this->conn->standaloneQuery($query); - $result = $result2->fetchColumn(); - - return $result; - } - - /** - * lists all availible database functions - * - * @return array - */ - public function listFunctions() - { - $query = "SELECT name FROM sys.user_source WHERE line = 1 AND type = 'FUNCTION'"; - - return $this->conn->fetchColumn($query); - } - - /** - * lists all database triggers - * - * @param string|null $database - * @return array - */ - public function listTriggers($database = null) - { - - } - - /** - * lists all database sequences - * - * @param string|null $database - * @return array - */ - public function listSequences($database = null) - { - $query = "SELECT sequence_name FROM sys.user_sequences"; - - $tableNames = $this->conn->fetchColumn($query); - - return array_map(array($this->conn->formatter, 'fixSequenceName'), $tableNames); - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableConstraints($table) - { - $table = $this->conn->quote($table, 'text'); - - $query = 'SELECT index_name name FROM user_constraints' - . ' WHERE table_name = ' . $table . ' OR table_name = ' . strtoupper($table); - - $constraints = $this->conn->fetchColumn($query); - - return array_map(array($this->conn->formatter, 'fixIndexName'), $constraints); - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableColumns($table) - { - $table = strtoupper($table); - $sql = "SELECT column_name, data_type, data_length, nullable, data_default, data_scale, data_precision FROM all_tab_columns" - . " WHERE table_name = '" . $table . "' ORDER BY column_name"; - - $result = $this->conn->fetchAssoc($sql); - - $descr = array(); - - foreach($result as $val) { - $val = array_change_key_case($val, CASE_LOWER); - $decl = $this->conn->dataDict->getPortableDeclaration($val); - - - $descr[$val['column_name']] = array( - 'name' => $val['column_name'], - 'notnull' => (bool) ($val['nullable'] === 'N'), - 'ntype' => $val['data_type'], - 'type' => $decl['type'][0], - 'alltypes' => $decl['type'], - 'fixed' => $decl['fixed'], - 'unsigned' => $decl['unsigned'], - 'default' => $val['data_default'], - 'length' => $val['data_length'], - 'precision' => $val['data_precision'], - 'scale' => $val['scale'], - ); - } - - return $descr; - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableIndexes($table) - { - $table = $this->conn->quote($table, 'text'); - $query = 'SELECT index_name name FROM user_indexes' - . ' WHERE table_name = ' . $table . ' OR table_name = ' . strtoupper($table) - . ' AND generated = ' . $this->conn->quote('N', 'text'); - - $indexes = $this->conn->fetchColumn($query); - - return array_map(array($this->conn->formatter, 'fixIndexName'), $indexes); - } - - /** - * lists tables - * - * @param string|null $database - * @return array - */ - public function listTables($database = null) - { - $query = 'SELECT table_name FROM sys.user_tables'; - return $this->conn->fetchColumn($query); - } - - /** - * lists table triggers - * - * @param string $table database table name - * @return array - */ - public function listTableTriggers($table) - { - - } - - /** - * lists table views - * - * @param string $table database table name - * @return array - */ - public function listTableViews($table) - { - - } - - /** - * lists database users - * - * @return array - */ - public function listUsers() - { - /** - if ($this->conn->options['emulate_database'] && $this->conn->options['database_name_prefix']) { - $query = 'SELECT SUBSTR(username, '; - $query.= (strlen($this->conn->options['database_name_prefix'])+1); - $query.= ") FROM sys.dba_users WHERE username NOT LIKE '"; - $query.= $this->conn->options['database_name_prefix']."%'"; - } else { - */ - - $query = 'SELECT username FROM sys.dba_users'; - //} - - return $this->conn->fetchColumn($query); - } - - /** - * lists database views - * - * @param string|null $database - * @return array - */ - public function listViews($database = null) - { - $query = 'SELECT view_name FROM sys.user_views'; - return $this->conn->fetchColumn($query); - } } diff --git a/lib/Doctrine/Import/Pgsql.php b/lib/Doctrine/Import/Pgsql.php index 06b84fcea..1e3b342aa 100644 --- a/lib/Doctrine/Import/Pgsql.php +++ b/lib/Doctrine/Import/Pgsql.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Import'); + /** * @package Doctrine * @subpackage Import @@ -28,224 +28,12 @@ Doctrine::autoload('Doctrine_Import'); * @author Lukas Smith (PEAR MDB2 library) * @version $Revision$ * @link www.phpdoctrine.org - * @since 1.0 + * @since 1.0 + * @todo Remove */ class Doctrine_Import_Pgsql extends Doctrine_Import { - protected $sql = array( - 'listDatabases' => 'SELECT datname FROM pg_database', - 'listFunctions' => "SELECT - proname - FROM - pg_proc pr, - pg_type tp - WHERE - tp.oid = pr.prorettype - AND pr.proisagg = FALSE - AND tp.typname <> 'trigger' - AND pr.pronamespace IN - (SELECT oid FROM pg_namespace - WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema'", - 'listSequences' => "SELECT - relname - FROM - pg_class - WHERE relkind = 'S' AND relnamespace IN - (SELECT oid FROM pg_namespace - WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')", - 'listTables' => "SELECT - c.relname AS table_name - FROM pg_class c, pg_user u - WHERE c.relowner = u.usesysid - AND c.relkind = 'r' - AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) - AND c.relname !~ '^(pg_|sql_)' - UNION - SELECT c.relname AS table_name - FROM pg_class c - WHERE c.relkind = 'r' - AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) - AND NOT EXISTS (SELECT 1 FROM pg_user WHERE usesysid = c.relowner) - AND c.relname !~ '^pg_'", - 'listViews' => 'SELECT viewname FROM pg_views', - 'listUsers' => 'SELECT usename FROM pg_user', - 'listTableConstraints' => "SELECT - relname - FROM - pg_class - WHERE oid IN ( - SELECT indexrelid - FROM pg_index, pg_class - WHERE pg_class.relname = %s - AND pg_class.oid = pg_index.indrelid - AND (indisunique = 't' OR indisprimary = 't') - )", - 'listTableIndexes' => "SELECT - relname - FROM - pg_class - WHERE oid IN ( - SELECT indexrelid - FROM pg_index, pg_class - WHERE pg_class.relname = %s - AND pg_class.oid=pg_index.indrelid - AND indisunique != 't' - AND indisprimary != 't' - )", - 'listTableColumns' => "SELECT - a.attnum, - a.attname AS field, - t.typname AS type, - format_type(a.atttypid, a.atttypmod) AS complete_type, - a.attnotnull AS isnotnull, - (SELECT 't' - FROM pg_index - WHERE c.oid = pg_index.indrelid - AND pg_index.indkey[0] = a.attnum - AND pg_index.indisprimary = 't' - ) AS pri, - (SELECT pg_attrdef.adsrc - FROM pg_attrdef - WHERE c.oid = pg_attrdef.adrelid - AND pg_attrdef.adnum=a.attnum - ) AS default - FROM pg_attribute a, pg_class c, pg_type t - WHERE c.relname = %s - AND a.attnum > 0 - AND a.attrelid = c.oid - AND a.atttypid = t.oid - ORDER BY a.attnum", - ); - - /** - * lists all database triggers - * - * @param string|null $database - * @return array - */ - public function listTriggers($database = null) - { - - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableConstraints($table) - { - $table = $this->conn->quote($table); - $query = sprintf($this->sql['listTableConstraints'], $table); - - return $this->conn->fetchColumn($query); - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableColumns($table) - { - $table = $this->conn->quote($table); - $query = sprintf($this->sql['listTableColumns'], $table); - $result = $this->conn->fetchAssoc($query); - - $columns = array(); - foreach ($result as $key => $val) { - $val = array_change_key_case($val, CASE_LOWER); - - if (strtolower($val['type']) === 'varchar') { - // get length from varchar definition - $length = preg_replace('~.*\(([0-9]*)\).*~', '$1', $val['complete_type']); - $val['length'] = $length; - } - - $decl = $this->conn->dataDict->getPortableDeclaration($val); - - $description = array( - 'name' => $val['field'], - 'ntype' => $val['type'], - 'type' => $decl['type'][0], - 'alltypes' => $decl['type'], - 'length' => $decl['length'], - 'fixed' => $decl['fixed'], - 'unsigned' => $decl['unsigned'], - 'notnull' => ($val['isnotnull'] == true), - 'default' => $val['default'], - 'primary' => ($val['pri'] == 't'), - ); - - $matches = array(); - - if (preg_match("/^nextval\('(.*)'(::.*)?\)$/", $description['default'], $matches)) { - - $description['sequence'] = $this->conn->formatter->fixSequenceName($matches[1]); - $description['default'] = null; - } - - $columns[$val['field']] = $description; - } - - return $columns; - } - - /** - * list all indexes in a table - * - * @param string $table database table name - * @return array - */ - public function listTableIndexes($table) - { - $table = $this->conn->quote($table); - $query = sprintf($this->sql['listTableIndexes'], $table); - - return $this->conn->fetchColumn($query); - } - - /** - * lists tables - * - * @param string|null $database - * @return array - */ - public function listTables($database = null) - { - return $this->conn->fetchColumn($this->sql['listTables']); - } - - /** - * lists table triggers - * - * @param string $table database table name - * @return array - */ - public function listTableTriggers($table) - { - $query = 'SELECT trg.tgname AS trigger_name - FROM pg_trigger trg, - pg_class tbl - WHERE trg.tgrelid = tbl.oid'; - if ($table !== null) { - $table = $this->conn->quote(strtoupper($table), 'string'); - $query .= " AND tbl.relname = $table"; - } - return $this->conn->fetchColumn($query); - } - - /** - * list the views in the database that reference a given table - * - * @param string $table database table name - * @return array - */ - public function listTableViews($table) - { - return $this->conn->fetchColumn($query); - } + + } \ No newline at end of file diff --git a/lib/Doctrine/Import/Sqlite.php b/lib/Doctrine/Import/Sqlite.php index 760d5e33c..922fa5fb4 100644 --- a/lib/Doctrine/Import/Sqlite.php +++ b/lib/Doctrine/Import/Sqlite.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Import'); + /** * @package Doctrine * @subpackage Import @@ -28,230 +28,9 @@ Doctrine::autoload('Doctrine_Import'); * @version $Revision$ * @link www.phpdoctrine.org * @since 1.0 + * @todo Remove */ class Doctrine_Import_Sqlite extends Doctrine_Import { - /** - * lists all databases - * - * @return array - */ - public function listDatabases() - { - } - - /** - * lists all availible database functions - * - * @return array - */ - public function listFunctions() - { - - } - - /** - * lists all database triggers - * - * @param string|null $database - * @return array - */ - public function listTriggers($database = null) - { - return $this->listTableTriggers(null); - } - - /** - * lists all database sequences - * - * @param string|null $database - * @return array - */ - public function listSequences($database = null) - { - $query = "SELECT name FROM sqlite_master WHERE type='table' AND sql NOT NULL ORDER BY name"; - $tableNames = $this->conn->fetchColumn($query); - - $result = array(); - foreach ($tableNames as $tableName) { - if ($sqn = $this->conn->fixSequenceName($tableName, true)) { - $result[] = $sqn; - } - } - if ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { - $result = array_map(($this->conn->getAttribute(Doctrine::ATTR_FIELD_CASE) == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); - } - return $result; - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableConstraints($table) - { - $table = $this->conn->quote($table, 'text'); - - $query = "SELECT sql FROM sqlite_master WHERE type='index' AND "; - - if ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { - $query .= 'LOWER(tbl_name) = ' . strtolower($table); - } else { - $query .= 'tbl_name = ' . $table; - } - $query .= ' AND sql NOT NULL ORDER BY name'; - $indexes = $this->conn->fetchColumn($query); - - $result = array(); - foreach ($indexes as $sql) { - if (preg_match("/^create unique index ([^ ]+) on /i", $sql, $tmp)) { - $index = $this->conn->formatter->fixIndexName($tmp[1]); - if ( ! empty($index)) { - $result[$index] = true; - } - } - } - - if ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { - $result = array_change_key_case($result, $this->conn->getAttribute(Doctrine::ATTR_FIELD_CASE)); - } - return array_keys($result); - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableColumns($table) - { - $sql = 'PRAGMA table_info(' . $table . ')'; - $result = $this->conn->fetchAll($sql); - - $description = array(); - $columns = array(); - foreach ($result as $key => $val) { - $val = array_change_key_case($val, CASE_LOWER); - $decl = $this->conn->dataDict->getPortableDeclaration($val); - - $description = array( - 'name' => $val['name'], - 'ntype' => $val['type'], - 'type' => $decl['type'][0], - 'alltypes' => $decl['type'], - 'notnull' => (bool) $val['notnull'], - 'default' => $val['dflt_value'], - 'primary' => (bool) $val['pk'], - 'length' => null, - 'scale' => null, - 'precision' => null, - 'unsigned' => null, - ); - $columns[$val['name']] = $description; - } - return $columns; - } - - /** - * lists table constraints - * - * @param string $table database table name - * @return array - */ - public function listTableIndexes($table) - { - $sql = 'PRAGMA index_list(' . $table . ')'; - return $this->conn->fetchColumn($sql); - } - /** - * lists tables - * - * @param string|null $database - * @return array - */ - public function listTables($database = null) - { - $sql = "SELECT name FROM sqlite_master WHERE type = 'table' " - . "UNION ALL SELECT name FROM sqlite_temp_master " - . "WHERE type = 'table' ORDER BY name"; - - return $this->conn->fetchColumn($sql); - } - - /** - * lists table triggers - * - * @param string $table database table name - * @return array - */ - public function listTableTriggers($table) - { - $query = "SELECT name FROM sqlite_master WHERE type='trigger' AND sql NOT NULL"; - if (!is_null($table)) { - if ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { - if ($this->conn->getAttribute(Doctrine::ATTR_FIELD_CASE) == CASE_LOWER) { - $query.= ' AND LOWER(tbl_name)='.$db->quote(strtolower($table), 'text'); - } else { - $query.= ' AND UPPER(tbl_name)='.$db->quote(strtoupper($table), 'text'); - } - } else { - $query.= ' AND tbl_name='.$db->quote($table, 'text'); - } - } - $result = $this->conn->fetchColumn($query); - - if ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { - $result = array_map(($this->conn->getAttribute(Doctrine::ATTR_FIELD_CASE) == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); - } - return $result; - } - - /** - * lists table views - * - * @param string $table database table name - * @return array - */ - public function listTableViews($table) - { - $query = "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL"; - $views = $db->fetchAll($query); - - $result = array(); - foreach ($views as $row) { - if (preg_match("/^create view .* \bfrom\b\s+\b{$table}\b /i", $row['sql'])) { - if ( ! empty($row['name'])) { - $result[$row['name']] = true; - } - } - } - return $result; - } - - /** - * lists database users - * - * @return array - */ - public function listUsers() - { - - } - - /** - * lists database views - * - * @param string|null $database - * @return array - */ - public function listViews($database = null) - { - $query = "SELECT name FROM sqlite_master WHERE type='view' AND sql NOT NULL"; - - return $this->conn->fetchColumn($query); - } } diff --git a/lib/Doctrine/Schema/FirebirdSchemaManager.php b/lib/Doctrine/Schema/FirebirdSchemaManager.php new file mode 100644 index 000000000..da943b10b --- /dev/null +++ b/lib/Doctrine/Schema/FirebirdSchemaManager.php @@ -0,0 +1,652 @@ +. + */ + +#namespace Doctrine::DBAL::Schema; + +/** + * xxx + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Lorenzo Alberton (PEAR MDB2 Interbase driver) + * @version $Revision$ + * @since 2.0 + */ +class Doctrine_Schema_FirebirdSchemaManager extends Doctrine_Schema_SchemaManager +{ + public function __construct(Doctrine_Connection_Firebird $conn) + { + $this->_conn = $conn; + } + + /** + * list all tables in the current database + * + * @return array data array + */ + public function listTables($database = null) + { + $query = 'SELECT RDB$RELATION_NAME FROM RDB$RELATIONS WHERE RDB$SYSTEM_FLAG=0 AND RDB$VIEW_BLR IS NULL'; + + return $this->conn->fetchColumn($query); + } + + /** + * list all fields in a tables in the current database + * + * @param string $table name of table that should be used in method + * @return mixed data array on success, a MDB2 error on failure + * @access public + */ + public function listTableFields($table) + { + $table = $this->_conn->quote(strtoupper($table), 'text'); + $query = 'SELECT RDB$FIELD_NAME FROM RDB$RELATION_FIELDS WHERE UPPER(RDB$RELATION_NAME) = ' . $table; + + return $this->_conn->fetchColumn($query); + } + + /** + * list all users + * + * @return array data array containing all database users + */ + public function listUsers() + { + return $this->_conn->fetchColumn('SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES'); + } + + /** + * list the views in the database + * + * @return array data array containing all database views + */ + public function listViews($database = null) + { + return $this->_conn->fetchColumn('SELECT DISTINCT RDB$VIEW_NAME FROM RDB$VIEW_RELATIONS'); + } + + /** + * list the views in the database that reference a given table + * + * @param string $table table for which all references views should be found + * @return array data array containing all views for given table + */ + public function listTableViews($table) + { + $query = 'SELECT DISTINCT RDB$VIEW_NAME FROM RDB$VIEW_RELATIONS'; + $table = $this->_conn->quote(strtoupper($table), 'text'); + $query .= ' WHERE UPPER(RDB$RELATION_NAME) = ' . $table; + + return $this->_conn->fetchColumn($query); + } + + /** + * list all functions in the current database + * + * @return array data array containing all availible functions + */ + public function listFunctions() + { + $query = 'SELECT RDB$FUNCTION_NAME FROM RDB$FUNCTIONS WHERE RDB$SYSTEM_FLAG IS NULL'; + + return $this->_conn->fetchColumn($query); + } + + /** + * This function will be called to get all triggers of the + * current database ($this->_conn->getDatabase()) + * + * @param string $table The name of the table from the + * previous database to query against. + * @return array data array containing all triggers for given table + */ + public function listTableTriggers($table) + { + $query = 'SELECT RDB$TRIGGER_NAME FROM RDB$TRIGGERS WHERE RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG = 0'; + + if ( ! is_null($table)) { + $table = $this->_conn->quote(strtoupper($table), 'text'); + $query .= ' WHERE UPPER(RDB$RELATION_NAME) = ' . $table; + } + + return $this->_conn->fetchColumn($query); + } + + /** + * 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/Schema/InformixSchemaManager.php b/lib/Doctrine/Schema/InformixSchemaManager.php new file mode 100644 index 000000000..be5327fff --- /dev/null +++ b/lib/Doctrine/Schema/InformixSchemaManager.php @@ -0,0 +1,65 @@ +. + */ + +#namespace Doctrine::DBAL::Schema; + +/** + * xxx + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Lorenzo Alberton (PEAR MDB2 Interbase driver) + * @version $Revision$ + * @since 2.0 + */ +class Doctrine_Schema_InformixSchemaManager extends Doctrine_Schema_SchemaManager +{ + protected $sql = array( + 'listTables' => "SELECT tabname,tabtype FROM systables WHERE tabtype IN ('T','V') AND owner != 'informix'", + 'listColumns' => "SELECT c.colname, c.coltype, c.collength, d.default, c.colno + FROM syscolumns c, systables t,outer sysdefaults d + WHERE c.tabid = t.tabid AND d.tabid = t.tabid AND d.colno = c.colno + AND tabname='%s' ORDER BY c.colno", + 'listPk' => "SELECT part1, part2, part3, part4, part5, part6, part7, part8 FROM + systables t, sysconstraints s, sysindexes i WHERE t.tabname='%s' + AND s.tabid=t.tabid AND s.constrtype='P' + AND i.idxname=s.idxname", + 'listForeignKeys' => "SELECT tr.tabname,updrule,delrule, + i.part1 o1,i2.part1 d1,i.part2 o2,i2.part2 d2,i.part3 o3,i2.part3 d3,i.part4 o4,i2.part4 d4, + i.part5 o5,i2.part5 d5,i.part6 o6,i2.part6 d6,i.part7 o7,i2.part7 d7,i.part8 o8,i2.part8 d8 + from systables t,sysconstraints s,sysindexes i, + sysreferences r,systables tr,sysconstraints s2,sysindexes i2 + where t.tabname='%s' + and s.tabid=t.tabid and s.constrtype='R' and r.constrid=s.constrid + and i.idxname=s.idxname and tr.tabid=r.ptabid + and s2.constrid=r.primary and i2.idxname=s2.idxname", + ); + + public function __construct(Doctrine_Connection_Informix $conn) + { + $this->_conn = $conn; + } + + +} + +?> \ No newline at end of file diff --git a/lib/Doctrine/Schema/MsSqlSchemaManager.php b/lib/Doctrine/Schema/MsSqlSchemaManager.php new file mode 100644 index 000000000..412bc386c --- /dev/null +++ b/lib/Doctrine/Schema/MsSqlSchemaManager.php @@ -0,0 +1,428 @@ +. + */ + +#namespace Doctrine::DBAL::Schema; + +/** + * xxx + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @version $Revision$ + * @since 2.0 + */ +class Doctrine_Schema_MsSqlSchemaManager extends Doctrine_Schema_SchemaManager +{ + public function __construct(Doctrine_Connection_Mssql $conn) + { + $this->_conn = $conn; + } + + /** + * 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; + } + + /** + * lists all database sequences + * + * @param string|null $database + * @return array + */ + public function listSequences($database = null) + { + $query = "SELECT name FROM sysobjects WHERE xtype = 'U'"; + $tableNames = $this->conn->fetchColumn($query); + + return array_map(array($this->conn->formatter, 'fixSequenceName'), $tableNames); + } + + /** + * lists table constraints + * + * @param string $table database table name + * @return array + */ + public function listTableColumns($table) + { + $sql = 'EXEC sp_columns @table_name = ' . $this->conn->quoteIdentifier($table, true); + $result = $this->conn->fetchAssoc($sql); + $columns = array(); + + foreach ($result as $key => $val) { + $val = array_change_key_case($val, CASE_LOWER); + + if (strstr($val['type_name'], ' ')) { + list($type, $identity) = explode(' ', $val['type_name']); + } else { + $type = $val['type_name']; + $identity = ''; + } + + if ($type == 'varchar') { + $type .= '(' . $val['length'] . ')'; + } + + $val['type'] = $type; + $val['identity'] = $identity; + $decl = $this->conn->dataDict->getPortableDeclaration($val); + + $description = array( + 'name' => $val['column_name'], + 'ntype' => $type, + 'type' => $decl['type'][0], + 'alltypes' => $decl['type'], + 'length' => $decl['length'], + 'fixed' => $decl['fixed'], + 'unsigned' => $decl['unsigned'], + 'notnull' => (bool) (trim($val['is_nullable']) === 'NO'), + 'default' => $val['column_def'], + 'primary' => (strtolower($identity) == 'identity'), + ); + $columns[$val['column_name']] = $description; + } + + return $columns; + } + + /** + * lists table constraints + * + * @param string $table database table name + * @return array + */ + public function listTableIndexes($table) + { + + } + + /** + * lists tables + * + * @param string|null $database + * @return array + */ + public function listTables($database = null) + { + $sql = "SELECT name FROM sysobjects WHERE type = 'U' AND name <> 'dtproperties' ORDER BY name"; + + return $this->conn->fetchColumn($sql); + } + + /** + * lists all triggers + * + * @return array + */ + public function listTriggers($database = null) + { + $query = "SELECT name FROM sysobjects WHERE xtype = 'TR'"; + + $result = $this->conn->fetchColumn($query); + + return $result; + } + + /** + * lists table triggers + * + * @param string $table database table name + * @return array + */ + public function listTableTriggers($table) + { + $table = $this->conn->quote($table, 'text'); + $query = "SELECT name FROM sysobjects WHERE xtype = 'TR' AND object_name(parent_obj) = " . $table; + + $result = $this->conn->fetchColumn($query); + + return $result; + } + + /** + * lists table views + * + * @param string $table database table name + * @return array + */ + public function listTableViews($table) + { + $keyName = 'INDEX_NAME'; + $pkName = 'PK_NAME'; + if ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { + if ($this->conn->getAttribute(Doctrine::ATTR_FIELD_CASE) == CASE_LOWER) { + $keyName = strtolower($keyName); + $pkName = strtolower($pkName); + } else { + $keyName = strtoupper($keyName); + $pkName = strtoupper($pkName); + } + } + $table = $this->conn->quote($table, 'text'); + $query = 'EXEC sp_statistics @table_name = ' . $table; + $indexes = $this->conn->fetchColumn($query, $keyName); + + $query = 'EXEC sp_pkeys @table_name = ' . $table; + $pkAll = $this->conn->fetchColumn($query, $pkName); + + $result = array(); + + foreach ($indexes as $index) { + if ( ! in_array($index, $pkAll) && $index != null) { + $result[] = $this->conn->formatter->fixIndexName($index); + } + } + + return $result; + } + + /** + * lists database views + * + * @param string|null $database + * @return array + */ + public function listViews($database = null) + { + $query = "SELECT name FROM sysobjects WHERE xtype = 'V'"; + + return $this->conn->fetchColumn($query); + } + +} + +?> \ No newline at end of file diff --git a/lib/Doctrine/Schema/MySqlSchemaManger.php b/lib/Doctrine/Schema/MySqlSchemaManger.php new file mode 100644 index 000000000..17d959867 --- /dev/null +++ b/lib/Doctrine/Schema/MySqlSchemaManger.php @@ -0,0 +1,956 @@ +. + */ + +#namespace Doctrine::DBAL::Schema; + +/** + * xxx + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @version $Revision$ + * @since 2.0 + */ +class Doctrine_Schema_MySqlSchemaManager extends Doctrine_Schema_SchemaManager +{ + protected $_sql = array( + 'showDatabases' => 'SHOW DATABASES', + 'listTableFields' => 'DESCRIBE %s', + 'listSequences' => 'SHOW TABLES', + 'listTables' => 'SHOW TABLES', + 'listUsers' => 'SELECT DISTINCT USER FROM USER', + 'listViews' => "SHOW FULL TABLES %s WHERE Table_type = 'VIEW'", + ); + + public function __construct(Doctrine_Connection_MySql $conn) + { + $this->_conn = $conn; + } + + /** + * lists all database sequences + * + * @param string|null $database + * @return array + * @override + */ + public function listSequences($database = null) + { + $query = 'SHOW TABLES'; + if ( ! is_null($database)) { + $query .= ' FROM ' . $database; + } + $tableNames = $this->_conn->fetchColumn($query); + + return array_map(array($this->_conn->formatter, 'fixSequenceName'), $tableNames); + } + + /** + * lists table constraints + * + * @param string $table database table name + * @return array + * @override + */ + public function listTableConstraints($table) + { + $keyName = 'Key_name'; + $nonUnique = 'Non_unique'; + if ($this->_conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { + if ($this->_conn->getAttribute(Doctrine::ATTR_FIELD_CASE) == CASE_LOWER) { + $keyName = strtolower($keyName); + $nonUnique = strtolower($nonUnique); + } else { + $keyName = strtoupper($keyName); + $nonUnique = strtoupper($nonUnique); + } + } + + $table = $this->_conn->quoteIdentifier($table, true); + $query = 'SHOW INDEX FROM ' . $table; + $indexes = $this->_conn->fetchAssoc($query); + + $result = array(); + foreach ($indexes as $indexData) { + if ( ! $indexData[$nonUnique]) { + if ($indexData[$keyName] !== 'PRIMARY') { + $index = $this->_conn->formatter->fixIndexName($indexData[$keyName]); + } else { + $index = 'PRIMARY'; + } + if ( ! empty($index)) { + $result[] = $index; + } + } + } + return $result; + } + + /** + * lists table foreign keys + * + * @param string $table database table name + * @return array + * @override + */ + public function listTableForeignKeys($table) + { + $sql = 'SHOW CREATE TABLE ' . $this->_conn->quoteIdentifier($table, true); + $definition = $this->_conn->fetchOne($sql); + if (!empty($definition)) { + $pattern = '/\bCONSTRAINT\s+([^\s]+)\s+FOREIGN KEY\b/i'; + if (preg_match_all($pattern, str_replace('`', '', $definition), $matches) > 1) { + foreach ($matches[1] as $constraint) { + $result[$constraint] = true; + } + } + } + + if ($this->_conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { + $result = array_change_key_case($result, $this->_conn->getAttribute(Doctrine::ATTR_FIELD_CASE)); + } + + return $result; + } + + /** + * lists table constraints + * + * @param string $table database table name + * @return array + * @override + */ + public function listTableColumns($table) + { + $sql = 'DESCRIBE ' . $this->_conn->quoteIdentifier($table, true); + $result = $this->_conn->fetchAssoc($sql); + + $description = array(); + $columns = array(); + foreach ($result as $key => $val) { + + $val = array_change_key_case($val, CASE_LOWER); + + $decl = $this->_conn->dataDict->getPortableDeclaration($val); + + $values = isset($decl['values']) ? $decl['values'] : array(); + + $description = array( + 'name' => $val['field'], + 'type' => $decl['type'][0], + 'alltypes' => $decl['type'], + 'ntype' => $val['type'], + 'length' => $decl['length'], + 'fixed' => $decl['fixed'], + 'unsigned' => $decl['unsigned'], + 'values' => $values, + 'primary' => (strtolower($val['key']) == 'pri'), + 'default' => $val['default'], + 'notnull' => (bool) ($val['null'] != 'YES'), + 'autoincrement' => (bool) (strpos($val['extra'], 'auto_increment') !== false), + ); + $columns[$val['field']] = $description; + } + + return $columns; + } + + /** + * lists table constraints + * + * @param string $table database table name + * @return array + * @override + */ + public function listTableIndexes($table) + { + $keyName = 'Key_name'; + $nonUnique = 'Non_unique'; + if ($this->_conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { + if ($this->_conn->getAttribute(Doctrine::ATTR_FIELD_CASE) == CASE_LOWER) { + $keyName = strtolower($keyName); + $nonUnique = strtolower($nonUnique); + } else { + $keyName = strtoupper($keyName); + $nonUnique = strtoupper($nonUnique); + } + } + + $table = $this->_conn->quoteIdentifier($table, true); + $query = 'SHOW INDEX FROM ' . $table; + $indexes = $this->_conn->fetchAssoc($query); + + + $result = array(); + foreach ($indexes as $indexData) { + if ($indexData[$nonUnique] && ($index = $this->_conn->formatter->fixIndexName($indexData[$keyName]))) { + $result[] = $index; + } + } + return $result; + } + + /** + * lists tables + * + * @param string|null $database + * @return array + * @override + */ + public function listTables($database = null) + { + return $this->_conn->fetchColumn($this->sql['listTables']); + } + + /** + * lists database views + * + * @param string|null $database + * @return array + * @override + */ + public function listViews($database = null) + { + if ( ! is_null($database)) { + $query = sprintf($this->sql['listViews'], ' FROM ' . $database); + } + + return $this->_conn->fetchColumn($query); + } + + /** + * create a new database + * + * @param string $name name of the database that should be created + * @return string + * @override + */ + 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 + * @override + */ + 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 + * @override + */ + 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 '; + if (!empty($options['temporary'])) { + $query .= 'TEMPORARY '; + } + $query.= '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 + * @override + */ + 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 + * @override + */ + 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 . '))'; + + if (!empty($options_strings)) { + $query .= ' '.implode(' ', $options_strings); + } + + $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 { + $res = $this->_conn->exec('DROP TABLE ' . $sequenceName); + } catch(Doctrine_Connection_Exception $e) { + throw new Doctrine_Export_Exception('could not drop inconsistent sequence table'); + } + + return $res; + } + + /** + * 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 + * @override + */ + public function createIndexSql($table, $name, array $definition) + { + $table = $table; + $name = $this->_conn->formatter->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($definition['fields']) . ')'; + + return $query; + } + + /** + * Obtain DBMS specific SQL code portion needed to declare an integer type + * field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param string $field associative array with the name of the properties + * of the field being declared as array indexes. + * Currently, the types of supported field + * properties are as follows: + * + * unsigned + * Boolean flag that indicates whether the field + * should be declared as unsigned integer if + * possible. + * + * default + * Integer value to be used as default for this + * field. + * + * notnull + * Boolean flag that indicates whether this field is + * constrained to not be set to null. + * @return string DBMS specific SQL code portion that should be used to + * declare the specified field. + * @override + */ + public function getIntegerDeclaration($name, $field) + { + $default = $autoinc = ''; + if ( ! empty($field['autoincrement'])) { + $autoinc = ' AUTO_INCREMENT'; + } elseif (array_key_exists('default', $field)) { + if ($field['default'] === '') { + $field['default'] = empty($field['notnull']) ? null : 0; + } + if (is_null($field['default'])) { + $default = ' DEFAULT NULL'; + } else { + $default = ' DEFAULT '.$this->_conn->quote($field['default']); + } + } elseif (empty($field['notnull'])) { + $default = ' DEFAULT NULL'; + } + + $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : ''; + $unsigned = (isset($field['unsigned']) && $field['unsigned']) ? ' UNSIGNED' : ''; + + $name = $this->_conn->quoteIdentifier($name, true); + + return $name . ' ' . $this->_conn->dataDict->getNativeDeclaration($field) . $unsigned . $default . $notnull . $autoinc; + } + + /** + * 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 + * @override + */ + public function getDefaultFieldDeclaration($field) + { + $default = empty($field['notnull']) && !in_array($field['type'], array('clob', 'blob')) + ? ' DEFAULT NULL' : ''; + + if (isset($field['default']) && ( ! isset($field['length']) || $field['length'] <= 255)) { + if ($field['default'] === '') { + $field['default'] = null; + if (! empty($field['notnull']) && array_key_exists($field['type'], $this->valid_default_values)) { + $field['default'] = $this->valid_default_values[$field['type']]; + } + + if ($field['default'] === '' + && ($this->_conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_EMPTY_TO_NULL) + ) { + $field['default'] = ' '; + } + } + + if ($field['type'] == 'enum' && $this->_conn->getAttribute(Doctrine::ATTR_USE_NATIVE_ENUM)) { + $fieldType = 'varchar'; + } else { + if ($field['type'] === 'boolean') { + $fields['default'] = $this->_conn->convertBooleans($field['default']); + } + $fieldType = $field['type']; + } + + $default = ' DEFAULT ' . $this->_conn->quote($field['default'], $fieldType); + } + 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 + * @override + */ + 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 + * @override + */ + 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 + * @override + */ + 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 + * @override + */ + 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 + * @override + */ + public function dropTableSql($table) + { + $table = $this->_conn->quoteIdentifier($table, true); + return 'DROP TABLE ' . $table; + } + + /** + * Enter description here... + * + * @param unknown_type $table + * @param unknown_type $name + * @return unknown + * @override + */ + public function dropForeignKey($table, $name) + { + $table = $this->_conn->quoteIdentifier($table); + $name = $this->_conn->quoteIdentifier($name); + return $this->_conn->exec('ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $name); + } + +} + +?> \ No newline at end of file diff --git a/lib/Doctrine/Schema/OracleSchemaManager.php b/lib/Doctrine/Schema/OracleSchemaManager.php new file mode 100644 index 000000000..688cb06cf --- /dev/null +++ b/lib/Doctrine/Schema/OracleSchemaManager.php @@ -0,0 +1,715 @@ +. + */ + +#namespace Doctrine::DBAL::Schema; + +/** + * xxx + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @version $Revision$ + * @since 2.0 + */ +class Doctrine_Schema_OracleSchemaManager extends Doctrine_Schema_SchemaManager +{ + public function __construct(Doctrine_Connection_Oracle $conn) + { + $this->_conn = $conn; + } + + /** + * 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; + } + + /** + * lists all databases + * + * @return array + */ + public function listDatabases() + { + if ( ! $this->_conn->getAttribute(Doctrine::ATTR_EMULATE_DATABASE)) { + throw new Doctrine_Import_Exception('database listing is only supported if the "emulate_database" option is enabled'); + } + /** + if ($this->_conn->options['database_name_prefix']) { + $query = 'SELECT SUBSTR(username, '; + $query.= (strlen($this->_conn->getAttribute(['database_name_prefix'])+1); + $query.= ") FROM sys.dba_users WHERE username LIKE '"; + $query.= $this->_conn->options['database_name_prefix']."%'"; + } else { + */ + $query = 'SELECT username FROM sys.dba_users'; + + $result2 = $this->_conn->standaloneQuery($query); + $result = $result2->fetchColumn(); + + return $result; + } + + /** + * lists all availible database functions + * + * @return array + */ + public function listFunctions() + { + $query = "SELECT name FROM sys.user_source WHERE line = 1 AND type = 'FUNCTION'"; + + return $this->_conn->fetchColumn($query); + } + + /** + * lists all database triggers + * + * @param string|null $database + * @return array + */ + public function listTriggers($database = null) + { + + } + + /** + * lists all database sequences + * + * @param string|null $database + * @return array + */ + public function listSequences($database = null) + { + $query = "SELECT sequence_name FROM sys.user_sequences"; + + $tableNames = $this->_conn->fetchColumn($query); + + return array_map(array($this->_conn->formatter, 'fixSequenceName'), $tableNames); + } + + /** + * lists table constraints + * + * @param string $table database table name + * @return array + */ + public function listTableConstraints($table) + { + $table = $this->_conn->quote($table, 'text'); + + $query = 'SELECT index_name name FROM user_constraints' + . ' WHERE table_name = ' . $table . ' OR table_name = ' . strtoupper($table); + + $constraints = $this->_conn->fetchColumn($query); + + return array_map(array($this->_conn->formatter, 'fixIndexName'), $constraints); + } + + /** + * lists table constraints + * + * @param string $table database table name + * @return array + */ + public function listTableColumns($table) + { + $table = strtoupper($table); + $sql = "SELECT column_name, data_type, data_length, nullable, data_default, data_scale, data_precision FROM all_tab_columns" + . " WHERE table_name = '" . $table . "' ORDER BY column_name"; + + $result = $this->_conn->fetchAssoc($sql); + + $descr = array(); + + foreach($result as $val) { + $val = array_change_key_case($val, CASE_LOWER); + $decl = $this->_conn->dataDict->getPortableDeclaration($val); + + + $descr[$val['column_name']] = array( + 'name' => $val['column_name'], + 'notnull' => (bool) ($val['nullable'] === 'N'), + 'ntype' => $val['data_type'], + 'type' => $decl['type'][0], + 'alltypes' => $decl['type'], + 'fixed' => $decl['fixed'], + 'unsigned' => $decl['unsigned'], + 'default' => $val['data_default'], + 'length' => $val['data_length'], + 'precision' => $val['data_precision'], + 'scale' => $val['scale'], + ); + } + + return $descr; + } + + /** + * lists table constraints + * + * @param string $table database table name + * @return array + */ + public function listTableIndexes($table) + { + $table = $this->_conn->quote($table, 'text'); + $query = 'SELECT index_name name FROM user_indexes' + . ' WHERE table_name = ' . $table . ' OR table_name = ' . strtoupper($table) + . ' AND generated = ' . $this->_conn->quote('N', 'text'); + + $indexes = $this->_conn->fetchColumn($query); + + return array_map(array($this->_conn->formatter, 'fixIndexName'), $indexes); + } + + /** + * lists tables + * + * @param string|null $database + * @return array + */ + public function listTables($database = null) + { + $query = 'SELECT table_name FROM sys.user_tables'; + return $this->_conn->fetchColumn($query); + } + + /** + * lists table triggers + * + * @param string $table database table name + * @return array + */ + public function listTableTriggers($table) + { + + } + + /** + * lists table views + * + * @param string $table database table name + * @return array + */ + public function listTableViews($table) + { + + } + + /** + * lists database users + * + * @return array + */ + public function listUsers() + { + /** + if ($this->_conn->options['emulate_database'] && $this->_conn->options['database_name_prefix']) { + $query = 'SELECT SUBSTR(username, '; + $query.= (strlen($this->_conn->options['database_name_prefix'])+1); + $query.= ") FROM sys.dba_users WHERE username NOT LIKE '"; + $query.= $this->_conn->options['database_name_prefix']."%'"; + } else { + */ + + $query = 'SELECT username FROM sys.dba_users'; + //} + + return $this->_conn->fetchColumn($query); + } + + /** + * lists database views + * + * @param string|null $database + * @return array + */ + public function listViews($database = null) + { + $query = 'SELECT view_name FROM sys.user_views'; + return $this->_conn->fetchColumn($query); + } + +} + +?> \ No newline at end of file diff --git a/lib/Doctrine/Schema/PostgreSqlSchemaManager.php b/lib/Doctrine/Schema/PostgreSqlSchemaManager.php new file mode 100644 index 000000000..c1f0ec7af --- /dev/null +++ b/lib/Doctrine/Schema/PostgreSqlSchemaManager.php @@ -0,0 +1,636 @@ +. + */ + +#namespace Doctrine::DBAL::Schema; + +/** + * xxx + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @version $Revision$ + * @since 2.0 + */ +class Doctrine_Schema_PostgreSqlSchemaManager extends Doctrine_Schema_SchemaManager +{ + protected $sql = array( + 'listDatabases' => 'SELECT datname FROM pg_database', + 'listFunctions' => "SELECT + proname + FROM + pg_proc pr, + pg_type tp + WHERE + tp.oid = pr.prorettype + AND pr.proisagg = FALSE + AND tp.typname <> 'trigger' + AND pr.pronamespace IN + (SELECT oid FROM pg_namespace + WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema'", + 'listSequences' => "SELECT + relname + FROM + pg_class + WHERE relkind = 'S' AND relnamespace IN + (SELECT oid FROM pg_namespace + WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')", + 'listTables' => "SELECT + c.relname AS table_name + FROM pg_class c, pg_user u + WHERE c.relowner = u.usesysid + AND c.relkind = 'r' + AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) + AND c.relname !~ '^(pg_|sql_)' + UNION + SELECT c.relname AS table_name + FROM pg_class c + WHERE c.relkind = 'r' + AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) + AND NOT EXISTS (SELECT 1 FROM pg_user WHERE usesysid = c.relowner) + AND c.relname !~ '^pg_'", + 'listViews' => 'SELECT viewname FROM pg_views', + 'listUsers' => 'SELECT usename FROM pg_user', + 'listTableConstraints' => "SELECT + relname + FROM + pg_class + WHERE oid IN ( + SELECT indexrelid + FROM pg_index, pg_class + WHERE pg_class.relname = %s + AND pg_class.oid = pg_index.indrelid + AND (indisunique = 't' OR indisprimary = 't') + )", + 'listTableIndexes' => "SELECT + relname + FROM + pg_class + WHERE oid IN ( + SELECT indexrelid + FROM pg_index, pg_class + WHERE pg_class.relname = %s + AND pg_class.oid=pg_index.indrelid + AND indisunique != 't' + AND indisprimary != 't' + )", + 'listTableColumns' => "SELECT + a.attnum, + a.attname AS field, + t.typname AS type, + format_type(a.atttypid, a.atttypmod) AS complete_type, + a.attnotnull AS isnotnull, + (SELECT 't' + FROM pg_index + WHERE c.oid = pg_index.indrelid + AND pg_index.indkey[0] = a.attnum + AND pg_index.indisprimary = 't' + ) AS pri, + (SELECT pg_attrdef.adsrc + FROM pg_attrdef + WHERE c.oid = pg_attrdef.adrelid + AND pg_attrdef.adnum=a.attnum + ) AS default + FROM pg_attribute a, pg_class c, pg_type t + WHERE c.relname = %s + AND a.attnum > 0 + AND a.attrelid = c.oid + AND a.atttypid = t.oid + ORDER BY a.attnum", + ); + + + + public function __construct(Doctrine_Connection_Pgsql $conn) + { + $this->_conn = $conn; + } + + /** + * create a new database + * + * @param string $name name of the database that should be created + * @throws PDOException + * @return void + */ + public function createDatabaseSql($name) + { + $query = 'CREATE DATABASE ' . $this->_conn->quoteIdentifier($name); + + return $query; + } + + /** + * drop an existing database + * + * @param string $name name of the database that should be dropped + * @throws PDOException + * @access public + */ + public function dropDatabaseSql($name) + { + $query = 'DROP DATABASE ' . $this->_conn->quoteIdentifier($name); + + return $query; + } + + /** + * 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; + } + + /** + * generates the sql for altering an existing table on postgresql + * + * @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 * + * @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. + * @see Doctrine_Export::alterTable() + * @return array + */ + public function alterTableSql($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 true; + } + + $sql = array(); + + if (isset($changes['add']) && is_array($changes['add'])) { + foreach ($changes['add'] as $fieldName => $field) { + $query = 'ADD ' . $this->getDeclaration($fieldName, $field); + $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; + } + } + + if (isset($changes['remove']) && is_array($changes['remove'])) { + foreach ($changes['remove'] as $fieldName => $field) { + $fieldName = $this->_conn->quoteIdentifier($fieldName, true); + $query = 'DROP ' . $fieldName; + $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; + } + } + + if (isset($changes['change']) && is_array($changes['change'])) { + foreach ($changes['change'] as $fieldName => $field) { + $fieldName = $this->_conn->quoteIdentifier($fieldName, true); + if (isset($field['type'])) { + $serverInfo = $this->_conn->getServerVersion(); + + if (is_array($serverInfo) && $serverInfo['major'] < 8) { + throw new Doctrine_Export_Exception('changing column type for "'.$field['type'].'\" requires PostgreSQL 8.0 or above'); + } + $query = 'ALTER ' . $fieldName . ' TYPE ' . $this->_conn->datatype->getTypeDeclaration($field['definition']); + $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; + } + if (array_key_exists('default', $field)) { + $query = 'ALTER ' . $fieldName . ' SET DEFAULT ' . $this->_conn->quote($field['definition']['default'], $field['definition']['type']); + $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; + } + if ( ! empty($field['notnull'])) { + $query = 'ALTER ' . $fieldName . ' ' . ($field['definition']['notnull'] ? 'SET' : 'DROP') . ' NOT NULL'; + $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; + } + } + } + + if (isset($changes['rename']) && is_array($changes['rename'])) { + foreach ($changes['rename'] as $fieldName => $field) { + $fieldName = $this->_conn->quoteIdentifier($fieldName, true); + $sql[] = 'ALTER TABLE ' . $name . ' RENAME COLUMN ' . $fieldName . ' TO ' . $this->_conn->quoteIdentifier($field['name'], true); + } + } + + $name = $this->_conn->quoteIdentifier($name, true); + if (isset($changes['name'])) { + $changeName = $this->_conn->quoteIdentifier($changes['name'], true); + $sql[] = 'ALTER TABLE ' . $name . ' RENAME TO ' . $changeName; + } + + 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. + * @throws Doctrine_Connection_Exception + * @return boolean + */ + public function alterTable($name, array $changes, $check = false) + { + $sql = $this->alterTableSql($name, $changes, $check); + foreach ($sql as $query) { + $this->_conn->exec($query); + } + return true; + } + + /** + * return RDBMS specific create sequence statement + * + * @throws Doctrine_Connection_Exception if something fails at database level + * @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($sequenceName, $start = 1, array $options = array()) + { + $sequenceName = $this->_conn->quoteIdentifier($this->_conn->formatter->getSequenceName($sequenceName), true); + return $this->_conn->exec('CREATE SEQUENCE ' . $sequenceName . ' INCREMENT 1' . + ($start < 1 ? ' MINVALUE ' . $start : '') . ' START ' . $start); + } + + /** + * drop existing sequence + * + * @param string $sequenceName name of the sequence to be dropped + */ + public function dropSequenceSql($sequenceName) + { + $sequenceName = $this->_conn->quoteIdentifier($this->_conn->formatter->getSequenceName($sequenceName), true); + return 'DROP SEQUENCE ' . $sequenceName; + } + + /** + * Creates a table. + * + * @param unknown_type $name + * @param array $fields + * @param array $options + * @return unknown + */ + 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); + + + 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 . ')'; + + $sql[] = $query; + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach($options['indexes'] as $index => $definition) { + $sql[] = $this->createIndexSql($name, $index, $definition); + } + } + + if (isset($options['foreignKeys'])) { + + foreach ((array) $options['foreignKeys'] as $k => $definition) { + if (is_array($definition)) { + $sql[] = $this->createForeignKeySql($name, $definition); + } + } + } + + return $sql; + } + + /** + * Obtain DBMS specific SQL code portion needed to declare an integer type + * field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param array $field associative array with the name of the properties + * of the field being declared as array indexes. Currently, the types + * of supported field properties are as follows: + * + * unsigned + * Boolean flag that indicates whether the field should be + * declared as unsigned integer if possible. + * + * default + * Integer value to be used as default for this field. + * + * notnull + * Boolean flag that indicates whether this field is constrained + * to not be set to null. + * @return string DBMS specific SQL code portion that should be used to + * declare the specified field. + */ + public function getIntegerDeclaration($name, $field) + { + /** + if ( ! empty($field['unsigned'])) { + $this->_conn->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer"; + } + */ + + if ( ! empty($field['autoincrement'])) { + $name = $this->_conn->quoteIdentifier($name, true); + return $name . ' ' . $this->_conn->dataDict->getNativeDeclaration($field); + } + + $default = ''; + if (array_key_exists('default', $field)) { + if ($field['default'] === '') { + $field['default'] = empty($field['notnull']) ? null : 0; + } + $default = ' DEFAULT '.$this->_conn->quote($field['default'], $field['type']); + } elseif (empty($field['notnull'])) { + $default = ' DEFAULT NULL'; + } + + $notnull = empty($field['notnull']) ? '' : ' NOT NULL'; + $name = $this->_conn->quoteIdentifier($name, true); + return $name . ' ' . $this->_conn->dataDict->getNativeDeclaration($field) . $default . $notnull; + } + + /** + * lists all database triggers + * + * @param string|null $database + * @return array + */ + public function listTriggers($database = null) + { + + } + + /** + * lists table constraints + * + * @param string $table database table name + * @return array + */ + public function listTableConstraints($table) + { + $table = $this->conn->quote($table); + $query = sprintf($this->sql['listTableConstraints'], $table); + + return $this->conn->fetchColumn($query); + } + + /** + * lists table constraints + * + * @param string $table database table name + * @return array + */ + public function listTableColumns($table) + { + $table = $this->conn->quote($table); + $query = sprintf($this->sql['listTableColumns'], $table); + $result = $this->conn->fetchAssoc($query); + + $columns = array(); + foreach ($result as $key => $val) { + $val = array_change_key_case($val, CASE_LOWER); + + if (strtolower($val['type']) === 'varchar') { + // get length from varchar definition + $length = preg_replace('~.*\(([0-9]*)\).*~', '$1', $val['complete_type']); + $val['length'] = $length; + } + + $decl = $this->_conn->dataDict->getPortableDeclaration($val); + + $description = array( + 'name' => $val['field'], + 'ntype' => $val['type'], + 'type' => $decl['type'][0], + 'alltypes' => $decl['type'], + 'length' => $decl['length'], + 'fixed' => $decl['fixed'], + 'unsigned' => $decl['unsigned'], + 'notnull' => ($val['isnotnull'] == true), + 'default' => $val['default'], + 'primary' => ($val['pri'] == 't'), + ); + + $matches = array(); + + if (preg_match("/^nextval\('(.*)'(::.*)?\)$/", $description['default'], $matches)) { + + $description['sequence'] = $this->_conn->formatter->fixSequenceName($matches[1]); + $description['default'] = null; + } + + $columns[$val['field']] = $description; + } + + return $columns; + } + + /** + * list all indexes in a table + * + * @param string $table database table name + * @return array + */ + public function listTableIndexes($table) + { + $table = $this->_conn->quote($table); + $query = sprintf($this->sql['listTableIndexes'], $table); + + return $this->_conn->fetchColumn($query); + } + + /** + * lists tables + * + * @param string|null $database + * @return array + */ + public function listTables($database = null) + { + return $this->_conn->fetchColumn($this->sql['listTables']); + } + + /** + * lists table triggers + * + * @param string $table database table name + * @return array + */ + public function listTableTriggers($table) + { + $query = 'SELECT trg.tgname AS trigger_name + FROM pg_trigger trg, + pg_class tbl + WHERE trg.tgrelid = tbl.oid'; + if ($table !== null) { + $table = $this->_conn->quote(strtoupper($table), 'string'); + $query .= " AND tbl.relname = $table"; + } + return $this->_conn->fetchColumn($query); + } + + /** + * list the views in the database that reference a given table + * + * @param string $table database table name + * @return array + */ + public function listTableViews($table) + { + return $this->_conn->fetchColumn($query); + } +} + +?> \ No newline at end of file diff --git a/lib/Doctrine/Schema/SchemaManager.php b/lib/Doctrine/Schema/SchemaManager.php new file mode 100644 index 000000000..3f1e7e8fd --- /dev/null +++ b/lib/Doctrine/Schema/SchemaManager.php @@ -0,0 +1,1189 @@ +. + */ + +#namespace Doctrine::DBAL::Schema; + +/** + * xxx + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @version $Revision$ + * @since 2.0 + */ +abstract class Doctrine_Schema_SchemaManager +{ + protected $_conn; + protected $sql = array(); + protected $valid_default_values = array( + 'text' => '', + 'boolean' => true, + 'integer' => 0, + 'decimal' => 0.0, + 'float' => 0.0, + 'timestamp' => '1970-01-01 00:00:00', + 'time' => '00:00:00', + 'date' => '1970-01-01', + 'clob' => '', + 'blob' => '', + 'string' => '' + ); + + /** + * lists all databases + * + * @return array + */ + public function listDatabases() + { + if ( ! isset($this->sql['listDatabases'])) { + throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); + } + + return $this->_conn->fetchColumn($this->sql['listDatabases']); + } + + /** + * lists all availible database functions + * + * @return array + */ + public function listFunctions() + { + if ( ! isset($this->sql['listFunctions'])) { + throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); + } + + return $this->_conn->fetchColumn($this->sql['listFunctions']); + } + + /** + * Lists all database triggers. + * + * @param string|null $database + * @return array + */ + public function listTriggers($database = null) + { + throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); + } + + /** + * lists all database sequences + * + * @param string|null $database + * @return array + */ + public function listSequences($database = null) + { + if ( ! isset($this->_sql['listSequences'])) { + throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); + } + + return $this->_conn->fetchColumn($this->sql['listSequences']); + } + + /** + * Lists table constraints. + * + * @param string $table database table name + * @return array + */ + public function listTableConstraints($table) + { + throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); + } + + /** + * Lists table columns. + * + * @param string $table database table name + * @return array + */ + public function listTableColumns($table) + { + throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); + } + + /** + * Lists table indexes. + * + * @param string $table database table name + * @return array + */ + public function listTableIndexes($table) + { + throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); + } + + /** + * Lists tables. + * + * @param string|null $database + * @return array + */ + public function listTables($database = null) + { + throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); + } + + /** + * Lists table triggers. + * + * @param string $table database table name + * @return array + */ + public function listTableTriggers($table) + { + throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); + } + + /** + * Lists table views. + * + * @param string $table database table name + * @return array + */ + public function listTableViews($table) + { + throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); + } + + /** + * Lists database users. + * + * @return array + */ + public function listUsers() + { + if ( ! isset($this->_sql['listUsers'])) { + throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); + } + + return $this->_conn->fetchColumn($this->sql['listUsers']); + } + + /** + * Lists database views. + * + * @param string|null $database + * @return array + */ + public function listViews($database = null) + { + if ( ! isset($this->_sql['listViews'])) { + throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.'); + } + + return $this->_conn->fetchColumn($this->sql['listViews']); + } + + /** + * drop an existing database + * (this method is implemented by the drivers) + * + * @param string $name name of the database that should be dropped + * @return void + */ + public function dropDatabase($database) + { + $this->_conn->execute($this->dropDatabaseSql($database)); + } + + /** + * drop an existing database + * (this method is implemented by the drivers) + * + * @param string $name name of the database that should be dropped + * @return void + */ + public function dropDatabaseSql($database) + { + throw new Doctrine_Export_Exception('Drop database not supported by this driver.'); + } + + /** + * dropTableSql + * drop an existing table + * + * @param string $table name of table that should be dropped from the database + * @return string + */ + public function dropTableSql($table) + { + return 'DROP TABLE ' . $this->conn->quoteIdentifier($table); + } + + /** + * dropTable + * drop an existing table + * + * @param string $table name of table that should be dropped from the database + * @return void + */ + public function dropTable($table) + { + $this->_conn->execute($this->dropTableSql($table)); + } + + /** + * 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 dropIndex($table, $name) + { + return $this->_conn->exec($this->dropIndexSql($table, $name)); + } + + /** + * dropIndexSql + * + * @param string $table name of table that should be used in method + * @param string $name name of the index to be dropped + * @return string SQL that is used for dropping an index + */ + public function dropIndexSql($table, $name) + { + $name = $this->_conn->quoteIdentifier($this->conn->formatter->getIndexName($name)); + + return 'DROP INDEX ' . $name; + } + + /** + * drop existing constraint + * + * @param string $table name of table that should be used in method + * @param string $name name of the constraint to be dropped + * @param string $primary hint if the constraint is primary + * @return void + */ + public function dropConstraint($table, $name, $primary = false) + { + $table = $this->_conn->quoteIdentifier($table); + $name = $this->_conn->quoteIdentifier($name); + + return $this->_conn->exec('ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $name); + } + + /** + * drop existing foreign key + * + * @param string $table name of table that should be used in method + * @param string $name name of the foreign key to be dropped + * @return void + */ + public function dropForeignKey($table, $name) + { + return $this->dropConstraint($table, $name); + } + + /** + * dropSequenceSql + * drop existing sequence + * (this method is implemented by the drivers) + * + * @throws Doctrine_Connection_Exception if something fails at database level + * @param string $sequenceName name of the sequence to be dropped + * @return void + */ + public function dropSequence($sequenceName) + { + $this->_conn->exec($this->dropSequenceSql($sequenceName)); + } + + /** + * dropSequenceSql + * drop existing sequence + * + * @throws Doctrine_Connection_Exception if something fails at database level + * @param string $sequenceName name of the sequence to be dropped + * @return void + */ + public function dropSequenceSql($sequenceName) + { + throw new Doctrine_Export_Exception('Drop sequence not supported by this driver.'); + } + + /** + * create a new database + * (this method is implemented by the drivers) + * + * @param string $name name of the database that should be created + * @return void + */ + public function createDatabase($database) + { + $this->_conn->execute($this->createDatabaseSql($database)); + } + + /** + * create a new database + * (this method is implemented by the drivers) + * + * @param string $name name of the database that should be created + * @return string + */ + public function createDatabaseSql($database) + { + throw new Doctrine_Export_Exception('Create database not supported by this driver.'); + } + + /** + * 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 string + */ + 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); + + + if (isset($options['primary']) && ! empty($options['primary'])) { + $queryFields .= ', PRIMARY KEY(' . implode(', ', array_values($options['primary'])) . ')'; + } + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach($options['indexes'] as $index => $definition) { + $queryFields .= ', ' . $this->getIndexDeclaration($index, $definition); + } + } + + $query = 'CREATE TABLE ' . $this->conn->quoteIdentifier($name, true) . ' (' . $queryFields; + + $check = $this->getCheckDeclaration($fields); + + if ( ! empty($check)) { + $query .= ', ' . $check; + } + + $query .= ')'; + + $sql[] = $query; + + if (isset($options['foreignKeys'])) { + + foreach ((array) $options['foreignKeys'] as $k => $definition) { + if (is_array($definition)) { + $sql[] = $this->createForeignKeySql($name, $definition); + } + } + } + return $sql; + } + + /** + * 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 + * @param array $options An associative array of table options: + * @see Doctrine_Export::createTableSql() + * + * @return void + */ + public function createTable($name, array $fields, array $options = array()) + { + // Build array of the primary keys if any of the individual field definitions + // specify primary => true + $count = 0; + foreach ($fields as $fieldName => $field) { + if (isset($field['primary']) && $field['primary']) { + if ($count == 0) { + $options['primary'] = array(); + } + $count++; + $options['primary'][] = $fieldName; + } + } + + $sql = (array) $this->createTableSql($name, $fields, $options); + + foreach ($sql as $query) { + $this->_conn->execute($query); + } + } + + /** + * create sequence + * + * @throws Doctrine_Connection_Exception if something fails at database level + * @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($seqName, $start = 1, array $options = array()) + { + return $this->_conn->execute($this->createSequenceSql($seqName, $start = 1, $options)); + } + + /** + * return RDBMS specific create sequence statement + * (this method is implemented by the drivers) + * + * @throws Doctrine_Connection_Exception if something fails at database level + * @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()) + { + throw new Doctrine_Export_Exception('Create sequence not supported by this driver.'); + } + + /** + * 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) + { + $sql = $this->createConstraintSql($table, $name, $definition); + + return $this->_conn->exec($sql); + } + + /** + * 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 createConstraintSql($table, $name, $definition) + { + $table = $this->_conn->quoteIdentifier($table); + $name = $this->_conn->quoteIdentifier($this->conn->formatter->getIndexName($name)); + $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $name; + + if (isset($definition['primary']) && $definition['primary']) { + $query .= ' PRIMARY KEY'; + } elseif (isset($definition['unique']) && $definition['unique']) { + $query .= ' UNIQUE'; + } + + $fields = array(); + foreach (array_keys($definition['fields']) as $field) { + $fields[] = $this->_conn->quoteIdentifier($field, true); + } + $query .= ' ('. implode(', ', $fields) . ')'; + + return $query; + } + + /** + * 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 supports() 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 createIndex($table, $name, array $definition) + { + return $this->_conn->execute($this->createIndexSql($table, $name, $definition)); + } + + /** + * 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. + * @see Doctrine_Export::createIndex() + * @return string + */ + public function createIndexSql($table, $name, array $definition) + { + $table = $this->_conn->quoteIdentifier($table); + $name = $this->_conn->quoteIdentifier($name); + $type = ''; + + if (isset($definition['type'])) { + switch (strtolower($definition['type'])) { + 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; + + $fields = array(); + foreach ($definition['fields'] as $field) { + $fields[] = $this->_conn->quoteIdentifier($field); + } + $query .= ' (' . implode(', ', $fields) . ')'; + + return $query; + } + /** + * createForeignKeySql + * + * @param string $table name of the table on which the foreign key is to be created + * @param array $definition associative array that defines properties of the foreign key to be created. + * @return string + */ + public function createForeignKeySql($table, array $definition) + { + $table = $this->_conn->quoteIdentifier($table); + + $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclaration($definition); + + return $query; + } + + /** + * createForeignKey + * + * @param string $table name of the table on which the foreign key is to be created + * @param array $definition associative array that defines properties of the foreign key to be created. + * @return string + */ + public function createForeignKey($table, array $definition) + { + $sql = $this->createForeignKeySql($table, $definition); + + return $this->_conn->execute($sql); + } + + /** + * alter an existing table + * (this method is implemented by the drivers) + * + * @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) + { + $sql = $this->alterTableSql($name, $changes, $check); + + if (is_string($sql) && $sql) { + $this->_conn->execute($sql); + } + } + + /** + * generates the sql for altering an existing table + * (this method is implemented by the drivers) + * + * @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 * + * @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. + * @see Doctrine_Export::alterTable() + * @return string + */ + public function alterTableSql($name, array $changes, $check = false) + { + throw new Doctrine_Export_Exception('Alter table not supported by this driver.'); + } + + /** + * Get declaration of a number of field in bulk + * + * @param array $fields a multidimensional associative array. + * The first dimension determines the field name, while the second + * dimension is keyed with the name of the properties + * of the field being declared as array indexes. Currently, the types + * of supported field properties are as follows: + * + * length + * Integer value that determines the maximum length of the text + * field. If this argument is missing the field should be + * declared to have the longest length allowed by the DBMS. + * + * default + * Text value to be used as default for this field. + * + * notnull + * Boolean flag that indicates whether this field is constrained + * to not be set to null. + * charset + * Text value with the default CHARACTER SET for this field. + * collation + * Text value with the default COLLATION for this field. + * unique + * unique constraint + * + * @return string + */ + public function getFieldDeclarationList(array $fields) + { + foreach ($fields as $fieldName => $field) { + $query = $this->getDeclaration($fieldName, $field); + + $queryFields[] = $query; + } + return implode(', ', $queryFields); + } + + /** + * Obtain DBMS specific SQL code portion needed to declare a generic type + * field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param array $field associative array with the name of the properties + * of the field being declared as array indexes. Currently, the types + * of supported field properties are as follows: + * + * length + * Integer value that determines the maximum length of the text + * field. If this argument is missing the field should be + * declared to have the longest length allowed by the DBMS. + * + * default + * Text value to be used as default for this field. + * + * notnull + * Boolean flag that indicates whether this field is constrained + * to not be set to null. + * charset + * Text value with the default CHARACTER SET for this field. + * collation + * Text value with the default COLLATION for this field. + * unique + * unique constraint + * check + * column check constraint + * + * @return string DBMS specific SQL code portion that should be used to + * declare the specified field. + */ + public function getDeclaration($name, array $field) + { + + $default = $this->getDefaultFieldDeclaration($field); + + $charset = (isset($field['charset']) && $field['charset']) ? + ' ' . $this->getCharsetFieldDeclaration($field['charset']) : ''; + + $collation = (isset($field['collation']) && $field['collation']) ? + ' ' . $this->getCollationFieldDeclaration($field['collation']) : ''; + + $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : ''; + + $unique = (isset($field['unique']) && $field['unique']) ? + ' ' . $this->getUniqueFieldDeclaration() : ''; + + $check = (isset($field['check']) && $field['check']) ? + ' ' . $field['check'] : ''; + + $method = 'get' . $field['type'] . 'Declaration'; + + if (method_exists($this->conn->dataDict, $method)) { + return $this->_conn->dataDict->$method($name, $field); + } else { + $dec = $this->_conn->dataDict->getNativeDeclaration($field); + } + return $this->_conn->quoteIdentifier($name, true) . ' ' . $dec . $charset . $default . $notnull . $unique . $check . $collation; + } + + /** + * 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'])) { + 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'] = null; + } + } + + if ($field['type'] === 'boolean') { + $field['default'] = $this->_conn->convertBooleans($field['default']); + } + $default = ' DEFAULT ' . $this->_conn->quote($field['default'], $field['type']); + } + return $default; + } + + /** + * Obtain DBMS specific SQL code portion needed to set a CHECK constraint + * declaration to be used in statements like CREATE TABLE. + * + * @param array $definition check definition + * @return string DBMS specific SQL code portion needed to set a CHECK constraint + */ + public function getCheckDeclaration(array $definition) + { + $constraints = array(); + foreach ($definition as $field => $def) { + if (is_string($def)) { + $constraints[] = 'CHECK (' . $def . ')'; + } else { + if (isset($def['min'])) { + $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')'; + } + + if (isset($def['max'])) { + $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')'; + } + } + } + + return implode(', ', $constraints); + } + + /** + * Obtain DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @param string $name 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->quoteIdentifier($name); + $type = ''; + + if (isset($definition['type'])) { + if (strtolower($definition['type']) == 'unique') { + $type = strtoupper($definition['type']) . ' '; + } else { + throw new Doctrine_Export_Exception('Unknown index type ' . $definition['type']); + } + } + + if ( ! isset($definition['fields']) || ! is_array($definition['fields'])) { + throw new Doctrine_Export_Exception('No index columns given.'); + } + + $query = $type . 'INDEX ' . $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) + { + $ret = array(); + foreach ($fields as $field => $definition) { + if (is_array($definition)) { + $ret[] = $this->_conn->quoteIdentifier($field); + } else { + $ret[] = $this->_conn->quoteIdentifier($definition); + } + } + return implode(', ', $ret); + } + + /** + * A method to return the required SQL string that fits between CREATE ... TABLE + * to create the table as a temporary table. + * + * Should be overridden in driver classes to return the correct string for the + * specific database type. + * + * The default is to return the string "TEMPORARY" - this will result in a + * SQL error for any database that does not support temporary tables, or that + * requires a different SQL command from "CREATE 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 'TEMPORARY'; + } + + /** + * getForeignKeyDeclaration + * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param array $definition an associative array with the following structure: + * name optional constraint name + * + * local the local field(s) + * + * foreign the foreign reference field(s) + * + * foreignTable the name of the foreign table + * + * onDelete referential delete action + * + * onUpdate referential update action + * + * deferred deferred constraint checking + * + * The onDelete and onUpdate keys accept the following values: + * + * CASCADE: Delete or update the row from the parent table and automatically delete or + * update the matching rows in the child table. Both ON DELETE CASCADE and ON UPDATE CASCADE are supported. + * Between two tables, you should not define several ON UPDATE CASCADE clauses that act on the same column + * in the parent table or in the child table. + * + * SET NULL: Delete or update the row from the parent table and set the foreign key column or columns in the + * child table to NULL. This is valid only if the foreign key columns do not have the NOT NULL qualifier + * specified. Both ON DELETE SET NULL and ON UPDATE SET NULL clauses are supported. + * + * NO ACTION: In standard SQL, NO ACTION means no action in the sense that an attempt to delete or update a primary + * key value is not allowed to proceed if there is a related foreign key value in the referenced table. + * + * RESTRICT: Rejects the delete or update operation for the parent table. NO ACTION and RESTRICT are the same as + * omitting the ON DELETE or ON UPDATE clause. + * + * SET DEFAULT + * + * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration. + */ + public function getForeignKeyDeclaration(array $definition) + { + $sql = $this->getForeignKeyBaseDeclaration($definition); + $sql .= $this->getAdvancedForeignKeyOptions($definition); + + return $sql; + } + + /** + * 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 + */ + public function getAdvancedForeignKeyOptions(array $definition) + { + $query = ''; + if ( ! empty($definition['onUpdate'])) { + $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialAction($definition['onUpdate']); + } + if ( ! empty($definition['onDelete'])) { + $query .= ' ON DELETE ' . $this->getForeignKeyReferentialAction($definition['onDelete']); + } + return $query; + } + + /** + * getForeignKeyReferentialAction + * + * returns given referential action in uppercase if valid, otherwise throws + * an exception + * + * @throws Doctrine_Exception_Exception if unknown referential action given + * @param string $action foreign key referential action + * @param string foreign key referential action in uppercase + */ + public function getForeignKeyReferentialAction($action) + { + $upper = strtoupper($action); + switch ($upper) { + case 'CASCADE': + case 'SET NULL': + case 'NO ACTION': + case 'RESTRICT': + case 'SET DEFAULT': + return $upper; + break; + default: + throw new Doctrine_Export_Exception('Unknown foreign key referential action \'' . $upper . '\' given.'); + } + } + + /** + * getForeignKeyBaseDeclaration + * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param array $definition + * @return string + */ + public function getForeignKeyBaseDeclaration(array $definition) + { + $sql = ''; + if (isset($definition['name'])) { + $sql .= ' CONSTRAINT ' . $this->conn->quoteIdentifier($definition['name']) . ' '; + } + $sql .= 'FOREIGN KEY ('; + + if ( ! isset($definition['local'])) { + throw new Doctrine_Export_Exception('Local reference field missing from definition.'); + } + if ( ! isset($definition['foreign'])) { + throw new Doctrine_Export_Exception('Foreign reference field missing from definition.'); + } + if ( ! isset($definition['foreignTable'])) { + throw new Doctrine_Export_Exception('Foreign reference table missing from definition.'); + } + + if ( ! is_array($definition['local'])) { + $definition['local'] = array($definition['local']); + } + if ( ! is_array($definition['foreign'])) { + $definition['foreign'] = array($definition['foreign']); + } + + $sql .= implode(', ', array_map(array($this->conn, 'quoteIdentifier'), $definition['local'])) + . ') REFERENCES ' + . $this->_conn->quoteIdentifier($definition['foreignTable']) . '(' + . implode(', ', array_map(array($this->conn, 'quoteIdentifier'), $definition['foreign'])) . ')'; + + return $sql; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the UNIQUE constraint + * of a field declaration to be used in statements like CREATE TABLE. + * + * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint + * of a field declaration. + */ + public function getUniqueFieldDeclaration() + { + return 'UNIQUE'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param string $charset name of the charset + * @return string DBMS specific SQL code portion needed to set the CHARACTER SET + * of a field declaration. + */ + public function getCharsetFieldDeclaration($charset) + { + return ''; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param string $collation name of the collation + * @return string DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration. + */ + public function getCollationFieldDeclaration($collation) + { + return ''; + } + +} + +?> \ No newline at end of file diff --git a/lib/Doctrine/Schema/SqliteSchemaManager.php b/lib/Doctrine/Schema/SqliteSchemaManager.php new file mode 100644 index 000000000..484ecd714 --- /dev/null +++ b/lib/Doctrine/Schema/SqliteSchemaManager.php @@ -0,0 +1,714 @@ +. + */ + +#namespace Doctrine::DBAL::Schema; + +/** + * xxx + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @version $Revision$ + * @since 2.0 + */ +class Doctrine_Schema_SqliteSchemaManager extends Doctrine_Schema_SchemaManager +{ + public function __construct(Doctrine_Connection_Sqlite $conn) + { + $this->_conn = $conn; + } + + /** + * lists all databases + * + * @return array + */ + public function listDatabases() + { + + } + + /** + * lists all availible database functions + * + * @return array + */ + public function listFunctions() + { + + } + + /** + * lists all database triggers + * + * @param string|null $database + * @return array + */ + public function listTriggers($database = null) + { + return $this->listTableTriggers(null); + } + + /** + * lists all database sequences + * + * @param string|null $database + * @return array + */ + public function listSequences($database = null) + { + $query = "SELECT name FROM sqlite_master WHERE type='table' AND sql NOT NULL ORDER BY name"; + $tableNames = $this->_conn->fetchColumn($query); + + $result = array(); + foreach ($tableNames as $tableName) { + if ($sqn = $this->_conn->fixSequenceName($tableName, true)) { + $result[] = $sqn; + } + } + if ($this->_conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { + $result = array_map(($this->_conn->getAttribute(Doctrine::ATTR_FIELD_CASE) == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); + } + return $result; + } + + /** + * lists table constraints + * + * @param string $table database table name + * @return array + */ + public function listTableConstraints($table) + { + $table = $this->_conn->quote($table, 'text'); + + $query = "SELECT sql FROM sqlite_master WHERE type='index' AND "; + + if ($this->_conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { + $query .= 'LOWER(tbl_name) = ' . strtolower($table); + } else { + $query .= 'tbl_name = ' . $table; + } + $query .= ' AND sql NOT NULL ORDER BY name'; + $indexes = $this->_conn->fetchColumn($query); + + $result = array(); + foreach ($indexes as $sql) { + if (preg_match("/^create unique index ([^ ]+) on /i", $sql, $tmp)) { + $index = $this->_conn->formatter->fixIndexName($tmp[1]); + if ( ! empty($index)) { + $result[$index] = true; + } + } + } + + if ($this->_conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { + $result = array_change_key_case($result, $this->_conn->getAttribute(Doctrine::ATTR_FIELD_CASE)); + } + return array_keys($result); + } + + /** + * lists table constraints + * + * @param string $table database table name + * @return array + */ + public function listTableColumns($table) + { + $sql = 'PRAGMA table_info(' . $table . ')'; + $result = $this->_conn->fetchAll($sql); + + $description = array(); + $columns = array(); + foreach ($result as $key => $val) { + $val = array_change_key_case($val, CASE_LOWER); + $decl = $this->_conn->dataDict->getPortableDeclaration($val); + + $description = array( + 'name' => $val['name'], + 'ntype' => $val['type'], + 'type' => $decl['type'][0], + 'alltypes' => $decl['type'], + 'notnull' => (bool) $val['notnull'], + 'default' => $val['dflt_value'], + 'primary' => (bool) $val['pk'], + 'length' => null, + 'scale' => null, + 'precision' => null, + 'unsigned' => null, + ); + $columns[$val['name']] = $description; + } + return $columns; + } + + /** + * lists table constraints + * + * @param string $table database table name + * @return array + */ + public function listTableIndexes($table) + { + $sql = 'PRAGMA index_list(' . $table . ')'; + return $this->_conn->fetchColumn($sql); + } + /** + * lists tables + * + * @param string|null $database + * @return array + */ + public function listTables($database = null) + { + $sql = "SELECT name FROM sqlite_master WHERE type = 'table' " + . "UNION ALL SELECT name FROM sqlite_temp_master " + . "WHERE type = 'table' ORDER BY name"; + + return $this->_conn->fetchColumn($sql); + } + + /** + * lists table triggers + * + * @param string $table database table name + * @return array + */ + public function listTableTriggers($table) + { + $query = "SELECT name FROM sqlite_master WHERE type='trigger' AND sql NOT NULL"; + if (!is_null($table)) { + if ($this->_conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { + if ($this->_conn->getAttribute(Doctrine::ATTR_FIELD_CASE) == CASE_LOWER) { + $query.= ' AND LOWER(tbl_name)='.$db->quote(strtolower($table), 'text'); + } else { + $query.= ' AND UPPER(tbl_name)='.$db->quote(strtoupper($table), 'text'); + } + } else { + $query.= ' AND tbl_name='.$db->quote($table, 'text'); + } + } + $result = $this->_conn->fetchColumn($query); + + if ($this->_conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) { + $result = array_map(($this->_conn->getAttribute(Doctrine::ATTR_FIELD_CASE) == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); + } + return $result; + } + + /** + * lists table views + * + * @param string $table database table name + * @return array + */ + public function listTableViews($table) + { + $query = "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL"; + $views = $db->fetchAll($query); + + $result = array(); + foreach ($views as $row) { + if (preg_match("/^create view .* \bfrom\b\s+\b{$table}\b /i", $row['sql'])) { + if ( ! empty($row['name'])) { + $result[$row['name']] = true; + } + } + } + return $result; + } + + /** + * lists database users + * + * @return array + */ + public function listUsers() + { + + } + + /** + * lists database views + * + * @param string|null $database + * @return array + */ + public function listViews($database = null) + { + $query = "SELECT name FROM sqlite_master WHERE type='view' AND sql NOT NULL"; + + return $this->_conn->fetchColumn($query); + } + + /** + * Drops an existing database + * + * @param string $databaseFile Path 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($databaseFile) + { + 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'); + } + } + + /** + * createDatabase + * + * Create sqlite database file + * + * @param string $databaseFile Path of the database that should be dropped + * @return void + */ + public function createDatabase($databaseFile) + { + return new PDO('sqlite:' . $databaseFile); + } + + /** + * 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; + } + + /** + * Obtain DBMS specific SQL code portion needed to declare an integer type + * field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param array $field associative array with the name of the properties + * of the field being declared as array indexes. + * Currently, the types of supported field + * properties are as follows: + * + * unsigned + * Boolean flag that indicates whether the field + * should be declared as unsigned integer if + * possible. + * + * default + * Integer value to be used as default for this + * field. + * + * notnull + * Boolean flag that indicates whether this field is + * constrained to not be set to null. + * @return string DBMS specific SQL code portion that should be used to + * declare the specified field. + * @access protected + */ + public function getIntegerDeclaration($name, array $field) + { + $default = $autoinc = ''; + $type = $this->_conn->dataDict->getNativeDeclaration($field); + + $autoincrement = isset($field['autoincrement']) && $field['autoincrement']; + + if ($autoincrement) { + $autoinc = ' PRIMARY KEY AUTOINCREMENT'; + $type = 'INTEGER'; + } elseif (array_key_exists('default', $field)) { + if ($field['default'] === '') { + $field['default'] = empty($field['notnull']) ? null : 0; + } + $default = ' DEFAULT ' . $this->_conn->quote($field['default'], $field['type']); + } elseif (empty($field['notnull'])) { + $default = ' DEFAULT NULL'; + } + + $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : ''; + + // sqlite does not support unsigned attribute for autoinremented fields + $unsigned = (isset($field['unsigned']) && $field['unsigned'] && !$autoincrement) ? ' UNSIGNED' : ''; + + $name = $this->_conn->quoteIdentifier($name, true); + return $name . ' ' . $type . $unsigned . $default . $notnull . $autoinc; + } +} + +?> \ No newline at end of file diff --git a/lib/Doctrine/Sequence/Mysql.php b/lib/Doctrine/Sequence/Mysql.php index aaf7a510f..c4edbbf35 100644 --- a/lib/Doctrine/Sequence/Mysql.php +++ b/lib/Doctrine/Sequence/Mysql.php @@ -19,13 +19,11 @@ * . */ -#namespace Doctrine::DBAL::Sequences; +#namespace Doctrine::DBAL::Sequencing; /** * Doctrine_Sequence_Mysql * - * @package Doctrine - * @subpackage Sequence * @author Konsta Vesterinen * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.phpdoctrine.org