refactoring.
This commit is contained in:
parent
3cd4fc5542
commit
3e20fc6aba
@ -27,8 +27,6 @@
|
||||
*
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 <roman@code-factory.org>
|
||||
|
@ -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()
|
||||
{
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -18,7 +18,7 @@
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
Doctrine::autoload('Doctrine_Export');
|
||||
|
||||
/**
|
||||
* Doctrine_Export_Frontbase
|
||||
*
|
||||
|
@ -18,7 +18,7 @@
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -18,7 +18,7 @@
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
Doctrine::autoload('Doctrine_Import');
|
||||
|
||||
/**
|
||||
* @package Doctrine
|
||||
* @subpackage Import
|
||||
@ -28,101 +28,10 @@ Doctrine::autoload('Doctrine_Import');
|
||||
* @author Lukas Smith <smith@pooteeweet.org> (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);
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
Doctrine::autoload('Doctrine_Import');
|
||||
|
||||
/**
|
||||
* @package Doctrine
|
||||
* @subpackage Import
|
||||
@ -27,29 +27,11 @@ Doctrine::autoload('Doctrine_Import');
|
||||
* @author Lukas Smith <smith@pooteeweet.org> (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",
|
||||
);
|
||||
|
||||
|
||||
}
|
@ -18,7 +18,7 @@
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
Doctrine::autoload('Doctrine_Import');
|
||||
|
||||
/**
|
||||
* @package Doctrine
|
||||
* @subpackage Import
|
||||
@ -26,214 +26,10 @@ Doctrine::autoload('Doctrine_Import');
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
Doctrine::autoload('Doctrine_Import');
|
||||
|
||||
/**
|
||||
* @package Doctrine
|
||||
* @subpackage Import
|
||||
@ -28,224 +28,12 @@ Doctrine::autoload('Doctrine_Import');
|
||||
* @author Lukas Smith <smith@pooteeweet.org> (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);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -18,7 +18,7 @@
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
652
lib/Doctrine/Schema/FirebirdSchemaManager.php
Normal file
652
lib/Doctrine/Schema/FirebirdSchemaManager.php
Normal file
@ -0,0 +1,652 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
|
||||
#namespace Doctrine::DBAL::Schema;
|
||||
|
||||
/**
|
||||
* xxx
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
|
||||
* @author Lorenzo Alberton <l.alberton@quipo.it> (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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
65
lib/Doctrine/Schema/InformixSchemaManager.php
Normal file
65
lib/Doctrine/Schema/InformixSchemaManager.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
|
||||
#namespace Doctrine::DBAL::Schema;
|
||||
|
||||
/**
|
||||
* xxx
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
|
||||
* @author Lorenzo Alberton <l.alberton@quipo.it> (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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
428
lib/Doctrine/Schema/MsSqlSchemaManager.php
Normal file
428
lib/Doctrine/Schema/MsSqlSchemaManager.php
Normal file
@ -0,0 +1,428 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
|
||||
#namespace Doctrine::DBAL::Schema;
|
||||
|
||||
/**
|
||||
* xxx
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Lukas Smith <smith@pooteeweet.org> (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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
956
lib/Doctrine/Schema/MySqlSchemaManger.php
Normal file
956
lib/Doctrine/Schema/MySqlSchemaManger.php
Normal file
@ -0,0 +1,956 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
|
||||
#namespace Doctrine::DBAL::Schema;
|
||||
|
||||
/**
|
||||
* xxx
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Lukas Smith <smith@pooteeweet.org> (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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
715
lib/Doctrine/Schema/OracleSchemaManager.php
Normal file
715
lib/Doctrine/Schema/OracleSchemaManager.php
Normal file
@ -0,0 +1,715 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
|
||||
#namespace Doctrine::DBAL::Schema;
|
||||
|
||||
/**
|
||||
* xxx
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Lukas Smith <smith@pooteeweet.org> (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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
636
lib/Doctrine/Schema/PostgreSqlSchemaManager.php
Normal file
636
lib/Doctrine/Schema/PostgreSqlSchemaManager.php
Normal file
@ -0,0 +1,636 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
|
||||
#namespace Doctrine::DBAL::Schema;
|
||||
|
||||
/**
|
||||
* xxx
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Lukas Smith <smith@pooteeweet.org> (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);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
1189
lib/Doctrine/Schema/SchemaManager.php
Normal file
1189
lib/Doctrine/Schema/SchemaManager.php
Normal file
File diff suppressed because it is too large
Load Diff
714
lib/Doctrine/Schema/SqliteSchemaManager.php
Normal file
714
lib/Doctrine/Schema/SqliteSchemaManager.php
Normal file
@ -0,0 +1,714 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
|
||||
#namespace Doctrine::DBAL::Schema;
|
||||
|
||||
/**
|
||||
* xxx
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Lukas Smith <smith@pooteeweet.org> (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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -19,13 +19,11 @@
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
|
||||
#namespace Doctrine::DBAL::Sequences;
|
||||
#namespace Doctrine::DBAL::Sequencing;
|
||||
|
||||
/**
|
||||
* Doctrine_Sequence_Mysql
|
||||
*
|
||||
* @package Doctrine
|
||||
* @subpackage Sequence
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.phpdoctrine.org
|
||||
|
Loading…
Reference in New Issue
Block a user