updated-packages

This commit is contained in:
RafficMohammed
2023-01-08 00:13:22 +05:30
parent 3ff7df7487
commit da241bacb6
12659 changed files with 563377 additions and 510538 deletions

View File

@@ -3,6 +3,7 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use function array_map;
use function crc32;
use function dechex;
@@ -23,14 +24,14 @@ use function substr;
abstract class AbstractAsset
{
/** @var string */
protected $_name;
protected $_name = '';
/**
* Namespace of the asset. If none isset the default namespace is assumed.
*
* @var string|null
*/
protected $_namespace = null;
protected $_namespace;
/** @var bool */
protected $_quoted = false;
@@ -48,11 +49,13 @@ abstract class AbstractAsset
$this->_quoted = true;
$name = $this->trimQuotes($name);
}
if (strpos($name, '.') !== false) {
$parts = explode('.', $name);
$this->_namespace = $parts[0];
$name = $parts[1];
}
$this->_name = $name;
}
@@ -84,7 +87,7 @@ abstract class AbstractAsset
* The shortest name is stripped of the default namespace. All other
* namespaced elements are returned as full-qualified names.
*
* @param string $defaultNamespaceName
* @param string|null $defaultNamespaceName
*
* @return string
*/
@@ -99,7 +102,7 @@ abstract class AbstractAsset
}
/**
* The normalized name is full-qualified and lowerspaced. Lowerspacing is
* The normalized name is full-qualified and lower-cased. Lower-casing is
* actually wrong, but we have to do it to keep our sanity. If you are
* using database objects that only differentiate in the casing (FOO vs
* Foo) then you will NOT be able to use Doctrine Schema abstraction.

View File

@@ -4,20 +4,24 @@ namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\ConnectionException;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs;
use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs;
use Doctrine\DBAL\Events;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\Deprecations\Deprecation;
use Throwable;
use function array_filter;
use function array_intersect;
use function array_map;
use function array_values;
use function assert;
use function call_user_func_array;
use function count;
use function func_get_args;
use function is_array;
use function is_callable;
use function is_string;
use function preg_match;
use function str_replace;
use function strtolower;
@@ -80,8 +84,11 @@ abstract class AbstractSchemaManager
unset($args[0]);
$args = array_values($args);
$callback = [$this, $method];
assert(is_callable($callback));
try {
return call_user_func_array([$this, $method], $args);
return call_user_func_array($callback, $args);
} catch (Throwable $e) {
return false;
}
@@ -96,7 +103,7 @@ abstract class AbstractSchemaManager
{
$sql = $this->_platform->getListDatabasesSQL();
$databases = $this->_conn->fetchAll($sql);
$databases = $this->_conn->fetchAllAssociative($sql);
return $this->_getPortableDatabasesList($databases);
}
@@ -110,7 +117,7 @@ abstract class AbstractSchemaManager
{
$sql = $this->_platform->getListNamespacesSQL();
$namespaces = $this->_conn->fetchAll($sql);
$namespaces = $this->_conn->fetchAllAssociative($sql);
return $this->getPortableNamespacesList($namespaces);
}
@@ -127,9 +134,10 @@ abstract class AbstractSchemaManager
if ($database === null) {
$database = $this->_conn->getDatabase();
}
$sql = $this->_platform->getListSequencesSQL($database);
$sequences = $this->_conn->fetchAll($sql);
$sequences = $this->_conn->fetchAllAssociative($sql);
return $this->filterAssetNames($this->_getPortableSequencesList($sequences));
}
@@ -138,10 +146,10 @@ abstract class AbstractSchemaManager
* Lists the columns for a given table.
*
* In contrast to other libraries and to the old version of Doctrine,
* this column definition does try to contain the 'primary' field for
* this column definition does try to contain the 'primary' column for
* the reason that it is not portable across different RDBMS. Use
* {@see listTableIndexes($tableName)} to retrieve the primary key
* of a table. We're a RDBMS specifies more details these are held
* of a table. Where a RDBMS specifies more details, these are held
* in the platformDetails array.
*
* @param string $table The name of the table.
@@ -157,7 +165,7 @@ abstract class AbstractSchemaManager
$sql = $this->_platform->getListTableColumnsSQL($table, $database);
$tableColumns = $this->_conn->fetchAll($sql);
$tableColumns = $this->_conn->fetchAllAssociative($sql);
return $this->_getPortableTableColumnList($table, $database, $tableColumns);
}
@@ -175,7 +183,7 @@ abstract class AbstractSchemaManager
{
$sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase());
$tableIndexes = $this->_conn->fetchAll($sql);
$tableIndexes = $this->_conn->fetchAllAssociative($sql);
return $this->_getPortableTableIndexesList($tableIndexes, $table);
}
@@ -183,15 +191,26 @@ abstract class AbstractSchemaManager
/**
* Returns true if all the given tables exist.
*
* @param string[] $tableNames
* The usage of a string $tableNames is deprecated. Pass a one-element array instead.
*
* @param string|string[] $names
*
* @return bool
*/
public function tablesExist($tableNames)
public function tablesExist($names)
{
$tableNames = array_map('strtolower', (array) $tableNames);
if (is_string($names)) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/3580',
'The usage of a string $tableNames in AbstractSchemaManager::tablesExist is deprecated. ' .
'Pass a one-element array instead.'
);
}
return count($tableNames) === count(array_intersect($tableNames, array_map('strtolower', $this->listTableNames())));
$names = array_map('strtolower', (array) $names);
return count($names) === count(array_intersect($names, array_map('strtolower', $this->listTableNames())));
}
/**
@@ -203,7 +222,7 @@ abstract class AbstractSchemaManager
{
$sql = $this->_platform->getListTablesSQL();
$tables = $this->_conn->fetchAll($sql);
$tables = $this->_conn->fetchAllAssociative($sql);
$tableNames = $this->_getPortableTablesList($tables);
return $this->filterAssetNames($tableNames);
@@ -255,20 +274,21 @@ abstract class AbstractSchemaManager
}
/**
* @param string $tableName
* @param string $name
*
* @return Table
*/
public function listTableDetails($tableName)
public function listTableDetails($name)
{
$columns = $this->listTableColumns($tableName);
$columns = $this->listTableColumns($name);
$foreignKeys = [];
if ($this->_platform->supportsForeignKeyConstraints()) {
$foreignKeys = $this->listTableForeignKeys($tableName);
$foreignKeys = $this->listTableForeignKeys($name);
}
$indexes = $this->listTableIndexes($tableName);
return new Table($tableName, $columns, $indexes, $foreignKeys, false, []);
$indexes = $this->listTableIndexes($name);
return new Table($name, $columns, $indexes, $foreignKeys);
}
/**
@@ -280,7 +300,7 @@ abstract class AbstractSchemaManager
{
$database = $this->_conn->getDatabase();
$sql = $this->_platform->getListViewsSQL($database);
$views = $this->_conn->fetchAll($sql);
$views = $this->_conn->fetchAllAssociative($sql);
return $this->_getPortableViewsList($views);
}
@@ -298,8 +318,9 @@ abstract class AbstractSchemaManager
if ($database === null) {
$database = $this->_conn->getDatabase();
}
$sql = $this->_platform->getListTableForeignKeysSQL($table, $database);
$tableForeignKeys = $this->_conn->fetchAll($sql);
$tableForeignKeys = $this->_conn->fetchAllAssociative($sql);
return $this->_getPortableTableForeignKeysList($tableForeignKeys);
}
@@ -323,13 +344,13 @@ abstract class AbstractSchemaManager
/**
* Drops the given table.
*
* @param string $tableName The name of the table to drop.
* @param string $name The name of the table to drop.
*
* @return void
*/
public function dropTable($tableName)
public function dropTable($name)
{
$this->_execSql($this->_platform->getDropTableSQL($tableName));
$this->_execSql($this->_platform->getDropTableSQL($name));
}
/**
@@ -419,7 +440,7 @@ abstract class AbstractSchemaManager
*/
public function createTable(Table $table)
{
$createFlags = AbstractPlatform::CREATE_INDEXES|AbstractPlatform::CREATE_FOREIGNKEYS;
$createFlags = AbstractPlatform::CREATE_INDEXES | AbstractPlatform::CREATE_FOREIGNKEYS;
$this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags));
}
@@ -518,7 +539,8 @@ abstract class AbstractSchemaManager
/**
* Drops and creates a new foreign key.
*
* @param ForeignKeyConstraint $foreignKey An associative array that defines properties of the foreign key to be created.
* @param ForeignKeyConstraint $foreignKey An associative array that defines properties
* of the foreign key to be created.
* @param Table|string $table The name of the table on which the foreign key is to be created.
*
* @return void
@@ -586,12 +608,7 @@ abstract class AbstractSchemaManager
*/
public function alterTable(TableDiff $tableDiff)
{
$queries = $this->_platform->getAlterTableSQL($tableDiff);
if (! is_array($queries) || ! count($queries)) {
return;
}
foreach ($queries as $ddlQuery) {
foreach ($this->_platform->getAlterTableSQL($tableDiff) as $ddlQuery) {
$this->_execSql($ddlQuery);
}
}
@@ -678,6 +695,8 @@ abstract class AbstractSchemaManager
}
/**
* @deprecated
*
* @param mixed[][] $functions
*
* @return mixed[][]
@@ -699,6 +718,8 @@ abstract class AbstractSchemaManager
}
/**
* @deprecated
*
* @param mixed[] $function
*
* @return mixed
@@ -747,14 +768,9 @@ abstract class AbstractSchemaManager
protected function _getPortableSequencesList($sequences)
{
$list = [];
foreach ($sequences as $value) {
$value = $this->_getPortableSequenceDefinition($value);
if (! $value) {
continue;
}
$list[] = $value;
$list[] = $this->_getPortableSequenceDefinition($value);
}
return $list;
@@ -765,11 +781,11 @@ abstract class AbstractSchemaManager
*
* @return Sequence
*
* @throws DBALException
* @throws Exception
*/
protected function _getPortableSequenceDefinition($sequence)
{
throw DBALException::notSupported('Sequences');
throw Exception::notSupported('Sequences');
}
/**
@@ -827,19 +843,20 @@ abstract class AbstractSchemaManager
/**
* Aggregates and groups the index results according to the required data result.
*
* @param mixed[][] $tableIndexRows
* @param mixed[][] $tableIndexes
* @param string|null $tableName
*
* @return Index[]
*/
protected function _getPortableTableIndexesList($tableIndexRows, $tableName = null)
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
$result = [];
foreach ($tableIndexRows as $tableIndex) {
foreach ($tableIndexes as $tableIndex) {
$indexName = $keyName = $tableIndex['key_name'];
if ($tableIndex['primary']) {
$keyName = 'primary';
}
$keyName = strtolower($keyName);
if (! isset($result[$keyName])) {
@@ -854,7 +871,7 @@ abstract class AbstractSchemaManager
$result[$keyName] = [
'name' => $indexName,
'columns' => [],
'unique' => $tableIndex['non_unique'] ? false : true,
'unique' => ! $tableIndex['non_unique'],
'primary' => $tableIndex['primary'],
'flags' => $tableIndex['flags'] ?? [],
'options' => $options,
@@ -881,7 +898,14 @@ abstract class AbstractSchemaManager
}
if (! $defaultPrevented) {
$index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary'], $data['flags'], $data['options']);
$index = new Index(
$data['name'],
$data['columns'],
$data['unique'],
$data['primary'],
$data['flags'],
$data['options']
);
}
if (! $index) {
@@ -947,9 +971,9 @@ abstract class AbstractSchemaManager
}
/**
* @param mixed[] $user
* @param string[] $user
*
* @return mixed[]
* @return string[]
*/
protected function _getPortableUserDefinition($user)
{
@@ -996,14 +1020,9 @@ abstract class AbstractSchemaManager
protected function _getPortableTableForeignKeysList($tableForeignKeys)
{
$list = [];
foreach ($tableForeignKeys as $value) {
$value = $this->_getPortableTableForeignKeyDefinition($value);
if (! $value) {
continue;
}
$list[] = $value;
$list[] = $this->_getPortableTableForeignKeyDefinition($value);
}
return $list;
@@ -1027,7 +1046,7 @@ abstract class AbstractSchemaManager
protected function _execSql($sql)
{
foreach ((array) $sql as $query) {
$this->_conn->executeUpdate($query);
$this->_conn->executeStatement($query);
}
}
@@ -1074,9 +1093,11 @@ abstract class AbstractSchemaManager
if (! isset($params['defaultTableOptions'])) {
$params['defaultTableOptions'] = [];
}
if (! isset($params['defaultTableOptions']['charset']) && isset($params['charset'])) {
$params['defaultTableOptions']['charset'] = $params['charset'];
}
$schemaConfig->setDefaultTableOptions($params['defaultTableOptions']);
return $schemaConfig;
@@ -1103,28 +1124,32 @@ abstract class AbstractSchemaManager
* Given a table comment this method tries to extract a typehint for Doctrine Type, or returns
* the type given as default.
*
* @param string $comment
* @param string $currentType
* @param string|null $comment
* @param string $currentType
*
* @return string
*/
public function extractDoctrineTypeFromComment($comment, $currentType)
{
if (preg_match('(\(DC2Type:(((?!\)).)+)\))', $comment, $match)) {
$currentType = $match[1];
if ($comment !== null && preg_match('(\(DC2Type:(((?!\)).)+)\))', $comment, $match)) {
return $match[1];
}
return $currentType;
}
/**
* @param string $comment
* @param string $type
* @param string|null $comment
* @param string|null $type
*
* @return string
* @return string|null
*/
public function removeDoctrineTypeFromComment($comment, $type)
{
if ($comment === null) {
return null;
}
return str_replace('(DC2Type:' . $type . ')', '', $comment);
}
}

View File

@@ -3,12 +3,11 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Types\Type;
use const E_USER_DEPRECATED;
use Doctrine\Deprecations\Deprecation;
use function array_merge;
use function is_numeric;
use function method_exists;
use function sprintf;
use function trigger_error;
/**
* Object representation of a database column.
@@ -19,7 +18,7 @@ class Column extends AbstractAsset
protected $_type;
/** @var int|null */
protected $_length = null;
protected $_length;
/** @var int */
protected $_precision = 10;
@@ -37,7 +36,7 @@ class Column extends AbstractAsset
protected $_notnull = true;
/** @var string|null */
protected $_default = null;
protected $_default;
/** @var bool */
protected $_autoincrement = false;
@@ -46,10 +45,10 @@ class Column extends AbstractAsset
protected $_platformOptions = [];
/** @var string|null */
protected $_columnDefinition = null;
protected $_columnDefinition;
/** @var string|null */
protected $_comment = null;
protected $_comment;
/** @var mixed[] */
protected $_customSchemaOptions = [];
@@ -57,12 +56,12 @@ class Column extends AbstractAsset
/**
* Creates a new Column.
*
* @param string $columnName
* @param string $name
* @param mixed[] $options
*/
public function __construct($columnName, Type $type, array $options = [])
public function __construct($name, Type $type, array $options = [])
{
$this->_setName($columnName);
$this->_setName($name);
$this->setType($type);
$this->setOptions($options);
}
@@ -78,14 +77,17 @@ class Column extends AbstractAsset
$method = 'set' . $name;
if (! method_exists($this, $method)) {
// next major: throw an exception
@trigger_error(sprintf(
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/2846',
'The "%s" column option is not supported,' .
' setting it is deprecated and will cause an error in Doctrine 3.0',
' setting unknown options is deprecated and will cause an error in Doctrine DBAL 3.0',
$name
), E_USER_DEPRECATED);
);
continue;
}
$this->$method($value);
}
@@ -356,7 +358,7 @@ class Column extends AbstractAsset
}
/**
* @param string $comment
* @param string|null $comment
*
* @return Column
*/

View File

@@ -18,15 +18,19 @@ class ColumnDiff
/** @var string[] */
public $changedProperties = [];
/** @var Column */
/** @var Column|null */
public $fromColumn;
/**
* @param string $oldColumnName
* @param string[] $changedProperties
*/
public function __construct($oldColumnName, Column $column, array $changedProperties = [], ?Column $fromColumn = null)
{
public function __construct(
$oldColumnName,
Column $column,
array $changedProperties = [],
?Column $fromColumn = null
) {
$this->oldColumnName = $oldColumnName;
$this->column = $column;
$this->changedProperties = $changedProperties;

View File

@@ -3,6 +3,7 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Types;
use function array_intersect_key;
use function array_key_exists;
use function array_keys;
@@ -10,7 +11,9 @@ use function array_map;
use function array_merge;
use function array_shift;
use function array_unique;
use function assert;
use function count;
use function get_class;
use function strtolower;
/**
@@ -65,7 +68,11 @@ class Comparator
if (! $fromSchema->hasTable($tableName)) {
$diff->newTables[$tableName] = $toSchema->getTable($tableName);
} else {
$tableDifferences = $this->diffTable($fromSchema->getTable($tableName), $toSchema->getTable($tableName));
$tableDifferences = $this->diffTable(
$fromSchema->getTable($tableName),
$toSchema->getTable($tableName)
);
if ($tableDifferences !== false) {
$diff->changedTables[$tableName] = $tableDifferences;
}
@@ -87,6 +94,7 @@ class Comparator
if (! isset($foreignKeysToTable[$foreignTable])) {
$foreignKeysToTable[$foreignTable] = [];
}
$foreignKeysToTable[$foreignTable][] = $foreignKey;
}
}
@@ -108,10 +116,13 @@ class Comparator
}
foreach ($diff->changedTables[$localTableName]->removedForeignKeys as $key => $removedForeignKey) {
assert($removedForeignKey instanceof ForeignKeyConstraint);
// We check if the key is from the removed table if not we skip.
if ($tableName !== strtolower($removedForeignKey->getForeignTableName())) {
continue;
}
unset($diff->changedTables[$localTableName]->removedForeignKeys[$key]);
}
}
@@ -177,47 +188,49 @@ class Comparator
}
/**
* Returns the difference between the tables $table1 and $table2.
* Returns the difference between the tables $fromTable and $toTable.
*
* If there are no differences this method returns the boolean false.
*
* @return TableDiff|false
*/
public function diffTable(Table $table1, Table $table2)
public function diffTable(Table $fromTable, Table $toTable)
{
$changes = 0;
$tableDifferences = new TableDiff($table1->getName());
$tableDifferences->fromTable = $table1;
$tableDifferences = new TableDiff($fromTable->getName());
$tableDifferences->fromTable = $fromTable;
$table1Columns = $table1->getColumns();
$table2Columns = $table2->getColumns();
$fromTableColumns = $fromTable->getColumns();
$toTableColumns = $toTable->getColumns();
/* See if all the fields in table 1 exist in table 2 */
foreach ($table2Columns as $columnName => $column) {
if ($table1->hasColumn($columnName)) {
/* See if all the columns in "from" table exist in "to" table */
foreach ($toTableColumns as $columnName => $column) {
if ($fromTable->hasColumn($columnName)) {
continue;
}
$tableDifferences->addedColumns[$columnName] = $column;
$changes++;
}
/* See if there are any removed fields in table 2 */
foreach ($table1Columns as $columnName => $column) {
// See if column is removed in table 2.
if (! $table2->hasColumn($columnName)) {
/* See if there are any removed columns in "to" table */
foreach ($fromTableColumns as $columnName => $column) {
// See if column is removed in "to" table.
if (! $toTable->hasColumn($columnName)) {
$tableDifferences->removedColumns[$columnName] = $column;
$changes++;
continue;
}
// See if column has changed properties in table 2.
$changedProperties = $this->diffColumn($column, $table2->getColumn($columnName));
// See if column has changed properties in "to" table.
$changedProperties = $this->diffColumn($column, $toTable->getColumn($columnName));
if (empty($changedProperties)) {
continue;
}
$columnDiff = new ColumnDiff($column->getName(), $table2->getColumn($columnName), $changedProperties);
$columnDiff = new ColumnDiff($column->getName(), $toTable->getColumn($columnName), $changedProperties);
$columnDiff->fromColumn = $column;
$tableDifferences->changedColumns[$column->getName()] = $columnDiff;
$changes++;
@@ -225,66 +238,69 @@ class Comparator
$this->detectColumnRenamings($tableDifferences);
$table1Indexes = $table1->getIndexes();
$table2Indexes = $table2->getIndexes();
$fromTableIndexes = $fromTable->getIndexes();
$toTableIndexes = $toTable->getIndexes();
/* See if all the indexes in table 1 exist in table 2 */
foreach ($table2Indexes as $indexName => $index) {
if (($index->isPrimary() && $table1->hasPrimaryKey()) || $table1->hasIndex($indexName)) {
/* See if all the indexes in "from" table exist in "to" table */
foreach ($toTableIndexes as $indexName => $index) {
if (($index->isPrimary() && $fromTable->hasPrimaryKey()) || $fromTable->hasIndex($indexName)) {
continue;
}
$tableDifferences->addedIndexes[$indexName] = $index;
$changes++;
}
/* See if there are any removed indexes in table 2 */
foreach ($table1Indexes as $indexName => $index) {
// See if index is removed in table 2.
if (($index->isPrimary() && ! $table2->hasPrimaryKey()) ||
! $index->isPrimary() && ! $table2->hasIndex($indexName)
/* See if there are any removed indexes in "to" table */
foreach ($fromTableIndexes as $indexName => $index) {
// See if index is removed in "to" table.
if (
($index->isPrimary() && ! $toTable->hasPrimaryKey()) ||
! $index->isPrimary() && ! $toTable->hasIndex($indexName)
) {
$tableDifferences->removedIndexes[$indexName] = $index;
$changes++;
continue;
}
// See if index has changed in table 2.
$table2Index = $index->isPrimary() ? $table2->getPrimaryKey() : $table2->getIndex($indexName);
// See if index has changed in "to" table.
$toTableIndex = $index->isPrimary() ? $toTable->getPrimaryKey() : $toTable->getIndex($indexName);
assert($toTableIndex instanceof Index);
if (! $this->diffIndex($index, $table2Index)) {
if (! $this->diffIndex($index, $toTableIndex)) {
continue;
}
$tableDifferences->changedIndexes[$indexName] = $table2Index;
$tableDifferences->changedIndexes[$indexName] = $toTableIndex;
$changes++;
}
$this->detectIndexRenamings($tableDifferences);
$fromFkeys = $table1->getForeignKeys();
$toFkeys = $table2->getForeignKeys();
$fromForeignKeys = $fromTable->getForeignKeys();
$toForeignKeys = $toTable->getForeignKeys();
foreach ($fromFkeys as $key1 => $constraint1) {
foreach ($toFkeys as $key2 => $constraint2) {
if ($this->diffForeignKey($constraint1, $constraint2) === false) {
unset($fromFkeys[$key1], $toFkeys[$key2]);
foreach ($fromForeignKeys as $fromKey => $fromConstraint) {
foreach ($toForeignKeys as $toKey => $toConstraint) {
if ($this->diffForeignKey($fromConstraint, $toConstraint) === false) {
unset($fromForeignKeys[$fromKey], $toForeignKeys[$toKey]);
} else {
if (strtolower($constraint1->getName()) === strtolower($constraint2->getName())) {
$tableDifferences->changedForeignKeys[] = $constraint2;
if (strtolower($fromConstraint->getName()) === strtolower($toConstraint->getName())) {
$tableDifferences->changedForeignKeys[] = $toConstraint;
$changes++;
unset($fromFkeys[$key1], $toFkeys[$key2]);
unset($fromForeignKeys[$fromKey], $toForeignKeys[$toKey]);
}
}
}
}
foreach ($fromFkeys as $constraint1) {
$tableDifferences->removedForeignKeys[] = $constraint1;
foreach ($fromForeignKeys as $fromConstraint) {
$tableDifferences->removedForeignKeys[] = $fromConstraint;
$changes++;
}
foreach ($toFkeys as $constraint2) {
$tableDifferences->addedForeignKeys[] = $constraint2;
foreach ($toForeignKeys as $toConstraint) {
$tableDifferences->addedForeignKeys[] = $toConstraint;
$changes++;
}
@@ -383,11 +399,17 @@ class Comparator
*/
public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2)
{
if (array_map('strtolower', $key1->getUnquotedLocalColumns()) !== array_map('strtolower', $key2->getUnquotedLocalColumns())) {
if (
array_map('strtolower', $key1->getUnquotedLocalColumns())
!== array_map('strtolower', $key2->getUnquotedLocalColumns())
) {
return true;
}
if (array_map('strtolower', $key1->getUnquotedForeignColumns()) !== array_map('strtolower', $key2->getUnquotedForeignColumns())) {
if (
array_map('strtolower', $key1->getUnquotedForeignColumns())
!== array_map('strtolower', $key2->getUnquotedForeignColumns())
) {
return true;
}
@@ -403,7 +425,7 @@ class Comparator
}
/**
* Returns the difference between the fields $field1 and $field2.
* Returns the difference between the columns
*
* If there are differences this method returns $field2, otherwise the
* boolean false.
@@ -417,7 +439,11 @@ class Comparator
$changedProperties = [];
foreach (['type', 'notnull', 'unsigned', 'autoincrement'] as $property) {
if (get_class($properties1['type']) !== get_class($properties2['type'])) {
$changedProperties[] = 'type';
}
foreach (['notnull', 'unsigned', 'autoincrement'] as $property) {
if ($properties1[$property] === $properties2[$property]) {
continue;
}
@@ -425,7 +451,8 @@ class Comparator
$changedProperties[] = $property;
}
// This is a very nasty hack to make comparator work with the legacy json_array type, which should be killed in v3
// This is a very nasty hack to make comparator work with the legacy json_array type,
// which should be killed in v3
if ($this->isALegacyJsonComparison($properties1['type'], $properties2['type'])) {
array_shift($changedProperties);
@@ -434,12 +461,15 @@ class Comparator
// Null values need to be checked additionally as they tell whether to create or drop a default value.
// null != 0, null != false, null != '' etc. This affects platform's table alteration SQL generation.
if (($properties1['default'] === null) !== ($properties2['default'] === null)
|| (string) $properties1['default'] !== (string) $properties2['default']) {
if (
($properties1['default'] === null) !== ($properties2['default'] === null)
|| $properties1['default'] != $properties2['default']
) {
$changedProperties[] = 'default';
}
if (($properties1['type'] instanceof Types\StringType && ! $properties1['type'] instanceof Types\GuidType) ||
if (
($properties1['type'] instanceof Types\StringType && ! $properties1['type'] instanceof Types\GuidType) ||
$properties1['type'] instanceof Types\BinaryType
) {
// check if value of length is set at all, default value assumed otherwise.
@@ -456,13 +486,15 @@ class Comparator
if (($properties1['precision'] ?: 10) !== ($properties2['precision'] ?: 10)) {
$changedProperties[] = 'precision';
}
if ($properties1['scale'] !== $properties2['scale']) {
$changedProperties[] = 'scale';
}
}
// A null value and an empty string are actually equal for a comment so they should not trigger a change.
if ($properties1['comment'] !== $properties2['comment'] &&
if (
$properties1['comment'] !== $properties2['comment'] &&
! ($properties1['comment'] === null && $properties2['comment'] === '') &&
! ($properties2['comment'] === null && $properties1['comment'] === '')
) {
@@ -499,14 +531,14 @@ class Comparator
*
* @deprecated
*/
private function isALegacyJsonComparison(Types\Type $one, Types\Type $other) : bool
private function isALegacyJsonComparison(Types\Type $one, Types\Type $other): bool
{
if (! $one instanceof Types\JsonType || ! $other instanceof Types\JsonType) {
return false;
}
return ( ! $one instanceof Types\JsonArrayType && $other instanceof Types\JsonArrayType)
|| ( ! $other instanceof Types\JsonArrayType && $one instanceof Types\JsonArrayType);
return (! $one instanceof Types\JsonArrayType && $other instanceof Types\JsonArrayType)
|| (! $other instanceof Types\JsonArrayType && $one instanceof Types\JsonArrayType);
}
/**

View File

@@ -2,14 +2,18 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Platforms\DB2Platform;
use Doctrine\DBAL\Types\Type;
use const CASE_LOWER;
use function array_change_key_case;
use function is_resource;
use function assert;
use function preg_match;
use function str_replace;
use function strpos;
use function strtolower;
use function substr;
use function trim;
use const CASE_LOWER;
/**
* IBM Db2 Schema Manager.
@@ -27,7 +31,7 @@ class DB2SchemaManager extends AbstractSchemaManager
$sql = $this->_platform->getListTablesSQL();
$sql .= ' AND CREATOR = UPPER(' . $this->_conn->quote($this->_conn->getUsername()) . ')';
$tables = $this->_conn->fetchAll($sql);
$tables = $this->_conn->fetchAllAssociative($sql);
return $this->filterAssetNames($this->_getPortableTablesList($tables));
}
@@ -41,14 +45,17 @@ class DB2SchemaManager extends AbstractSchemaManager
$length = null;
$fixed = null;
$unsigned = false;
$scale = false;
$precision = false;
$default = null;
if ($tableColumn['default'] !== null && $tableColumn['default'] !== 'NULL') {
$default = trim($tableColumn['default'], "'");
$default = $tableColumn['default'];
if (preg_match('/^\'(.*)\'$/s', $default, $matches)) {
$default = str_replace("''", "'", $matches[1]);
}
}
$type = $this->_platform->getDoctrineTypeMapping($tableColumn['typename']);
@@ -63,13 +70,16 @@ class DB2SchemaManager extends AbstractSchemaManager
$length = $tableColumn['length'];
$fixed = false;
break;
case 'character':
$length = $tableColumn['length'];
$fixed = true;
break;
case 'clob':
$length = $tableColumn['length'];
break;
case 'decimal':
case 'double':
case 'real':
@@ -80,11 +90,11 @@ class DB2SchemaManager extends AbstractSchemaManager
$options = [
'length' => $length,
'unsigned' => (bool) $unsigned,
'unsigned' => false,
'fixed' => (bool) $fixed,
'default' => $default,
'autoincrement' => (bool) $tableColumn['autoincrement'],
'notnull' => (bool) ($tableColumn['nulls'] === 'N'),
'notnull' => $tableColumn['nulls'] === 'N',
'scale' => null,
'precision' => null,
'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== ''
@@ -118,14 +128,14 @@ class DB2SchemaManager extends AbstractSchemaManager
/**
* {@inheritdoc}
*/
protected function _getPortableTableIndexesList($tableIndexRows, $tableName = null)
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
foreach ($tableIndexRows as &$tableIndexRow) {
foreach ($tableIndexes as &$tableIndexRow) {
$tableIndexRow = array_change_key_case($tableIndexRow, CASE_LOWER);
$tableIndexRow['primary'] = (bool) $tableIndexRow['primary'];
}
return parent::_getPortableTableIndexesList($tableIndexRows, $tableName);
return parent::_getPortableTableIndexesList($tableIndexes, $tableName);
}
/**
@@ -173,13 +183,17 @@ class DB2SchemaManager extends AbstractSchemaManager
}
/**
* {@inheritdoc}
* @param string $def
*
* @return string|null
*/
protected function _getPortableForeignKeyRuleDef($def)
{
if ($def === 'C') {
return 'CASCADE';
} elseif ($def === 'N') {
}
if ($def === 'N') {
return 'SET NULL';
}
@@ -192,15 +206,34 @@ class DB2SchemaManager extends AbstractSchemaManager
protected function _getPortableViewDefinition($view)
{
$view = array_change_key_case($view, CASE_LOWER);
// sadly this still segfaults on PDO_IBM, see http://pecl.php.net/bugs/bug.php?id=17199
//$view['text'] = (is_resource($view['text']) ? stream_get_contents($view['text']) : $view['text']);
if (! is_resource($view['text'])) {
$pos = strpos($view['text'], ' AS ');
$sql = substr($view['text'], $pos+4);
} else {
$sql = '';
$sql = '';
$pos = strpos($view['text'], ' AS ');
if ($pos !== false) {
$sql = substr($view['text'], $pos + 4);
}
return new View($view['name'], $sql);
}
/**
* {@inheritdoc}
*/
public function listTableDetails($name): Table
{
$table = parent::listTableDetails($name);
$platform = $this->_platform;
assert($platform instanceof DB2Platform);
$sql = $platform->getListTableCommentsSQL($name);
$tableOptions = $this->_conn->fetchAssociative($sql);
if ($tableOptions !== false) {
$table->addOption('comment', $tableOptions['REMARKS']);
}
return $table;
}
}

View File

@@ -3,6 +3,7 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Types\Type;
use function explode;
use function strtolower;
use function trim;

View File

@@ -3,14 +3,14 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use function array_combine;
use function array_keys;
use function array_map;
use function end;
use function explode;
use function in_array;
use function strrpos;
use function strtolower;
use function strtoupper;
use function substr;
/**
* An abstraction class for a foreign key constraint.
@@ -63,15 +63,18 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint
* @param string|null $name Name of the foreign key constraint.
* @param mixed[] $options Options associated with the foreign key constraint.
*/
public function __construct(array $localColumnNames, $foreignTableName, array $foreignColumnNames, $name = null, array $options = [])
{
$this->_setName($name);
$identifierConstructorCallback = static function ($column) {
return new Identifier($column);
};
$this->_localColumnNames = $localColumnNames
? array_combine($localColumnNames, array_map($identifierConstructorCallback, $localColumnNames))
: [];
public function __construct(
array $localColumnNames,
$foreignTableName,
array $foreignColumnNames,
$name = null,
array $options = []
) {
if ($name !== null) {
$this->_setName($name);
}
$this->_localColumnNames = $this->createIdentifierMap($localColumnNames);
if ($foreignTableName instanceof Table) {
$this->_foreignTableName = $foreignTableName;
@@ -79,12 +82,26 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint
$this->_foreignTableName = new Identifier($foreignTableName);
}
$this->_foreignColumnNames = $foreignColumnNames
? array_combine($foreignColumnNames, array_map($identifierConstructorCallback, $foreignColumnNames))
: [];
$this->_foreignColumnNames = $this->createIdentifierMap($foreignColumnNames);
$this->_options = $options;
}
/**
* @param string[] $names
*
* @return Identifier[]
*/
private function createIdentifierMap(array $names): array
{
$identifiers = [];
foreach ($names as $name) {
$identifiers[$name] = new Identifier($name);
}
return $identifiers;
}
/**
* Returns the name of the referencing table
* the foreign key constraint is associated with.
@@ -218,9 +235,14 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint
*/
public function getUnqualifiedForeignTableName()
{
$parts = explode('.', $this->_foreignTableName->getName());
$name = $this->_foreignTableName->getName();
$position = strrpos($name, '.');
return strtolower(end($parts));
if ($position !== false) {
$name = substr($name, $position + 1);
}
return strtolower($name);
}
/**
@@ -349,7 +371,7 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint
}
}
return false;
return null;
}
/**

View File

@@ -4,6 +4,8 @@ namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use InvalidArgumentException;
use function array_filter;
use function array_keys;
use function array_map;
use function array_search;
@@ -45,18 +47,24 @@ class Index extends AbstractAsset implements Constraint
private $options = [];
/**
* @param string $indexName
* @param string $name
* @param string[] $columns
* @param bool $isUnique
* @param bool $isPrimary
* @param string[] $flags
* @param mixed[] $options
*/
public function __construct($indexName, array $columns, $isUnique = false, $isPrimary = false, array $flags = [], array $options = [])
{
public function __construct(
$name,
array $columns,
$isUnique = false,
$isPrimary = false,
array $flags = [],
array $options = []
) {
$isUnique = $isUnique || $isPrimary;
$this->_setName($indexName);
$this->_setName($name);
$this->_isUnique = $isUnique;
$this->_isPrimary = $isPrimary;
$this->options = $options;
@@ -64,6 +72,7 @@ class Index extends AbstractAsset implements Constraint
foreach ($columns as $column) {
$this->_addColumn($column);
}
foreach ($flags as $flag) {
$this->addFlag($flag);
}
@@ -153,17 +162,17 @@ class Index extends AbstractAsset implements Constraint
}
/**
* @param string $columnName
* @param string $name
* @param int $pos
*
* @return bool
*/
public function hasColumnAtPosition($columnName, $pos = 0)
public function hasColumnAtPosition($name, $pos = 0)
{
$columnName = $this->trimQuotes(strtolower($columnName));
$name = $this->trimQuotes(strtolower($name));
$indexColumns = array_map('strtolower', $this->getUnquotedColumns());
return array_search($columnName, $indexColumns) === $pos;
return array_search($name, $indexColumns) === $pos;
}
/**
@@ -180,7 +189,10 @@ class Index extends AbstractAsset implements Constraint
$sameColumns = true;
for ($i = 0; $i < $numberOfColumns; $i++) {
if (isset($columnNames[$i]) && $this->trimQuotes(strtolower($columns[$i])) === $this->trimQuotes(strtolower($columnNames[$i]))) {
if (
isset($columnNames[$i])
&& $this->trimQuotes(strtolower($columns[$i])) === $this->trimQuotes(strtolower($columnNames[$i]))
) {
continue;
}
@@ -211,6 +223,10 @@ class Index extends AbstractAsset implements Constraint
return false;
}
if (! $this->hasSameColumnLengths($other)) {
return false;
}
if (! $this->isUnique() && ! $this->isPrimary()) {
// this is a special case: If the current key is neither primary or unique, any unique or
// primary key will always have the same effect for the index and there cannot be any constraint
@@ -238,11 +254,15 @@ class Index extends AbstractAsset implements Constraint
{
if ($other->isPrimary()) {
return false;
} elseif ($this->isSimpleIndex() && $other->isUnique()) {
}
if ($this->isSimpleIndex() && $other->isUnique()) {
return false;
}
return $this->spansColumns($other->getColumns()) && ($this->isPrimary() || $this->isUnique()) && $this->samePartialIndex($other);
return $this->spansColumns($other->getColumns())
&& ($this->isPrimary() || $this->isUnique())
&& $this->samePartialIndex($other);
}
/**
@@ -330,10 +350,27 @@ class Index extends AbstractAsset implements Constraint
*/
private function samePartialIndex(Index $other)
{
if ($this->hasOption('where') && $other->hasOption('where') && $this->getOption('where') === $other->getOption('where')) {
if (
$this->hasOption('where')
&& $other->hasOption('where')
&& $this->getOption('where') === $other->getOption('where')
) {
return true;
}
return ! $this->hasOption('where') && ! $other->hasOption('where');
}
/**
* Returns whether the index has the same column lengths as the other
*/
private function hasSameColumnLengths(self $other): bool
{
$filter = static function (?int $length): bool {
return $length !== null;
};
return array_filter($this->options['lengths'] ?? [], $filter)
=== array_filter($other->options['lengths'] ?? [], $filter);
}
}

View File

@@ -5,26 +5,45 @@ namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Platforms\MariaDb1027Platform;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\DBAL\Types\Type;
use const CASE_LOWER;
use function array_change_key_case;
use function array_shift;
use function array_values;
use function end;
use function assert;
use function explode;
use function is_string;
use function preg_match;
use function preg_replace;
use function str_replace;
use function stripslashes;
use function strpos;
use function strtok;
use function strtolower;
use function trim;
use function strtr;
use const CASE_LOWER;
/**
* Schema manager for the MySql RDBMS.
*/
class MySqlSchemaManager extends AbstractSchemaManager
{
/**
* @see https://mariadb.com/kb/en/library/string-literals/#escape-sequences
*/
private const MARIADB_ESCAPE_SEQUENCES = [
'\\0' => "\0",
"\\'" => "'",
'\\"' => '"',
'\\b' => "\b",
'\\n' => "\n",
'\\r' => "\r",
'\\t' => "\t",
'\\Z' => "\x1a",
'\\\\' => '\\',
'\\%' => '%',
'\\_' => '_',
// Internally, MariaDB escapes single quotes using the standard syntax
"''" => "'",
];
/**
* {@inheritdoc}
*/
@@ -64,12 +83,17 @@ class MySqlSchemaManager extends AbstractSchemaManager
} else {
$v['primary'] = false;
}
if (strpos($v['index_type'], 'FULLTEXT') !== false) {
$v['flags'] = ['FULLTEXT'];
} elseif (strpos($v['index_type'], 'SPATIAL') !== false) {
$v['flags'] = ['SPATIAL'];
}
$v['length'] = $v['sub_part'] ?? null;
// Ignore prohibited prefix `length` for spatial index
if (strpos($v['index_type'], 'SPATIAL') === false) {
$v['length'] = isset($v['sub_part']) ? (int) $v['sub_part'] : null;
}
$tableIndexes[$k] = $v;
}
@@ -77,14 +101,6 @@ class MySqlSchemaManager extends AbstractSchemaManager
return parent::_getPortableTableIndexesList($tableIndexes, $tableName);
}
/**
* {@inheritdoc}
*/
protected function _getPortableSequenceDefinition($sequence)
{
return end($sequence);
}
/**
* {@inheritdoc}
*/
@@ -102,6 +118,8 @@ class MySqlSchemaManager extends AbstractSchemaManager
$dbType = strtolower($tableColumn['type']);
$dbType = strtok($dbType, '(), ');
assert(is_string($dbType));
$length = $tableColumn['length'] ?? strtok('(), ');
$fixed = null;
@@ -126,6 +144,7 @@ class MySqlSchemaManager extends AbstractSchemaManager
case 'binary':
$fixed = true;
break;
case 'float':
case 'double':
case 'real':
@@ -136,25 +155,33 @@ class MySqlSchemaManager extends AbstractSchemaManager
$scale = $match[2];
$length = null;
}
break;
case 'tinytext':
$length = MySqlPlatform::LENGTH_LIMIT_TINYTEXT;
break;
case 'text':
$length = MySqlPlatform::LENGTH_LIMIT_TEXT;
break;
case 'mediumtext':
$length = MySqlPlatform::LENGTH_LIMIT_MEDIUMTEXT;
break;
case 'tinyblob':
$length = MySqlPlatform::LENGTH_LIMIT_TINYBLOB;
break;
case 'blob':
$length = MySqlPlatform::LENGTH_LIMIT_BLOB;
break;
case 'mediumblob':
$length = MySqlPlatform::LENGTH_LIMIT_MEDIUMBLOB;
break;
case 'tinyint':
case 'smallint':
case 'mediumint':
@@ -193,6 +220,10 @@ class MySqlSchemaManager extends AbstractSchemaManager
$column = new Column($tableColumn['field'], Type::getType($type), $options);
if (isset($tableColumn['characterset'])) {
$column->setPlatformOption('charset', $tableColumn['characterset']);
}
if (isset($tableColumn['collation'])) {
$column->setPlatformOption('collation', $tableColumn['collation']);
}
@@ -216,28 +247,27 @@ class MySqlSchemaManager extends AbstractSchemaManager
*
* @param string|null $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7
*/
private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault) : ?string
private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault): ?string
{
if ($columnDefault === 'NULL' || $columnDefault === null) {
return null;
}
if ($columnDefault[0] === "'") {
return stripslashes(
str_replace(
"''",
"'",
preg_replace('/^\'(.*)\'$/', '$1', $columnDefault)
)
);
if (preg_match('/^\'(.*)\'$/', $columnDefault, $matches)) {
return strtr($matches[1], self::MARIADB_ESCAPE_SEQUENCES);
}
switch ($columnDefault) {
case 'current_timestamp()':
return $platform->getCurrentTimestampSQL();
case 'curdate()':
return $platform->getCurrentDateSQL();
case 'curtime()':
return $platform->getCurrentTimeSQL();
}
return $columnDefault;
}
@@ -253,6 +283,7 @@ class MySqlSchemaManager extends AbstractSchemaManager
if (! isset($value['delete_rule']) || $value['delete_rule'] === 'RESTRICT') {
$value['delete_rule'] = null;
}
if (! isset($value['update_rule']) || $value['update_rule'] === 'RESTRICT') {
$value['update_rule'] = null;
}
@@ -266,6 +297,7 @@ class MySqlSchemaManager extends AbstractSchemaManager
'onUpdate' => $value['update_rule'],
];
}
$list[$value['constraint_name']]['local'][] = $value['column_name'];
$list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name'];
}
@@ -273,9 +305,9 @@ class MySqlSchemaManager extends AbstractSchemaManager
$result = [];
foreach ($list as $constraint) {
$result[] = new ForeignKeyConstraint(
array_values($constraint['local']),
$constraint['local'],
$constraint['foreignTable'],
array_values($constraint['foreign']),
$constraint['foreign'],
$constraint['name'],
[
'onDelete' => $constraint['onDelete'],
@@ -287,43 +319,56 @@ class MySqlSchemaManager extends AbstractSchemaManager
return $result;
}
public function listTableDetails($tableName)
/**
* {@inheritdoc}
*/
public function listTableDetails($name)
{
$table = parent::listTableDetails($tableName);
$table = parent::listTableDetails($name);
/** @var MySqlPlatform $platform */
$platform = $this->_platform;
$sql = $platform->getListTableMetadataSQL($tableName);
assert($platform instanceof MySqlPlatform);
$sql = $platform->getListTableMetadataSQL($name);
$tableOptions = $this->_conn->fetchAssoc($sql);
$tableOptions = $this->_conn->fetchAssociative($sql);
$table->addOption('engine', $tableOptions['ENGINE']);
if ($tableOptions['TABLE_COLLATION'] !== null) {
$table->addOption('collation', $tableOptions['TABLE_COLLATION']);
}
if ($tableOptions['AUTO_INCREMENT'] !== null) {
$table->addOption('autoincrement', $tableOptions['AUTO_INCREMENT']);
}
$table->addOption('comment', $tableOptions['TABLE_COMMENT']);
if ($tableOptions['CREATE_OPTIONS'] === null) {
if ($tableOptions === false) {
return $table;
}
$createOptionsString = trim($tableOptions['CREATE_OPTIONS']);
$table->addOption('engine', $tableOptions['ENGINE']);
$createOptions = [];
if ($createOptionsString !== '') {
foreach (explode(' ', $createOptionsString) as $option) {
[$createOption, $value] = explode('=', $option);
$createOptions[$createOption] = $value;
}
if ($tableOptions['TABLE_COLLATION'] !== null) {
$table->addOption('collation', $tableOptions['TABLE_COLLATION']);
}
$table->addOption('create_options', $createOptions);
if ($tableOptions['AUTO_INCREMENT'] !== null) {
$table->addOption('autoincrement', $tableOptions['AUTO_INCREMENT']);
}
$table->addOption('comment', $tableOptions['TABLE_COMMENT']);
$table->addOption('create_options', $this->parseCreateOptions($tableOptions['CREATE_OPTIONS']));
return $table;
}
/**
* @return string[]|true[]
*/
private function parseCreateOptions(?string $string): array
{
$options = [];
if ($string === null || $string === '') {
return $options;
}
foreach (explode(' ', $string) as $pair) {
$parts = explode('=', $pair, 2);
$options[$parts[0]] = $parts[1] ?? true;
}
return $options;
}
}

View File

@@ -3,20 +3,25 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\DriverException;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Types\Type;
use const CASE_LOWER;
use Throwable;
use function array_change_key_case;
use function array_values;
use function assert;
use function is_string;
use function preg_match;
use function sprintf;
use function str_replace;
use function strpos;
use function strtolower;
use function strtoupper;
use function trim;
use const CASE_LOWER;
/**
* Oracle Schema Manager.
*/
@@ -31,8 +36,9 @@ class OracleSchemaManager extends AbstractSchemaManager
parent::dropDatabase($database);
} catch (DBALException $exception) {
$exception = $exception->getPrevious();
assert($exception instanceof Throwable);
if (! $exception instanceof DriverException) {
if (! $exception instanceof Exception) {
throw $exception;
}
@@ -96,7 +102,7 @@ class OracleSchemaManager extends AbstractSchemaManager
$keyName = strtolower($tableIndex['name']);
$buffer = [];
if (strtolower($tableIndex['is_primary']) === 'p') {
if ($tableIndex['is_primary'] === 'P') {
$keyName = 'primary';
$buffer['primary'] = true;
$buffer['non_unique'] = false;
@@ -104,6 +110,7 @@ class OracleSchemaManager extends AbstractSchemaManager
$buffer['primary'] = false;
$buffer['non_unique'] = ! $tableIndex['is_unique'];
}
$buffer['key_name'] = $keyName;
$buffer['column_name'] = $this->getQuotedIdentifierName($tableIndex['column_name']);
$indexBuffer[] = $buffer;
@@ -128,26 +135,35 @@ class OracleSchemaManager extends AbstractSchemaManager
}
}
$unsigned = $fixed = null;
$unsigned = $fixed = $precision = $scale = $length = null;
if (! isset($tableColumn['column_name'])) {
$tableColumn['column_name'] = '';
}
// Default values returned from database sometimes have trailing spaces.
$tableColumn['data_default'] = trim($tableColumn['data_default']);
if (is_string($tableColumn['data_default'])) {
$tableColumn['data_default'] = trim($tableColumn['data_default']);
}
if ($tableColumn['data_default'] === '' || $tableColumn['data_default'] === 'NULL') {
$tableColumn['data_default'] = null;
}
if ($tableColumn['data_default'] !== null) {
// Default values returned from database are enclosed in single quotes.
$tableColumn['data_default'] = trim($tableColumn['data_default'], "'");
// Default values returned from database are represented as literal expressions
if (preg_match('/^\'(.*)\'$/s', $tableColumn['data_default'], $matches)) {
$tableColumn['data_default'] = str_replace("''", "'", $matches[1]);
}
}
$precision = null;
$scale = null;
if ($tableColumn['data_precision'] !== null) {
$precision = (int) $tableColumn['data_precision'];
}
if ($tableColumn['data_scale'] !== null) {
$scale = (int) $tableColumn['data_scale'];
}
$type = $this->_platform->getDoctrineTypeMapping($dbType);
$type = $this->extractDoctrineTypeFromComment($tableColumn['comments'], $type);
@@ -155,69 +171,34 @@ class OracleSchemaManager extends AbstractSchemaManager
switch ($dbType) {
case 'number':
if ($tableColumn['data_precision'] === 20 && $tableColumn['data_scale'] === 0) {
$precision = 20;
$scale = 0;
$type = 'bigint';
} elseif ($tableColumn['data_precision'] === 5 && $tableColumn['data_scale'] === 0) {
$type = 'smallint';
$precision = 5;
$scale = 0;
} elseif ($tableColumn['data_precision'] === 1 && $tableColumn['data_scale'] === 0) {
$precision = 1;
$scale = 0;
$type = 'boolean';
} elseif ($tableColumn['data_scale'] > 0) {
$precision = $tableColumn['data_precision'];
$scale = $tableColumn['data_scale'];
$type = 'decimal';
if ($precision === 20 && $scale === 0) {
$type = 'bigint';
} elseif ($precision === 5 && $scale === 0) {
$type = 'smallint';
} elseif ($precision === 1 && $scale === 0) {
$type = 'boolean';
} elseif ($scale > 0) {
$type = 'decimal';
}
$length = null;
break;
case 'pls_integer':
case 'binary_integer':
$length = null;
break;
case 'varchar':
case 'varchar2':
case 'nvarchar2':
$length = $tableColumn['char_length'];
$fixed = false;
break;
case 'char':
case 'nchar':
$length = $tableColumn['char_length'];
$fixed = true;
break;
case 'date':
case 'timestamp':
$length = null;
break;
case 'float':
case 'binary_float':
case 'binary_double':
$precision = $tableColumn['data_precision'];
$scale = $tableColumn['data_scale'];
$length = null;
break;
case 'clob':
case 'nclob':
$length = null;
break;
case 'blob':
case 'raw':
case 'long raw':
case 'bfile':
$length = null;
break;
case 'rowid':
case 'urowid':
default:
$length = null;
}
$options = [
'notnull' => (bool) ($tableColumn['nullable'] === 'N'),
'notnull' => $tableColumn['nullable'] === 'N',
'fixed' => (bool) $fixed,
'unsigned' => (bool) $unsigned,
'default' => $tableColumn['data_default'],
@@ -291,6 +272,8 @@ class OracleSchemaManager extends AbstractSchemaManager
/**
* {@inheritdoc}
*
* @deprecated
*/
protected function _getPortableFunctionDefinition($function)
{
@@ -311,6 +294,10 @@ class OracleSchemaManager extends AbstractSchemaManager
/**
* {@inheritdoc}
*
* @param string|null $database
*
* Calling this method without an argument or by passing NULL is deprecated.
*/
public function createDatabase($database = null)
{
@@ -318,15 +305,18 @@ class OracleSchemaManager extends AbstractSchemaManager
$database = $this->_conn->getDatabase();
}
$params = $this->_conn->getParams();
$username = $database;
$password = $params['password'];
$statement = 'CREATE USER ' . $database;
$query = 'CREATE USER ' . $username . ' IDENTIFIED BY ' . $password;
$this->_conn->executeUpdate($query);
$params = $this->_conn->getParams();
$query = 'GRANT DBA TO ' . $username;
$this->_conn->executeUpdate($query);
if (isset($params['password'])) {
$statement .= ' IDENTIFIED BY ' . $params['password'];
}
$this->_conn->executeStatement($statement);
$statement = 'GRANT DBA TO ' . $database;
$this->_conn->executeStatement($statement);
}
/**
@@ -340,7 +330,7 @@ class OracleSchemaManager extends AbstractSchemaManager
$sql = $this->_platform->getDropAutoincrementSql($table);
foreach ($sql as $query) {
$this->_conn->executeUpdate($query);
$this->_conn->executeStatement($query);
}
return true;
@@ -398,7 +388,7 @@ WHERE
AND p.addr(+) = s.paddr
SQL;
$activeUserSessions = $this->_conn->fetchAll($sql, [strtoupper($user)]);
$activeUserSessions = $this->_conn->fetchAllAssociative($sql, [strtoupper($user)]);
foreach ($activeUserSessions as $activeUserSession) {
$activeUserSession = array_change_key_case($activeUserSession, CASE_LOWER);
@@ -412,4 +402,24 @@ SQL;
);
}
}
/**
* {@inheritdoc}
*/
public function listTableDetails($name): Table
{
$table = parent::listTableDetails($name);
$platform = $this->_platform;
assert($platform instanceof OraclePlatform);
$sql = $platform->getListTableCommentsSQL($name);
$tableOptions = $this->_conn->fetchAssociative($sql);
if ($tableOptions !== false) {
$table->addOption('comment', $tableOptions['COMMENTS']);
}
return $table;
}
}

View File

@@ -6,7 +6,8 @@ use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
use Doctrine\DBAL\Types\Type;
use const CASE_LOWER;
use Doctrine\DBAL\Types\Types;
use function array_change_key_case;
use function array_filter;
use function array_keys;
@@ -20,18 +21,18 @@ use function preg_match;
use function preg_replace;
use function sprintf;
use function str_replace;
use function stripos;
use function strlen;
use function strpos;
use function strtolower;
use function trim;
use const CASE_LOWER;
/**
* PostgreSQL Schema Manager.
*/
class PostgreSqlSchemaManager extends AbstractSchemaManager
{
/** @var string[] */
/** @var string[]|null */
private $existingSchemaPaths;
/**
@@ -41,7 +42,9 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
*/
public function getSchemaNames()
{
$statement = $this->_conn->executeQuery("SELECT nspname FROM pg_namespace WHERE nspname !~ '^pg_.*' AND nspname != 'information_schema'");
$statement = $this->_conn->executeQuery(
"SELECT nspname FROM pg_namespace WHERE nspname !~ '^pg_.*' AND nspname != 'information_schema'"
);
return $statement->fetchAll(FetchMode::COLUMN);
}
@@ -56,7 +59,11 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
public function getSchemaSearchPaths()
{
$params = $this->_conn->getParams();
$schema = explode(',', $this->_conn->fetchColumn('SHOW search_path'));
$searchPaths = $this->_conn->fetchColumn('SHOW search_path');
assert($searchPaths !== false);
$schema = explode(',', $searchPaths);
if (isset($params['user'])) {
$schema = str_replace('"$user"', $params['user'], $schema);
@@ -78,6 +85,8 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
$this->determineExistingSchemaSearchPaths();
}
assert($this->existingSchemaPaths !== null);
return $this->existingSchemaPaths;
}
@@ -134,24 +143,26 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
{
$onUpdate = null;
$onDelete = null;
$localColumns = null;
$foreignColumns = null;
$localColumns = [];
$foreignColumns = [];
$foreignTable = null;
if (preg_match('(ON UPDATE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) {
$onUpdate = $match[1];
}
if (preg_match('(ON DELETE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) {
$onDelete = $match[1];
}
if (preg_match('/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)/', $tableForeignKey['condef'], $values)) {
// PostgreSQL returns identifiers that are keywords with quotes, we need them later, don't get
// the idea to trim them here.
$localColumns = array_map('trim', explode(',', $values[1]));
$foreignColumns = array_map('trim', explode(',', $values[3]));
$foreignTable = $values[2];
}
$result = preg_match('/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)/', $tableForeignKey['condef'], $values);
assert($result === 1);
// PostgreSQL returns identifiers that are keywords with quotes, we need them later, don't get
// the idea to trim them here.
$localColumns = array_map('trim', explode(',', $values[1]));
$foreignColumns = array_map('trim', explode(',', $values[3]));
$foreignTable = $values[2];
return new ForeignKeyConstraint(
$localColumns,
@@ -220,8 +231,7 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
implode(' ,', $colNumbers)
);
$stmt = $this->_conn->executeQuery($columnNameSql);
$indexColumns = $stmt->fetchAll();
$indexColumns = $this->_conn->fetchAllAssociative($columnNameSql);
// required for getting the order of the columns right.
foreach ($colNumbers as $colNum) {
@@ -299,7 +309,9 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
if (! isset($sequence['increment_by'], $sequence['min_value'])) {
/** @var string[] $data */
$data = $this->_conn->fetchAssoc('SELECT min_value, increment_by FROM ' . $this->_platform->quoteIdentifier($sequenceName));
$data = $this->_conn->fetchAssoc(
'SELECT min_value, increment_by FROM ' . $this->_platform->quoteIdentifier($sequenceName)
);
$sequence += $data;
}
@@ -323,27 +335,33 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
$matches = [];
$autoincrement = false;
if (preg_match("/^nextval\('(.*)'(::.*)?\)$/", $tableColumn['default'], $matches)) {
if (
$tableColumn['default'] !== null
&& preg_match("/^nextval\('(.*)'(::.*)?\)$/", $tableColumn['default'], $matches) === 1
) {
$tableColumn['sequence'] = $matches[1];
$tableColumn['default'] = null;
$autoincrement = true;
}
if (preg_match("/^['(](.*)[')]::.*$/", $tableColumn['default'], $matches)) {
$tableColumn['default'] = $matches[1];
}
if (stripos($tableColumn['default'], 'NULL') === 0) {
$tableColumn['default'] = null;
if ($tableColumn['default'] !== null) {
if (preg_match("/^['(](.*)[')]::/", $tableColumn['default'], $matches) === 1) {
$tableColumn['default'] = $matches[1];
} elseif (preg_match('/^NULL::/', $tableColumn['default']) === 1) {
$tableColumn['default'] = null;
}
}
$length = $tableColumn['length'] ?? null;
if ($length === '-1' && isset($tableColumn['atttypmod'])) {
$length = $tableColumn['atttypmod'] - 4;
}
if ((int) $length <= 0) {
$length = null;
}
$fixed = null;
if (! isset($tableColumn['name'])) {
@@ -355,7 +373,11 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
$jsonb = null;
$dbType = strtolower($tableColumn['type']);
if (strlen($tableColumn['domain_type']) && ! $this->_platform->hasDoctrineTypeMappingFor($tableColumn['type'])) {
if (
$tableColumn['domain_type'] !== null
&& $tableColumn['domain_type'] !== ''
&& ! $this->_platform->hasDoctrineTypeMappingFor($tableColumn['type'])
) {
$dbType = strtolower($tableColumn['domain_type']);
$tableColumn['complete_type'] = $tableColumn['domain_complete_type'];
}
@@ -370,17 +392,20 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
$tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']);
$length = null;
break;
case 'int':
case 'int4':
case 'integer':
$tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']);
$length = null;
break;
case 'bigint':
case 'int8':
$tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']);
$length = null;
break;
case 'bool':
case 'boolean':
if ($tableColumn['default'] === 'true') {
@@ -393,18 +418,22 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
$length = null;
break;
case 'text':
$fixed = false;
break;
case 'varchar':
case 'interval':
case '_varchar':
case 'varchar':
$tableColumn['default'] = $this->parseDefaultExpression($tableColumn['default']);
$fixed = false;
break;
case 'interval':
$fixed = false;
break;
case 'char':
case 'bpchar':
$fixed = true;
break;
case 'float':
case 'float4':
case 'float8':
@@ -421,7 +450,9 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
$scale = $match[2];
$length = null;
}
break;
case 'year':
$length = null;
break;
@@ -456,7 +487,7 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
$column->setPlatformOption('collation', $tableColumn['collation']);
}
if (in_array($column->getType()->getName(), [Type::JSON_ARRAY, Type::JSON], true)) {
if (in_array($column->getType()->getName(), [Types::JSON_ARRAY, Types::JSON], true)) {
$column->setPlatformOption('jsonb', $jsonb);
}
@@ -472,10 +503,42 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
*/
private function fixVersion94NegativeNumericDefaultValue($defaultValue)
{
if (strpos($defaultValue, '(') === 0) {
if ($defaultValue !== null && strpos($defaultValue, '(') === 0) {
return trim($defaultValue, '()');
}
return $defaultValue;
}
/**
* Parses a default value expression as given by PostgreSQL
*/
private function parseDefaultExpression(?string $default): ?string
{
if ($default === null) {
return $default;
}
return str_replace("''", "'", $default);
}
/**
* {@inheritdoc}
*/
public function listTableDetails($name): Table
{
$table = parent::listTableDetails($name);
$platform = $this->_platform;
assert($platform instanceof PostgreSqlPlatform);
$sql = $platform->getListTableMetadataSQL($name);
$tableOptions = $this->_conn->fetchAssoc($sql);
if ($tableOptions !== false) {
$table->addOption('comment', $tableOptions['table_comment']);
}
return $table;
}
}

View File

@@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Platforms\SQLAnywherePlatform;
use Doctrine\DBAL\Types\Type;
use function assert;
use function preg_replace;
@@ -46,6 +47,8 @@ class SQLAnywhereSchemaManager extends AbstractSchemaManager
* Starts a database.
*
* @param string $database The name of the database to start.
*
* @return void
*/
public function startDatabase($database)
{
@@ -57,6 +60,8 @@ class SQLAnywhereSchemaManager extends AbstractSchemaManager
* Stops a database.
*
* @param string $database The name of the database to stop.
*
* @return void
*/
public function stopDatabase($database)
{
@@ -107,6 +112,7 @@ class SQLAnywhereSchemaManager extends AbstractSchemaManager
case 'char':
case 'nchar':
$fixed = true;
break;
}
switch ($type) {
@@ -114,6 +120,7 @@ class SQLAnywhereSchemaManager extends AbstractSchemaManager
case 'float':
$precision = $tableColumn['length'];
$scale = $tableColumn['scale'];
break;
}
return new Column(
@@ -193,9 +200,9 @@ class SQLAnywhereSchemaManager extends AbstractSchemaManager
/**
* {@inheritdoc}
*/
protected function _getPortableTableIndexesList($tableIndexRows, $tableName = null)
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
foreach ($tableIndexRows as &$tableIndex) {
foreach ($tableIndexes as &$tableIndex) {
$tableIndex['primary'] = (bool) $tableIndex['primary'];
$tableIndex['flags'] = [];
@@ -214,7 +221,7 @@ class SQLAnywhereSchemaManager extends AbstractSchemaManager
$tableIndex['flags'][] = 'for_olap_workload';
}
return parent::_getPortableTableIndexesList($tableIndexRows, $tableName);
return parent::_getPortableTableIndexesList($tableIndexes, $tableName);
}
/**
@@ -222,9 +229,8 @@ class SQLAnywhereSchemaManager extends AbstractSchemaManager
*/
protected function _getPortableViewDefinition($view)
{
return new View(
$view['table_name'],
preg_replace('/^.*\s+as\s+SELECT(.*)/i', 'SELECT$1', $view['view_def'])
);
$definition = preg_replace('/^.*\s+as\s+SELECT(.*)/i', 'SELECT$1', $view['view_def']);
return new View($view['table_name'], $definition);
}
}

View File

@@ -3,17 +3,20 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\DriverException;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Platforms\SQLServerPlatform;
use Doctrine\DBAL\Types\Type;
use PDOException;
use Throwable;
use function assert;
use function count;
use function in_array;
use function preg_replace;
use function is_string;
use function preg_match;
use function sprintf;
use function str_replace;
use function strpos;
use function strtok;
use function trim;
/**
* SQL Server Schema Manager.
@@ -29,8 +32,9 @@ class SQLServerSchemaManager extends AbstractSchemaManager
parent::dropDatabase($database);
} catch (DBALException $exception) {
$exception = $exception->getPrevious();
assert($exception instanceof Throwable);
if (! $exception instanceof DriverException) {
if (! $exception instanceof Exception) {
throw $exception;
}
@@ -61,7 +65,9 @@ class SQLServerSchemaManager extends AbstractSchemaManager
*/
protected function _getPortableTableColumnDefinition($tableColumn)
{
$dbType = strtok($tableColumn['type'], '(), ');
$dbType = strtok($tableColumn['type'], '(), ');
assert(is_string($dbType));
$fixed = null;
$length = (int) $tableColumn['length'];
$default = $tableColumn['default'];
@@ -71,15 +77,7 @@ class SQLServerSchemaManager extends AbstractSchemaManager
}
if ($default !== null) {
while ($default !== ($default2 = preg_replace('/^\((.*)\)$/', '$1', $default))) {
$default = trim($default2, "'");
if ($default !== 'getdate()') {
continue;
}
$default = $this->_platform->getCurrentTimestampSQL();
}
$default = $this->parseDefaultExpression($default);
}
switch ($dbType) {
@@ -89,11 +87,13 @@ class SQLServerSchemaManager extends AbstractSchemaManager
// Unicode data requires 2 bytes per character
$length /= 2;
break;
case 'varchar':
// TEXT type is returned as VARCHAR(MAX) with a length of -1
if ($length === -1) {
$dbType = 'text';
}
break;
}
@@ -109,7 +109,7 @@ class SQLServerSchemaManager extends AbstractSchemaManager
'length' => $length === 0 || ! in_array($type, ['text', 'string']) ? null : $length,
'unsigned' => false,
'fixed' => (bool) $fixed,
'default' => $default !== 'NULL' ? $default : null,
'default' => $default,
'notnull' => (bool) $tableColumn['notnull'],
'scale' => $tableColumn['scale'],
'precision' => $tableColumn['precision'],
@@ -126,6 +126,27 @@ class SQLServerSchemaManager extends AbstractSchemaManager
return $column;
}
private function parseDefaultExpression(string $value): ?string
{
while (preg_match('/^\((.*)\)$/s', $value, $matches)) {
$value = $matches[1];
}
if ($value === 'NULL') {
return null;
}
if (preg_match('/^\'(.*)\'$/s', $value, $matches)) {
$value = str_replace("''", "'", $matches[1]);
}
if ($value === 'getdate()') {
return $this->_platform->getCurrentTimestampSQL();
}
return $value;
}
/**
* {@inheritdoc}
*/
@@ -134,20 +155,22 @@ class SQLServerSchemaManager extends AbstractSchemaManager
$foreignKeys = [];
foreach ($tableForeignKeys as $tableForeignKey) {
if (! isset($foreignKeys[$tableForeignKey['ForeignKey']])) {
$foreignKeys[$tableForeignKey['ForeignKey']] = [
$name = $tableForeignKey['ForeignKey'];
if (! isset($foreignKeys[$name])) {
$foreignKeys[$name] = [
'local_columns' => [$tableForeignKey['ColumnName']],
'foreign_table' => $tableForeignKey['ReferenceTableName'],
'foreign_columns' => [$tableForeignKey['ReferenceColumnName']],
'name' => $tableForeignKey['ForeignKey'],
'name' => $name,
'options' => [
'onUpdate' => str_replace('_', ' ', $tableForeignKey['update_referential_action_desc']),
'onDelete' => str_replace('_', ' ', $tableForeignKey['delete_referential_action_desc']),
],
];
} else {
$foreignKeys[$tableForeignKey['ForeignKey']]['local_columns'][] = $tableForeignKey['ColumnName'];
$foreignKeys[$tableForeignKey['ForeignKey']]['foreign_columns'][] = $tableForeignKey['ReferenceColumnName'];
$foreignKeys[$name]['local_columns'][] = $tableForeignKey['ColumnName'];
$foreignKeys[$name]['foreign_columns'][] = $tableForeignKey['ReferenceColumnName'];
}
}
@@ -157,15 +180,15 @@ class SQLServerSchemaManager extends AbstractSchemaManager
/**
* {@inheritdoc}
*/
protected function _getPortableTableIndexesList($tableIndexRows, $tableName = null)
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
foreach ($tableIndexRows as &$tableIndex) {
foreach ($tableIndexes as &$tableIndex) {
$tableIndex['non_unique'] = (bool) $tableIndex['non_unique'];
$tableIndex['primary'] = (bool) $tableIndex['primary'];
$tableIndex['flags'] = $tableIndex['flags'] ? [$tableIndex['flags']] : null;
}
return parent::_getPortableTableIndexesList($tableIndexRows, $tableName);
return parent::_getPortableTableIndexesList($tableIndexes, $tableName);
}
/**
@@ -216,7 +239,7 @@ class SQLServerSchemaManager extends AbstractSchemaManager
protected function _getPortableViewDefinition($view)
{
// @todo
return new View($view['name'], null);
return new View($view['name'], '');
}
/**
@@ -227,13 +250,7 @@ class SQLServerSchemaManager extends AbstractSchemaManager
$sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase());
try {
$tableIndexes = $this->_conn->fetchAll($sql);
} catch (PDOException $e) {
if ($e->getCode() === 'IMSSP') {
return [];
}
throw $e;
$tableIndexes = $this->_conn->fetchAllAssociative($sql);
} catch (DBALException $e) {
if (strpos($e->getMessage(), 'SQLSTATE [01000, 15472]') === 0) {
return [];
@@ -253,7 +270,7 @@ class SQLServerSchemaManager extends AbstractSchemaManager
if (count($tableDiff->removedColumns) > 0) {
foreach ($tableDiff->removedColumns as $col) {
$columnConstraintSql = $this->getColumnConstraintSQL($tableDiff->name, $col->getName());
foreach ($this->_conn->fetchAll($columnConstraintSql) as $constraint) {
foreach ($this->_conn->fetchAllAssociative($columnConstraintSql) as $constraint) {
$this->_conn->exec(
sprintf(
'ALTER TABLE %s DROP CONSTRAINT %s',
@@ -278,11 +295,11 @@ class SQLServerSchemaManager extends AbstractSchemaManager
*/
private function getColumnConstraintSQL($table, $column)
{
return "SELECT SysObjects.[Name]
FROM SysObjects INNER JOIN (SELECT [Name],[ID] FROM SysObjects WHERE XType = 'U') AS Tab
ON Tab.[ID] = Sysobjects.[Parent_Obj]
INNER JOIN sys.default_constraints DefCons ON DefCons.[object_id] = Sysobjects.[ID]
INNER JOIN SysColumns Col ON Col.[ColID] = DefCons.[parent_column_id] AND Col.[ID] = Tab.[ID]
return "SELECT sysobjects.[Name]
FROM sysobjects INNER JOIN (SELECT [Name],[ID] FROM sysobjects WHERE XType = 'U') AS Tab
ON Tab.[ID] = sysobjects.[Parent_Obj]
INNER JOIN sys.default_constraints DefCons ON DefCons.[object_id] = sysobjects.[ID]
INNER JOIN syscolumns Col ON Col.[ColID] = DefCons.[parent_column_id] AND Col.[ID] = Tab.[ID]
WHERE Col.[Name] = " . $this->_conn->quote($column) . ' AND Tab.[Name] = ' . $this->_conn->quote($table) . '
ORDER BY Col.[Name]';
}
@@ -307,4 +324,24 @@ class SQLServerSchemaManager extends AbstractSchemaManager
)
);
}
/**
* @param string $name
*/
public function listTableDetails($name): Table
{
$table = parent::listTableDetails($name);
$platform = $this->_platform;
assert($platform instanceof SQLServerPlatform);
$sql = $platform->getListTableMetadataSQL($name);
$tableOptions = $this->_conn->fetchAssociative($sql);
if ($tableOptions !== false) {
$table->addOption('comment', $tableOptions['table_comment']);
}
return $table;
}
}

View File

@@ -7,6 +7,7 @@ use Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector;
use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector;
use Doctrine\DBAL\Schema\Visitor\NamespaceVisitor;
use Doctrine\DBAL\Schema\Visitor\Visitor;
use function array_keys;
use function strpos;
use function strtolower;
@@ -51,7 +52,7 @@ class Schema extends AbstractAsset
protected $_sequences = [];
/** @var SchemaConfig */
protected $_schemaConfig = false;
protected $_schemaConfig;
/**
* @param Table[] $tables
@@ -67,6 +68,7 @@ class Schema extends AbstractAsset
if ($schemaConfig === null) {
$schemaConfig = new SchemaConfig();
}
$this->_schemaConfig = $schemaConfig;
$this->_setName($schemaConfig->getName() ?: 'public');
@@ -105,7 +107,11 @@ class Schema extends AbstractAsset
throw SchemaException::tableAlreadyExists($tableName);
}
if (! $table->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) {
if (
$namespaceName !== null
&& ! $table->isInDefaultNamespace($this->getName())
&& ! $this->hasNamespace($namespaceName)
) {
$this->createNamespace($namespaceName);
}
@@ -127,7 +133,11 @@ class Schema extends AbstractAsset
throw SchemaException::sequenceAlreadyExists($seqName);
}
if (! $sequence->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) {
if (
$namespaceName !== null
&& ! $sequence->isInDefaultNamespace($this->getName())
&& ! $this->hasNamespace($namespaceName)
) {
$this->createNamespace($namespaceName);
}
@@ -155,20 +165,20 @@ class Schema extends AbstractAsset
}
/**
* @param string $tableName
* @param string $name
*
* @return Table
*
* @throws SchemaException
*/
public function getTable($tableName)
public function getTable($name)
{
$tableName = $this->getFullQualifiedAssetName($tableName);
if (! isset($this->_tables[$tableName])) {
throw SchemaException::tableDoesNotExist($tableName);
$name = $this->getFullQualifiedAssetName($name);
if (! isset($this->_tables[$name])) {
throw SchemaException::tableDoesNotExist($name);
}
return $this->_tables[$tableName];
return $this->_tables[$name];
}
/**
@@ -206,29 +216,29 @@ class Schema extends AbstractAsset
/**
* Does this schema have a namespace with the given name?
*
* @param string $namespaceName
* @param string $name
*
* @return bool
*/
public function hasNamespace($namespaceName)
public function hasNamespace($name)
{
$namespaceName = strtolower($this->getUnquotedAssetName($namespaceName));
$name = strtolower($this->getUnquotedAssetName($name));
return isset($this->namespaces[$namespaceName]);
return isset($this->namespaces[$name]);
}
/**
* Does this schema have a table with the given name?
*
* @param string $tableName
* @param string $name
*
* @return bool
*/
public function hasTable($tableName)
public function hasTable($name)
{
$tableName = $this->getFullQualifiedAssetName($tableName);
$name = $this->getFullQualifiedAssetName($name);
return isset($this->_tables[$tableName]);
return isset($this->_tables[$name]);
}
/**
@@ -242,32 +252,32 @@ class Schema extends AbstractAsset
}
/**
* @param string $sequenceName
* @param string $name
*
* @return bool
*/
public function hasSequence($sequenceName)
public function hasSequence($name)
{
$sequenceName = $this->getFullQualifiedAssetName($sequenceName);
$name = $this->getFullQualifiedAssetName($name);
return isset($this->_sequences[$sequenceName]);
return isset($this->_sequences[$name]);
}
/**
* @param string $sequenceName
* @param string $name
*
* @return Sequence
*
* @throws SchemaException
*/
public function getSequence($sequenceName)
public function getSequence($name)
{
$sequenceName = $this->getFullQualifiedAssetName($sequenceName);
if (! $this->hasSequence($sequenceName)) {
throw SchemaException::sequenceDoesNotExist($sequenceName);
$name = $this->getFullQualifiedAssetName($name);
if (! $this->hasSequence($name)) {
throw SchemaException::sequenceDoesNotExist($name);
}
return $this->_sequences[$sequenceName];
return $this->_sequences[$name];
}
/**
@@ -281,21 +291,21 @@ class Schema extends AbstractAsset
/**
* Creates a new namespace.
*
* @param string $namespaceName The name of the namespace to create.
* @param string $name The name of the namespace to create.
*
* @return \Doctrine\DBAL\Schema\Schema This schema instance.
* @return Schema This schema instance.
*
* @throws SchemaException
*/
public function createNamespace($namespaceName)
public function createNamespace($name)
{
$unquotedNamespaceName = strtolower($this->getUnquotedAssetName($namespaceName));
$unquotedName = strtolower($this->getUnquotedAssetName($name));
if (isset($this->namespaces[$unquotedNamespaceName])) {
throw SchemaException::namespaceAlreadyExists($unquotedNamespaceName);
if (isset($this->namespaces[$unquotedName])) {
throw SchemaException::namespaceAlreadyExists($unquotedName);
}
$this->namespaces[$unquotedNamespaceName] = $namespaceName;
$this->namespaces[$unquotedName] = $name;
return $this;
}
@@ -303,17 +313,17 @@ class Schema extends AbstractAsset
/**
* Creates a new table.
*
* @param string $tableName
* @param string $name
*
* @return Table
*/
public function createTable($tableName)
public function createTable($name)
{
$table = new Table($tableName);
$table = new Table($name);
$this->_addTable($table);
foreach ($this->_schemaConfig->getDefaultTableOptions() as $name => $value) {
$table->addOption($name, $value);
foreach ($this->_schemaConfig->getDefaultTableOptions() as $option => $value) {
$table->addOption($option, $value);
}
return $table;
@@ -322,17 +332,17 @@ class Schema extends AbstractAsset
/**
* Renames a table.
*
* @param string $oldTableName
* @param string $newTableName
* @param string $oldName
* @param string $newName
*
* @return \Doctrine\DBAL\Schema\Schema
* @return Schema
*/
public function renameTable($oldTableName, $newTableName)
public function renameTable($oldName, $newName)
{
$table = $this->getTable($oldTableName);
$table->_setName($newTableName);
$table = $this->getTable($oldName);
$table->_setName($newName);
$this->dropTable($oldTableName);
$this->dropTable($oldName);
$this->_addTable($table);
return $this;
@@ -341,15 +351,15 @@ class Schema extends AbstractAsset
/**
* Drops a table from the schema.
*
* @param string $tableName
* @param string $name
*
* @return \Doctrine\DBAL\Schema\Schema
* @return Schema
*/
public function dropTable($tableName)
public function dropTable($name)
{
$tableName = $this->getFullQualifiedAssetName($tableName);
$this->getTable($tableName);
unset($this->_tables[$tableName]);
$name = $this->getFullQualifiedAssetName($name);
$this->getTable($name);
unset($this->_tables[$name]);
return $this;
}
@@ -357,29 +367,29 @@ class Schema extends AbstractAsset
/**
* Creates a new sequence.
*
* @param string $sequenceName
* @param string $name
* @param int $allocationSize
* @param int $initialValue
*
* @return Sequence
*/
public function createSequence($sequenceName, $allocationSize = 1, $initialValue = 1)
public function createSequence($name, $allocationSize = 1, $initialValue = 1)
{
$seq = new Sequence($sequenceName, $allocationSize, $initialValue);
$seq = new Sequence($name, $allocationSize, $initialValue);
$this->_addSequence($seq);
return $seq;
}
/**
* @param string $sequenceName
* @param string $name
*
* @return \Doctrine\DBAL\Schema\Schema
* @return Schema
*/
public function dropSequence($sequenceName)
public function dropSequence($name)
{
$sequenceName = $this->getFullQualifiedAssetName($sequenceName);
unset($this->_sequences[$sequenceName]);
$name = $this->getFullQualifiedAssetName($name);
unset($this->_sequences[$name]);
return $this;
}
@@ -464,6 +474,7 @@ class Schema extends AbstractAsset
foreach ($this->_tables as $k => $table) {
$this->_tables[$k] = clone $table;
}
foreach ($this->_sequences as $k => $sequence) {
$this->_sequences[$k] = clone $sequence;
}

View File

@@ -3,6 +3,7 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use function array_merge;
/**
@@ -10,7 +11,7 @@ use function array_merge;
*/
class SchemaDiff
{
/** @var Schema */
/** @var Schema|null */
public $fromSchema;
/**
@@ -151,6 +152,7 @@ class SchemaDiff
$foreignKeySql[] = $platform->getCreateForeignKeySQL($foreignKey, $table);
}
}
$sql = array_merge($sql, $foreignKeySql);
if ($saveMode === false) {

View File

@@ -2,11 +2,15 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Exception;
use function implode;
use function sprintf;
class SchemaException extends DBALException
/**
* @psalm-immutable
*/
class SchemaException extends Exception
{
public const TABLE_DOESNT_EXIST = 10;
public const TABLE_ALREADY_EXISTS = 20;
@@ -23,7 +27,7 @@ class SchemaException extends DBALException
/**
* @param string $tableName
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function tableDoesNotExist($tableName)
{
@@ -33,7 +37,7 @@ class SchemaException extends DBALException
/**
* @param string $indexName
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function indexNameInvalid($indexName)
{
@@ -47,7 +51,7 @@ class SchemaException extends DBALException
* @param string $indexName
* @param string $table
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function indexDoesNotExist($indexName, $table)
{
@@ -61,7 +65,7 @@ class SchemaException extends DBALException
* @param string $indexName
* @param string $table
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function indexAlreadyExists($indexName, $table)
{
@@ -75,7 +79,7 @@ class SchemaException extends DBALException
* @param string $columnName
* @param string $table
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function columnDoesNotExist($columnName, $table)
{
@@ -88,7 +92,7 @@ class SchemaException extends DBALException
/**
* @param string $namespaceName
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function namespaceAlreadyExists($namespaceName)
{
@@ -101,7 +105,7 @@ class SchemaException extends DBALException
/**
* @param string $tableName
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function tableAlreadyExists($tableName)
{
@@ -112,7 +116,7 @@ class SchemaException extends DBALException
* @param string $tableName
* @param string $columnName
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function columnAlreadyExists($tableName, $columnName)
{
@@ -123,30 +127,30 @@ class SchemaException extends DBALException
}
/**
* @param string $sequenceName
* @param string $name
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function sequenceAlreadyExists($sequenceName)
public static function sequenceAlreadyExists($name)
{
return new self("The sequence '" . $sequenceName . "' already exists.", self::SEQUENCE_ALREADY_EXISTS);
return new self("The sequence '" . $name . "' already exists.", self::SEQUENCE_ALREADY_EXISTS);
}
/**
* @param string $sequenceName
* @param string $name
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function sequenceDoesNotExist($sequenceName)
public static function sequenceDoesNotExist($name)
{
return new self("There exists no sequence with the name '" . $sequenceName . "'.", self::SEQUENCE_DOENST_EXIST);
return new self("There exists no sequence with the name '" . $name . "'.", self::SEQUENCE_DOENST_EXIST);
}
/**
* @param string $fkName
* @param string $table
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function foreignKeyDoesNotExist($fkName, $table)
{
@@ -157,22 +161,22 @@ class SchemaException extends DBALException
}
/**
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function namedForeignKeyRequired(Table $localTable, ForeignKeyConstraint $foreignKey)
{
return new self(
'The performed schema operation on ' . $localTable->getName() . ' requires a named foreign key, ' .
'but the given foreign key from (' . implode(', ', $foreignKey->getColumns()) . ') onto foreign table ' .
"'" . $foreignKey->getForeignTableName() . "' (" . implode(', ', $foreignKey->getForeignColumns()) . ') is currently ' .
'unnamed.'
"'" . $foreignKey->getForeignTableName() . "' (" . implode(', ', $foreignKey->getForeignColumns()) . ')' .
' is currently unnamed.'
);
}
/**
* @param string $changeName
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function alterTableChangeNotSupported($changeName)
{

View File

@@ -3,8 +3,8 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Schema\Visitor\Visitor;
use function count;
use function is_numeric;
use function sprintf;
/**
@@ -19,7 +19,7 @@ class Sequence extends AbstractAsset
protected $initialValue = 1;
/** @var int|null */
protected $cache = null;
protected $cache;
/**
* @param string $name
@@ -62,11 +62,11 @@ class Sequence extends AbstractAsset
/**
* @param int $allocationSize
*
* @return \Doctrine\DBAL\Schema\Sequence
* @return Sequence
*/
public function setAllocationSize($allocationSize)
{
$this->allocationSize = is_numeric($allocationSize) ? (int) $allocationSize : 1;
$this->allocationSize = (int) $allocationSize ?: 1;
return $this;
}
@@ -74,11 +74,11 @@ class Sequence extends AbstractAsset
/**
* @param int $initialValue
*
* @return \Doctrine\DBAL\Schema\Sequence
* @return Sequence
*/
public function setInitialValue($initialValue)
{
$this->initialValue = is_numeric($initialValue) ? (int) $initialValue : 1;
$this->initialValue = (int) $initialValue ?: 1;
return $this;
}
@@ -86,7 +86,7 @@ class Sequence extends AbstractAsset
/**
* @param int $cache
*
* @return \Doctrine\DBAL\Schema\Sequence
* @return Sequence
*/
public function setCache($cache)
{
@@ -105,11 +105,13 @@ class Sequence extends AbstractAsset
*/
public function isAutoIncrementsFor(Table $table)
{
if (! $table->hasPrimaryKey()) {
$primaryKey = $table->getPrimaryKey();
if ($primaryKey === null) {
return false;
}
$pkColumns = $table->getPrimaryKey()->getColumns();
$pkColumns = $primaryKey->getColumns();
if (count($pkColumns) !== 1) {
return false;

View File

@@ -2,17 +2,16 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Types\StringType;
use Doctrine\DBAL\Types\TextType;
use Doctrine\DBAL\Types\Type;
use const CASE_LOWER;
use function array_change_key_case;
use function array_map;
use function array_merge;
use function array_reverse;
use function array_values;
use function explode;
use function file_exists;
use function preg_match;
@@ -28,6 +27,8 @@ use function trim;
use function unlink;
use function usort;
use const CASE_LOWER;
/**
* Sqlite SchemaManager.
*/
@@ -50,13 +51,12 @@ class SqliteSchemaManager extends AbstractSchemaManager
*/
public function createDatabase($database)
{
$params = $this->_conn->getParams();
$driver = $params['driver'];
$options = [
'driver' => $driver,
'path' => $database,
];
$conn = DriverManager::getConnection($options);
$params = $this->_conn->getParams();
$params['path'] = $database;
unset($params['memory']);
$conn = DriverManager::getConnection($params);
$conn->connect();
$conn->close();
}
@@ -77,7 +77,7 @@ class SqliteSchemaManager extends AbstractSchemaManager
*/
public function createForeignKey(ForeignKeyConstraint $foreignKey, $table)
{
$tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table);
$tableDiff = $this->getTableDiffForAlterForeignKey($table);
$tableDiff->addedForeignKeys[] = $foreignKey;
$this->alterTable($tableDiff);
@@ -88,7 +88,7 @@ class SqliteSchemaManager extends AbstractSchemaManager
*/
public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table)
{
$tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table);
$tableDiff = $this->getTableDiffForAlterForeignKey($table);
$tableDiff->changedForeignKeys[] = $foreignKey;
$this->alterTable($tableDiff);
@@ -99,7 +99,7 @@ class SqliteSchemaManager extends AbstractSchemaManager
*/
public function dropForeignKey($foreignKey, $table)
{
$tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table);
$tableDiff = $this->getTableDiffForAlterForeignKey($table);
$tableDiff->removedForeignKeys[] = $foreignKey;
$this->alterTable($tableDiff);
@@ -113,14 +113,16 @@ class SqliteSchemaManager extends AbstractSchemaManager
if ($database === null) {
$database = $this->_conn->getDatabase();
}
$sql = $this->_platform->getListTableForeignKeysSQL($table, $database);
$tableForeignKeys = $this->_conn->fetchAll($sql);
$tableForeignKeys = $this->_conn->fetchAllAssociative($sql);
if (! empty($tableForeignKeys)) {
$createSql = $this->getCreateTableSQL($table);
if ($createSql !== null && preg_match_all(
'#
if (
$createSql !== null && preg_match_all(
'#
(?:CONSTRAINT\s+([^\s]+)\s+)?
(?:FOREIGN\s+KEY[^\)]+\)\s*)?
REFERENCES\s+[^\s]+\s+(?:\([^\)]+\))?
@@ -129,9 +131,10 @@ class SqliteSchemaManager extends AbstractSchemaManager
(NOT\s+DEFERRABLE|DEFERRABLE)
(?:\s+INITIALLY\s+(DEFERRED|IMMEDIATE))?
)?#isx',
$createSql,
$match
)) {
$createSql,
$match
)
) {
$names = array_reverse($match[1]);
$deferrable = array_reverse($match[2]);
$deferred = array_reverse($match[3]);
@@ -140,10 +143,13 @@ class SqliteSchemaManager extends AbstractSchemaManager
}
foreach ($tableForeignKeys as $key => $value) {
$id = $value['id'];
$tableForeignKeys[$key]['constraint_name'] = isset($names[$id]) && $names[$id] !== '' ? $names[$id] : $id;
$tableForeignKeys[$key]['deferrable'] = isset($deferrable[$id]) && strtolower($deferrable[$id]) === 'deferrable';
$tableForeignKeys[$key]['deferred'] = isset($deferred[$id]) && strtolower($deferred[$id]) === 'deferred';
$id = $value['id'];
$tableForeignKeys[$key] = array_merge($tableForeignKeys[$key], [
'constraint_name' => isset($names[$id]) && $names[$id] !== '' ? $names[$id] : $id,
'deferrable' => isset($deferrable[$id]) && strtolower($deferrable[$id]) === 'deferrable',
'deferred' => isset($deferred[$id]) && strtolower($deferred[$id]) === 'deferred',
]);
}
}
@@ -168,21 +174,28 @@ class SqliteSchemaManager extends AbstractSchemaManager
$indexBuffer = [];
// fetch primary
$stmt = $this->_conn->executeQuery(sprintf(
$indexArray = $this->_conn->fetchAllAssociative(sprintf(
'PRAGMA TABLE_INFO (%s)',
$this->_conn->quote($tableName)
));
$indexArray = $stmt->fetchAll(FetchMode::ASSOCIATIVE);
usort($indexArray, static function ($a, $b) {
if ($a['pk'] === $b['pk']) {
return $a['cid'] - $b['cid'];
usort(
$indexArray,
/**
* @param array<string,mixed> $a
* @param array<string,mixed> $b
*/
static function (array $a, array $b): int {
if ($a['pk'] === $b['pk']) {
return $a['cid'] - $b['cid'];
}
return $a['pk'] - $b['pk'];
}
);
return $a['pk'] - $b['pk'];
});
foreach ($indexArray as $indexColumnRow) {
if ($indexColumnRow['pk'] === '0') {
if ($indexColumnRow['pk'] === 0 || $indexColumnRow['pk'] === '0') {
continue;
}
@@ -205,13 +218,12 @@ class SqliteSchemaManager extends AbstractSchemaManager
$idx = [];
$idx['key_name'] = $keyName;
$idx['primary'] = false;
$idx['non_unique'] = $tableIndex['unique']?false:true;
$idx['non_unique'] = ! $tableIndex['unique'];
$stmt = $this->_conn->executeQuery(sprintf(
'PRAGMA INDEX_INFO (%s)',
$this->_conn->quote($keyName)
));
$indexArray = $stmt->fetchAll(FetchMode::ASSOCIATIVE);
$indexArray = $this->_conn->fetchAllAssociative(sprintf(
'PRAGMA INDEX_INFO (%s)',
$this->_conn->quote($keyName)
));
foreach ($indexArray as $indexColumnRow) {
$idx['column_name'] = $indexColumnRow['name'];
@@ -223,7 +235,11 @@ class SqliteSchemaManager extends AbstractSchemaManager
}
/**
* {@inheritdoc}
* @deprecated
*
* @param array<string, mixed> $tableIndex
*
* @return array<string, bool|string>
*/
protected function _getPortableTableIndexDefinition($tableIndex)
{
@@ -245,7 +261,7 @@ class SqliteSchemaManager extends AbstractSchemaManager
$autoincrementCount = 0;
foreach ($tableColumns as $tableColumn) {
if ($tableColumn['pk'] === '0') {
if ($tableColumn['pk'] === 0 || $tableColumn['pk'] === '0') {
continue;
}
@@ -274,7 +290,10 @@ class SqliteSchemaManager extends AbstractSchemaManager
$type = $column->getType();
if ($type instanceof StringType || $type instanceof TextType) {
$column->setPlatformOption('collation', $this->parseColumnCollationFromSQL($columnName, $createSql) ?: 'BINARY');
$column->setPlatformOption(
'collation',
$this->parseColumnCollationFromSQL($columnName, $createSql) ?: 'BINARY'
);
}
$comment = $this->parseColumnCommentFromSQL($columnName, $createSql);
@@ -283,9 +302,9 @@ class SqliteSchemaManager extends AbstractSchemaManager
continue;
}
$type = $this->extractDoctrineTypeFromComment($comment, null);
$type = $this->extractDoctrineTypeFromComment($comment, '');
if ($type !== null) {
if ($type !== '') {
$column->setType(Type::getType($type));
$comment = $this->removeDoctrineTypeFromComment($comment, $type);
@@ -324,10 +343,14 @@ class SqliteSchemaManager extends AbstractSchemaManager
if ($default === 'NULL') {
$default = null;
}
if ($default !== null) {
// SQLite returns strings wrapped in single quotes, so we need to strip them
$default = preg_replace("/^'(.*)'$/", '\1', $default);
// SQLite returns the default value as a literal expression, so we need to parse it
if (preg_match('/^\'(.*)\'$/s', $default, $matches)) {
$default = str_replace("''", "'", $matches[1]);
}
}
$notnull = (bool) $tableColumn['notnull'];
if (! isset($tableColumn['name'])) {
@@ -350,15 +373,17 @@ class SqliteSchemaManager extends AbstractSchemaManager
if (strpos($tableColumn['length'], ',') === false) {
$tableColumn['length'] .= ',0';
}
[$precision, $scale] = array_map('trim', explode(',', $tableColumn['length']));
}
$length = null;
break;
}
$options = [
'length' => $length,
'unsigned' => (bool) $unsigned,
'unsigned' => $unsigned,
'fixed' => $fixed,
'notnull' => $notnull,
'default' => $default,
@@ -391,6 +416,7 @@ class SqliteSchemaManager extends AbstractSchemaManager
if (! isset($value['on_delete']) || $value['on_delete'] === 'RESTRICT') {
$value['on_delete'] = null;
}
if (! isset($value['on_update']) || $value['on_update'] === 'RESTRICT') {
$value['on_update'] = null;
}
@@ -403,25 +429,31 @@ class SqliteSchemaManager extends AbstractSchemaManager
'onDelete' => $value['on_delete'],
'onUpdate' => $value['on_update'],
'deferrable' => $value['deferrable'],
'deferred'=> $value['deferred'],
'deferred' => $value['deferred'],
];
}
$list[$name]['local'][] = $value['from'];
$list[$name]['local'][] = $value['from'];
if ($value['to'] === null) {
continue;
}
$list[$name]['foreign'][] = $value['to'];
}
$result = [];
foreach ($list as $constraint) {
$result[] = new ForeignKeyConstraint(
array_values($constraint['local']),
$constraint['local'],
$constraint['foreignTable'],
array_values($constraint['foreign']),
$constraint['foreign'],
$constraint['name'],
[
'onDelete' => $constraint['onDelete'],
'onUpdate' => $constraint['onUpdate'],
'deferrable' => $constraint['deferrable'],
'deferred'=> $constraint['deferred'],
'deferred' => $constraint['deferred'],
]
);
}
@@ -434,14 +466,17 @@ class SqliteSchemaManager extends AbstractSchemaManager
*
* @return TableDiff
*
* @throws DBALException
* @throws Exception
*/
private function getTableDiffForAlterForeignKey(ForeignKeyConstraint $foreignKey, $table)
private function getTableDiffForAlterForeignKey($table)
{
if (! $table instanceof Table) {
$tableDetails = $this->tryMethod('listTableDetails', $table);
if ($table === false) {
throw new DBALException(sprintf('Sqlite schema manager requires to modify foreign keys table definition "%s".', $table));
if ($tableDetails === false) {
throw new Exception(
sprintf('Sqlite schema manager requires to modify foreign keys table definition "%s".', $table)
);
}
$table = $tableDetails;
@@ -453,9 +488,10 @@ class SqliteSchemaManager extends AbstractSchemaManager
return $tableDiff;
}
private function parseColumnCollationFromSQL(string $column, string $sql) : ?string
private function parseColumnCollationFromSQL(string $column, string $sql): ?string
{
$pattern = '{(?:\W' . preg_quote($column) . '\W|\W' . preg_quote($this->_platform->quoteSingleIdentifier($column))
$pattern = '{(?:\W' . preg_quote($column) . '\W|\W'
. preg_quote($this->_platform->quoteSingleIdentifier($column))
. '\W)[^,(]+(?:\([^()]+\)[^,]*)?(?:(?:DEFAULT|CHECK)\s*(?:\(.*?\))?[^,]*)*COLLATE\s+["\']?([^\s,"\')]+)}is';
if (preg_match($pattern, $sql, $match) !== 1) {
@@ -465,10 +501,15 @@ class SqliteSchemaManager extends AbstractSchemaManager
return $match[1];
}
private function parseColumnCommentFromSQL(string $column, string $sql) : ?string
private function parseTableCommentFromSQL(string $table, string $sql): ?string
{
$pattern = '{[\s(,](?:\W' . preg_quote($this->_platform->quoteSingleIdentifier($column)) . '\W|\W' . preg_quote($column)
. '\W)(?:\(.*?\)|[^,(])*?,?((?:(?!\n))(?:\s*--[^\n]*\n?)+)}i';
$pattern = '/\s* # Allow whitespace characters at start of line
CREATE\sTABLE # Match "CREATE TABLE"
(?:\W"' . preg_quote($this->_platform->quoteSingleIdentifier($table), '/') . '"\W|\W' . preg_quote($table, '/')
. '\W) # Match table name (quoted and unquoted)
( # Start capture
(?:\s*--[^\n]*\n?)+ # Capture anything that starts with whitespaces followed by -- until the end of the line(s)
)/ix';
if (preg_match($pattern, $sql, $match) !== 1) {
return null;
@@ -479,7 +520,21 @@ class SqliteSchemaManager extends AbstractSchemaManager
return $comment === '' ? null : $comment;
}
private function getCreateTableSQL(string $table) : ?string
private function parseColumnCommentFromSQL(string $column, string $sql): ?string
{
$pattern = '{[\s(,](?:\W' . preg_quote($this->_platform->quoteSingleIdentifier($column))
. '\W|\W' . preg_quote($column) . '\W)(?:\([^)]*?\)|[^,(])*?,?((?:(?!\n))(?:\s*--[^\n]*\n?)+)}i';
if (preg_match($pattern, $sql, $match) !== 1) {
return null;
}
$comment = preg_replace('{^\s*--}m', '', rtrim($match[1], "\n"));
return $comment === '' ? null : $comment;
}
private function getCreateTableSQL(string $table): ?string
{
return $this->_conn->fetchColumn(
<<<'SQL'
@@ -498,4 +553,22 @@ SQL
[$table]
) ?: null;
}
/**
* @param string $name
*/
public function listTableDetails($name): Table
{
$table = parent::listTableDetails($name);
$tableCreateSql = $this->getCreateTableSQL($name) ?? '';
$comment = $this->parseTableCommentFromSQL($name, $tableCreateSql);
if ($comment !== null) {
$table->addOption('comment', $comment);
}
return $table;
}
}

View File

@@ -3,10 +3,13 @@
namespace Doctrine\DBAL\Schema\Synchronizer;
use Doctrine\DBAL\Connection;
use Doctrine\Deprecations\Deprecation;
use Throwable;
/**
* Abstract schema synchronizer with methods for executing batches of SQL.
*
* @deprecated
*/
abstract class AbstractSchemaSynchronizer implements SchemaSynchronizer
{
@@ -16,10 +19,18 @@ abstract class AbstractSchemaSynchronizer implements SchemaSynchronizer
public function __construct(Connection $conn)
{
$this->conn = $conn;
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4213',
'SchemaSynchronizer API is deprecated without a replacement and will be removed in DBAL 3.0'
);
}
/**
* @param string[] $sql
*
* @return void
*/
protected function processSqlSafely(array $sql)
{
@@ -33,6 +44,8 @@ abstract class AbstractSchemaSynchronizer implements SchemaSynchronizer
/**
* @param string[] $sql
*
* @return void
*/
protected function processSql(array $sql)
{

View File

@@ -7,6 +7,8 @@ use Doctrine\DBAL\Schema\Schema;
/**
* The synchronizer knows how to synchronize a schema with the configured
* database.
*
* @deprecated
*/
interface SchemaSynchronizer
{

View File

@@ -7,10 +7,13 @@ use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\Comparator;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector;
use function count;
/**
* Schema Synchronizer for Default DBAL Connection.
*
* @deprecated
*/
class SingleDatabaseSynchronizer extends AbstractSchemaSynchronizer
{
@@ -31,7 +34,6 @@ class SingleDatabaseSynchronizer extends AbstractSchemaSynchronizer
return $createSchema->toSql($this->platform);
}
/**
* {@inheritdoc}
*/
@@ -87,11 +89,14 @@ class SingleDatabaseSynchronizer extends AbstractSchemaSynchronizer
}
foreach ($dropSchema->getTables() as $table) {
if (! $table->hasPrimaryKey()) {
$primaryKey = $table->getPrimaryKey();
if ($primaryKey === null) {
continue;
}
$columns = $table->getPrimaryKey()->getColumns();
$columns = $primaryKey->getColumns();
if (count($columns) > 1) {
continue;
}

View File

@@ -2,27 +2,24 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Schema\Visitor\Visitor;
use Doctrine\DBAL\Types\Type;
use const ARRAY_FILTER_USE_KEY;
use function array_filter;
use function array_merge;
use function in_array;
use function is_numeric;
use function is_string;
use function preg_match;
use function strlen;
use function strtolower;
use const ARRAY_FILTER_USE_KEY;
/**
* Object Representation of a table.
*/
class Table extends AbstractAsset
{
/** @var string */
protected $_name = null;
/** @var Column[] */
protected $_columns = [];
@@ -32,35 +29,43 @@ class Table extends AbstractAsset
/** @var Index[] */
protected $_indexes = [];
/** @var string */
/** @var string|false */
protected $_primaryKeyName = false;
/** @var ForeignKeyConstraint[] */
protected $_fkConstraints = [];
/** @var mixed[] */
protected $_options = [];
protected $_options = [
'create_options' => [],
];
/** @var SchemaConfig|null */
protected $_schemaConfig = null;
protected $_schemaConfig;
/**
* @param string $tableName
* @param string $name
* @param Column[] $columns
* @param Index[] $indexes
* @param ForeignKeyConstraint[] $fkConstraints
* @param int $idGeneratorType
* @param mixed[] $options
*
* @throws DBALException
* @throws Exception
*/
public function __construct($tableName, array $columns = [], array $indexes = [], array $fkConstraints = [], $idGeneratorType = 0, array $options = [])
{
if (strlen($tableName) === 0) {
throw DBALException::invalidTableName($tableName);
public function __construct(
$name,
array $columns = [],
array $indexes = [],
array $fkConstraints = [],
$idGeneratorType = 0,
array $options = []
) {
if (strlen($name) === 0) {
throw Exception::invalidTableName($name);
}
$this->_setName($tableName);
$this->_setName($name);
foreach ($columns as $column) {
$this->_addColumn($column);
@@ -74,7 +79,7 @@ class Table extends AbstractAsset
$this->_addForeignKeyConstraint($constraint);
}
$this->_options = $options;
$this->_options = array_merge($this->_options, $options);
}
/**
@@ -100,16 +105,16 @@ class Table extends AbstractAsset
/**
* Sets the Primary Key.
*
* @param mixed[][] $columns
* @param string|bool $indexName
* @param string[] $columnNames
* @param string|false $indexName
*
* @return self
*/
public function setPrimaryKey(array $columns, $indexName = false)
public function setPrimaryKey(array $columnNames, $indexName = false)
{
$this->_addIndex($this->_createIndex($columns, $indexName ?: 'primary', true, true));
$this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true));
foreach ($columns as $columnName) {
foreach ($columnNames as $columnName) {
$column = $this->getColumn($columnName);
$column->setNotnull(true);
}
@@ -118,7 +123,7 @@ class Table extends AbstractAsset
}
/**
* @param mixed[][] $columnNames
* @param string[] $columnNames
* @param string|null $indexName
* @param string[] $flags
* @param mixed[] $options
@@ -145,6 +150,10 @@ class Table extends AbstractAsset
*/
public function dropPrimaryKey()
{
if ($this->_primaryKeyName === false) {
return;
}
$this->dropIndex($this->_primaryKeyName);
$this->_primaryKeyName = false;
}
@@ -152,23 +161,24 @@ class Table extends AbstractAsset
/**
* Drops an index from this table.
*
* @param string $indexName The index name.
* @param string $name The index name.
*
* @return void
*
* @throws SchemaException If the index does not exist.
*/
public function dropIndex($indexName)
public function dropIndex($name)
{
$indexName = $this->normalizeIdentifier($indexName);
if (! $this->hasIndex($indexName)) {
throw SchemaException::indexDoesNotExist($indexName, $this->_name);
$name = $this->normalizeIdentifier($name);
if (! $this->hasIndex($name)) {
throw SchemaException::indexDoesNotExist($name, $this->_name);
}
unset($this->_indexes[$indexName]);
unset($this->_indexes[$name]);
}
/**
* @param mixed[][] $columnNames
* @param string[] $columnNames
* @param string|null $indexName
* @param mixed[] $options
*
@@ -190,8 +200,8 @@ class Table extends AbstractAsset
/**
* Renames an index.
*
* @param string $oldIndexName The name of the index to rename from.
* @param string|null $newIndexName The name of the index to rename to.
* @param string $oldName The name of the index to rename from.
* @param string|null $newName The name of the index to rename to.
* If null is given, the index name will be auto-generated.
*
* @return self This table instance.
@@ -199,52 +209,51 @@ class Table extends AbstractAsset
* @throws SchemaException If no index exists for the given current name
* or if an index with the given new name already exists on this table.
*/
public function renameIndex($oldIndexName, $newIndexName = null)
public function renameIndex($oldName, $newName = null)
{
$oldIndexName = $this->normalizeIdentifier($oldIndexName);
$normalizedNewIndexName = $this->normalizeIdentifier($newIndexName);
$oldName = $this->normalizeIdentifier($oldName);
$normalizedNewName = $this->normalizeIdentifier($newName);
if ($oldIndexName === $normalizedNewIndexName) {
if ($oldName === $normalizedNewName) {
return $this;
}
if (! $this->hasIndex($oldIndexName)) {
throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name);
if (! $this->hasIndex($oldName)) {
throw SchemaException::indexDoesNotExist($oldName, $this->_name);
}
if ($this->hasIndex($normalizedNewIndexName)) {
throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name);
if ($this->hasIndex($normalizedNewName)) {
throw SchemaException::indexAlreadyExists($normalizedNewName, $this->_name);
}
$oldIndex = $this->_indexes[$oldIndexName];
$oldIndex = $this->_indexes[$oldName];
if ($oldIndex->isPrimary()) {
$this->dropPrimaryKey();
return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName);
return $this->setPrimaryKey($oldIndex->getColumns(), $newName ?? false);
}
unset($this->_indexes[$oldIndexName]);
unset($this->_indexes[$oldName]);
if ($oldIndex->isUnique()) {
return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions());
return $this->addUniqueIndex($oldIndex->getColumns(), $newName, $oldIndex->getOptions());
}
return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags(), $oldIndex->getOptions());
return $this->addIndex($oldIndex->getColumns(), $newName, $oldIndex->getFlags(), $oldIndex->getOptions());
}
/**
* Checks if an index begins in the order of the given columns.
*
* @param mixed[][] $columnsNames
* @param string[] $columnNames
*
* @return bool
*/
public function columnsAreIndexed(array $columnsNames)
public function columnsAreIndexed(array $columnNames)
{
foreach ($this->getIndexes() as $index) {
/** @var $index Index */
if ($index->spansColumns($columnsNames)) {
if ($index->spansColumns($columnNames)) {
return true;
}
}
@@ -253,28 +262,30 @@ class Table extends AbstractAsset
}
/**
* @param mixed[][] $columnNames
* @param string $indexName
* @param bool $isUnique
* @param bool $isPrimary
* @param string[] $flags
* @param mixed[] $options
* @param string[] $columnNames
* @param string $indexName
* @param bool $isUnique
* @param bool $isPrimary
* @param string[] $flags
* @param mixed[] $options
*
* @return Index
*
* @throws SchemaException
*/
private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = [], array $options = [])
{
private function _createIndex(
array $columnNames,
$indexName,
$isUnique,
$isPrimary,
array $flags = [],
array $options = []
) {
if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
throw SchemaException::indexNameInvalid($indexName);
}
foreach ($columnNames as $columnName => $indexColOptions) {
if (is_numeric($columnName) && is_string($indexColOptions)) {
$columnName = $indexColOptions;
}
foreach ($columnNames as $columnName) {
if (! $this->hasColumn($columnName)) {
throw SchemaException::columnDoesNotExist($columnName, $this->_name);
}
@@ -284,15 +295,15 @@ class Table extends AbstractAsset
}
/**
* @param string $columnName
* @param string $name
* @param string $typeName
* @param mixed[] $options
*
* @return Column
*/
public function addColumn($columnName, $typeName, array $options = [])
public function addColumn($name, $typeName, array $options = [])
{
$column = new Column($columnName, Type::getType($typeName), $options);
$column = new Column($name, Type::getType($typeName), $options);
$this->_addColumn($column);
@@ -304,14 +315,16 @@ class Table extends AbstractAsset
*
* @deprecated
*
* @param string $oldColumnName
* @param string $newColumnName
* @param string $oldName
* @param string $name
*
* @throws DBALException
* @return void
*
* @throws Exception
*/
public function renameColumn($oldColumnName, $newColumnName)
public function renameColumn($oldName, $name)
{
throw new DBALException('Table#renameColumn() was removed, because it drops and recreates ' .
throw new Exception('Table#renameColumn() was removed, because it drops and recreates ' .
'the column instead. There is no fix available, because a schema diff cannot reliably detect if a ' .
'column was renamed or one column was created and another one dropped.');
}
@@ -319,14 +332,14 @@ class Table extends AbstractAsset
/**
* Change Column Details.
*
* @param string $columnName
* @param string $name
* @param mixed[] $options
*
* @return self
*/
public function changeColumn($columnName, array $options)
public function changeColumn($name, array $options)
{
$column = $this->getColumn($columnName);
$column = $this->getColumn($name);
$column->setOptions($options);
return $this;
@@ -335,14 +348,14 @@ class Table extends AbstractAsset
/**
* Drops a Column from the Table.
*
* @param string $columnName
* @param string $name
*
* @return self
*/
public function dropColumn($columnName)
public function dropColumn($name)
{
$columnName = $this->normalizeIdentifier($columnName);
unset($this->_columns[$columnName]);
$name = $this->normalizeIdentifier($name);
unset($this->_columns[$name]);
return $this;
}
@@ -360,11 +373,26 @@ class Table extends AbstractAsset
*
* @return self
*/
public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [], $constraintName = null)
{
$constraintName = $constraintName ?: $this->_generateIdentifierName(array_merge((array) $this->getName(), $localColumnNames), 'fk', $this->_getMaxIdentifierLength());
public function addForeignKeyConstraint(
$foreignTable,
array $localColumnNames,
array $foreignColumnNames,
array $options = [],
$constraintName = null
) {
$constraintName = $constraintName ?: $this->_generateIdentifierName(
array_merge((array) $this->getName(), $localColumnNames),
'fk',
$this->_getMaxIdentifierLength()
);
return $this->addNamedForeignKeyConstraint($constraintName, $foreignTable, $localColumnNames, $foreignColumnNames, $options);
return $this->addNamedForeignKeyConstraint(
$constraintName,
$foreignTable,
$localColumnNames,
$foreignColumnNames,
$options
);
}
/**
@@ -381,8 +409,12 @@ class Table extends AbstractAsset
*
* @return self
*/
public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [])
{
public function addUnnamedForeignKeyConstraint(
$foreignTable,
array $localColumnNames,
array $foreignColumnNames,
array $options = []
) {
return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options);
}
@@ -401,8 +433,13 @@ class Table extends AbstractAsset
*
* @throws SchemaException
*/
public function addNamedForeignKeyConstraint($name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [])
{
public function addNamedForeignKeyConstraint(
$name,
$foreignTable,
array $localColumnNames,
array $foreignColumnNames,
array $options = []
) {
if ($foreignTable instanceof Table) {
foreach ($foreignColumnNames as $columnName) {
if (! $foreignTable->hasColumn($columnName)) {
@@ -431,7 +468,7 @@ class Table extends AbstractAsset
/**
* @param string $name
* @param string $value
* @param mixed $value
*
* @return self
*/
@@ -480,7 +517,8 @@ class Table extends AbstractAsset
$replacedImplicitIndexes[] = $name;
}
if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
if (
(isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
($this->_primaryKeyName !== false && $indexCandidate->isPrimary())
) {
throw SchemaException::indexAlreadyExists($indexName, $this->_name);
@@ -515,18 +553,24 @@ class Table extends AbstractAsset
$this->_getMaxIdentifierLength()
);
}
$name = $this->normalizeIdentifier($name);
$this->_fkConstraints[$name] = $constraint;
// add an explicit index on the foreign key columns. If there is already an index that fulfils this requirements drop the request.
// In the case of __construct calling this method during hydration from schema-details all the explicitly added indexes
// lead to duplicates. This creates computation overhead in this case, however no duplicate indexes are ever added (based on columns).
$indexName = $this->_generateIdentifierName(
/* Add an implicit index (defined by the DBAL) on the foreign key
columns. If there is already a user-defined index that fulfills these
requirements drop the request. In the case of __construct() calling
this method during hydration from schema-details, all the explicitly
added indexes lead to duplicates. This creates computation overhead in
this case, however no duplicate indexes are ever added (based on
columns). */
$indexName = $this->_generateIdentifierName(
array_merge([$this->getName()], $constraint->getColumns()),
'idx',
$this->_getMaxIdentifierLength()
);
$indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false);
foreach ($this->_indexes as $existingIndex) {
@@ -542,53 +586,53 @@ class Table extends AbstractAsset
/**
* Returns whether this table has a foreign key constraint with the given name.
*
* @param string $constraintName
* @param string $name
*
* @return bool
*/
public function hasForeignKey($constraintName)
public function hasForeignKey($name)
{
$constraintName = $this->normalizeIdentifier($constraintName);
$name = $this->normalizeIdentifier($name);
return isset($this->_fkConstraints[$constraintName]);
return isset($this->_fkConstraints[$name]);
}
/**
* Returns the foreign key constraint with the given name.
*
* @param string $constraintName The constraint name.
* @param string $name The constraint name.
*
* @return ForeignKeyConstraint
*
* @throws SchemaException If the foreign key does not exist.
*/
public function getForeignKey($constraintName)
public function getForeignKey($name)
{
$constraintName = $this->normalizeIdentifier($constraintName);
if (! $this->hasForeignKey($constraintName)) {
throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
$name = $this->normalizeIdentifier($name);
if (! $this->hasForeignKey($name)) {
throw SchemaException::foreignKeyDoesNotExist($name, $this->_name);
}
return $this->_fkConstraints[$constraintName];
return $this->_fkConstraints[$name];
}
/**
* Removes the foreign key constraint with the given name.
*
* @param string $constraintName The constraint name.
* @param string $name The constraint name.
*
* @return void
*
* @throws SchemaException
*/
public function removeForeignKey($constraintName)
public function removeForeignKey($name)
{
$constraintName = $this->normalizeIdentifier($constraintName);
if (! $this->hasForeignKey($constraintName)) {
throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
$name = $this->normalizeIdentifier($name);
if (! $this->hasForeignKey($name)) {
throw SchemaException::foreignKeyDoesNotExist($name, $this->_name);
}
unset($this->_fkConstraints[$constraintName]);
unset($this->_fkConstraints[$name]);
}
/**
@@ -598,9 +642,11 @@ class Table extends AbstractAsset
*/
public function getColumns()
{
$primaryKey = $this->getPrimaryKey();
$primaryKeyColumns = [];
if ($this->hasPrimaryKey()) {
$primaryKeyColumns = $this->filterColumns($this->getPrimaryKey()->getColumns());
if ($primaryKey !== null) {
$primaryKeyColumns = $this->filterColumns($primaryKey->getColumns());
}
return array_merge($primaryKeyColumns, $this->getForeignKeyColumns(), $this->_columns);
@@ -617,6 +663,7 @@ class Table extends AbstractAsset
foreach ($this->getForeignKeys() as $foreignKey) {
$foreignKeyColumns = array_merge($foreignKeyColumns, $foreignKey->getColumns());
}
return $this->filterColumns($foreignKeyColumns);
}
@@ -629,7 +676,7 @@ class Table extends AbstractAsset
*/
private function filterColumns(array $columnNames)
{
return array_filter($this->_columns, static function ($columnName) use ($columnNames) {
return array_filter($this->_columns, static function (string $columnName) use ($columnNames) {
return in_array($columnName, $columnNames, true);
}, ARRAY_FILTER_USE_KEY);
}
@@ -637,34 +684,34 @@ class Table extends AbstractAsset
/**
* Returns whether this table has a Column with the given name.
*
* @param string $columnName The column name.
* @param string $name The column name.
*
* @return bool
*/
public function hasColumn($columnName)
public function hasColumn($name)
{
$columnName = $this->normalizeIdentifier($columnName);
$name = $this->normalizeIdentifier($name);
return isset($this->_columns[$columnName]);
return isset($this->_columns[$name]);
}
/**
* Returns the Column with the given name.
*
* @param string $columnName The column name.
* @param string $name The column name.
*
* @return Column
*
* @throws SchemaException If the column does not exist.
*/
public function getColumn($columnName)
public function getColumn($name)
{
$columnName = $this->normalizeIdentifier($columnName);
if (! $this->hasColumn($columnName)) {
throw SchemaException::columnDoesNotExist($columnName, $this->_name);
$name = $this->normalizeIdentifier($name);
if (! $this->hasColumn($name)) {
throw SchemaException::columnDoesNotExist($name, $this->_name);
}
return $this->_columns[$columnName];
return $this->_columns[$name];
}
/**
@@ -674,11 +721,11 @@ class Table extends AbstractAsset
*/
public function getPrimaryKey()
{
if (! $this->hasPrimaryKey()) {
return null;
if ($this->_primaryKeyName !== false) {
return $this->getIndex($this->_primaryKeyName);
}
return $this->getIndex($this->_primaryKeyName);
return null;
}
/**
@@ -686,14 +733,17 @@ class Table extends AbstractAsset
*
* @return string[]
*
* @throws DBALException
* @throws Exception
*/
public function getPrimaryKeyColumns()
{
if (! $this->hasPrimaryKey()) {
throw new DBALException('Table ' . $this->getName() . ' has no primary key.');
$primaryKey = $this->getPrimaryKey();
if ($primaryKey === null) {
throw new Exception('Table ' . $this->getName() . ' has no primary key.');
}
return $this->getPrimaryKey()->getColumns();
return $primaryKey->getColumns();
}
/**
@@ -709,34 +759,34 @@ class Table extends AbstractAsset
/**
* Returns whether this table has an Index with the given name.
*
* @param string $indexName The index name.
* @param string $name The index name.
*
* @return bool
*/
public function hasIndex($indexName)
public function hasIndex($name)
{
$indexName = $this->normalizeIdentifier($indexName);
$name = $this->normalizeIdentifier($name);
return isset($this->_indexes[$indexName]);
return isset($this->_indexes[$name]);
}
/**
* Returns the Index with the given name.
*
* @param string $indexName The index name.
* @param string $name The index name.
*
* @return Index
*
* @throws SchemaException If the index does not exist.
*/
public function getIndex($indexName)
public function getIndex($name)
{
$indexName = $this->normalizeIdentifier($indexName);
if (! $this->hasIndex($indexName)) {
throw SchemaException::indexDoesNotExist($indexName, $this->_name);
$name = $this->normalizeIdentifier($name);
if (! $this->hasIndex($name)) {
throw SchemaException::indexDoesNotExist($name, $this->_name);
}
return $this->_indexes[$indexName];
return $this->_indexes[$name];
}
/**
@@ -815,9 +865,11 @@ class Table extends AbstractAsset
foreach ($this->_columns as $k => $column) {
$this->_columns[$k] = clone $column;
}
foreach ($this->_indexes as $k => $index) {
$this->_indexes[$k] = clone $index;
}
foreach ($this->_fkConstraints as $k => $fk) {
$this->_fkConstraints[$k] = clone $fk;
$this->_fkConstraints[$k]->setLocalTable($this);
@@ -829,12 +881,29 @@ class Table extends AbstractAsset
*
* Trims quotes and lowercases the given identifier.
*
* @param string $identifier The identifier to normalize.
* @param string|null $identifier The identifier to normalize.
*
* @return string The normalized identifier.
*/
private function normalizeIdentifier($identifier)
{
if ($identifier === null) {
return '';
}
return $this->trimQuotes(strtolower($identifier));
}
public function setComment(?string $comment): self
{
// For keeping backward compatibility with MySQL in previous releases, table comments are stored as options.
$this->addOption('comment', $comment);
return $this;
}
public function getComment(): ?string
{
return $this->_options['comment'] ?? null;
}
}

View File

@@ -10,27 +10,27 @@ use Doctrine\DBAL\Platforms\AbstractPlatform;
class TableDiff
{
/** @var string */
public $name = null;
public $name;
/** @var string|bool */
/** @var string|false */
public $newName = false;
/**
* All added fields.
* All added columns
*
* @var Column[]
*/
public $addedColumns;
/**
* All changed fields.
* All changed columns
*
* @var ColumnDiff[]
*/
public $changedColumns = [];
/**
* All removed fields.
* All removed columns
*
* @var Column[]
*/
@@ -92,7 +92,7 @@ class TableDiff
*/
public $removedForeignKeys = [];
/** @var Table */
/** @var Table|null */
public $fromTable;
/**
@@ -139,10 +139,14 @@ class TableDiff
}
/**
* @return Identifier|string|bool
* @return Identifier|false
*/
public function getNewName()
{
return $this->newName ? new Identifier($this->newName) : $this->newName;
if ($this->newName === false) {
return false;
}
return new Identifier($this->newName);
}
}

View File

@@ -6,6 +6,7 @@ use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Sequence;
use Doctrine\DBAL\Schema\Table;
use function array_merge;
class CreateSchemaSqlCollector extends AbstractVisitor
@@ -23,7 +24,7 @@ class CreateSchemaSqlCollector extends AbstractVisitor
private $createFkConstraintQueries = [];
/** @var AbstractPlatform */
private $platform = null;
private $platform;
public function __construct(AbstractPlatform $platform)
{
@@ -47,7 +48,7 @@ class CreateSchemaSqlCollector extends AbstractVisitor
*/
public function acceptTable(Table $table)
{
$this->createTableQueries = array_merge($this->createTableQueries, (array) $this->platform->getCreateTableSQL($table));
$this->createTableQueries = array_merge($this->createTableQueries, $this->platform->getCreateTableSQL($table));
}
/**

View File

@@ -8,6 +8,8 @@ use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\DBAL\Schema\Sequence;
use Doctrine\DBAL\Schema\Table;
use SplObjectStorage;
use function assert;
use function strlen;
/**
@@ -30,7 +32,7 @@ class DropSchemaSqlCollector extends AbstractVisitor
public function __construct(AbstractPlatform $platform)
{
$this->platform = $platform;
$this->clearQueries();
$this->initializeQueries();
}
/**
@@ -66,9 +68,7 @@ class DropSchemaSqlCollector extends AbstractVisitor
*/
public function clearQueries()
{
$this->constraints = new SplObjectStorage();
$this->sequences = new SplObjectStorage();
$this->tables = new SplObjectStorage();
$this->initializeQueries();
}
/**
@@ -79,18 +79,28 @@ class DropSchemaSqlCollector extends AbstractVisitor
$sql = [];
foreach ($this->constraints as $fkConstraint) {
assert($fkConstraint instanceof ForeignKeyConstraint);
$localTable = $this->constraints[$fkConstraint];
$sql[] = $this->platform->getDropForeignKeySQL($fkConstraint, $localTable);
}
foreach ($this->sequences as $sequence) {
assert($sequence instanceof Sequence);
$sql[] = $this->platform->getDropSequenceSQL($sequence);
}
foreach ($this->tables as $table) {
assert($table instanceof Table);
$sql[] = $this->platform->getDropTableSQL($table);
}
return $sql;
}
private function initializeQueries(): void
{
$this->constraints = new SplObjectStorage();
$this->sequences = new SplObjectStorage();
$this->tables = new SplObjectStorage();
}
}

View File

@@ -5,11 +5,10 @@ namespace Doctrine\DBAL\Schema\Visitor;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\Table;
use function current;
use function file_put_contents;
use function in_array;
use function mt_rand;
use function sha1;
use function strtolower;
/**
@@ -41,7 +40,7 @@ class Graphviz extends AbstractVisitor
*/
public function acceptSchema(Schema $schema)
{
$this->output = 'digraph "' . sha1(mt_rand()) . '" {' . "\n";
$this->output = 'digraph "' . $schema->getName() . '" {' . "\n";
$this->output .= 'splines = true;' . "\n";
$this->output .= 'overlap = false;' . "\n";
$this->output .= 'outputorder=edgesfirst;' . "\n";
@@ -72,20 +71,28 @@ class Graphviz extends AbstractVisitor
$label = '<<TABLE CELLSPACING="0" BORDER="1" ALIGN="LEFT">';
// The title
$label .= '<TR><TD BORDER="1" COLSPAN="3" ALIGN="CENTER" BGCOLOR="#fcaf3e"><FONT COLOR="#2e3436" FACE="Helvetica" POINT-SIZE="12">' . $table->getName() . '</FONT></TD></TR>';
$label .= '<TR><TD BORDER="1" COLSPAN="3" ALIGN="CENTER" BGCOLOR="#fcaf3e">'
. '<FONT COLOR="#2e3436" FACE="Helvetica" POINT-SIZE="12">' . $table->getName() . '</FONT></TD></TR>';
// The attributes block
foreach ($table->getColumns() as $column) {
$columnLabel = $column->getName();
$label .= '<TR>';
$label .= '<TD BORDER="0" ALIGN="LEFT" BGCOLOR="#eeeeec">';
$label .= '<FONT COLOR="#2e3436" FACE="Helvetica" POINT-SIZE="12">' . $columnLabel . '</FONT>';
$label .= '</TD><TD BORDER="0" ALIGN="LEFT" BGCOLOR="#eeeeec"><FONT COLOR="#2e3436" FACE="Helvetica" POINT-SIZE="10">' . strtolower($column->getType()) . '</FONT></TD>';
$label .= '<TD BORDER="0" ALIGN="RIGHT" BGCOLOR="#eeeeec" PORT="col' . $column->getName() . '">';
if ($table->hasPrimaryKey() && in_array($column->getName(), $table->getPrimaryKey()->getColumns())) {
$label .= '<TR>'
. '<TD BORDER="0" ALIGN="LEFT" BGCOLOR="#eeeeec">'
. '<FONT COLOR="#2e3436" FACE="Helvetica" POINT-SIZE="12">' . $columnLabel . '</FONT>'
. '</TD>'
. '<TD BORDER="0" ALIGN="LEFT" BGCOLOR="#eeeeec">'
. '<FONT COLOR="#2e3436" FACE="Helvetica" POINT-SIZE="10">' . strtolower($column->getType()) . '</FONT>'
. '</TD>'
. '<TD BORDER="0" ALIGN="RIGHT" BGCOLOR="#eeeeec" PORT="col' . $column->getName() . '">';
$primaryKey = $table->getPrimaryKey();
if ($primaryKey !== null && in_array($column->getName(), $primaryKey->getColumns())) {
$label .= "\xe2\x9c\xb7";
}
$label .= '</TD></TR>';
}
@@ -107,6 +114,7 @@ class Graphviz extends AbstractVisitor
foreach ($options as $key => $value) {
$node .= $key . '=' . $value . ' ';
}
$node .= "]\n";
return $node;
@@ -125,6 +133,7 @@ class Graphviz extends AbstractVisitor
foreach ($options as $key => $value) {
$relation .= $key . '=' . $value . ' ';
}
$relation .= "]\n";
return $relation;

View File

@@ -11,6 +11,8 @@ interface NamespaceVisitor
* Accepts a schema namespace name.
*
* @param string $namespaceName The schema namespace name to accept.
*
* @return void
*/
public function acceptNamespace($namespaceName);
}

View File

@@ -20,7 +20,7 @@ use Doctrine\DBAL\Schema\Table;
*/
class RemoveNamespacedAssets extends AbstractVisitor
{
/** @var Schema */
/** @var Schema|null */
private $schema;
/**
@@ -36,6 +36,10 @@ class RemoveNamespacedAssets extends AbstractVisitor
*/
public function acceptTable(Table $table)
{
if ($this->schema === null) {
return;
}
if ($table->isInDefaultNamespace($this->schema->getName())) {
return;
}
@@ -48,6 +52,10 @@ class RemoveNamespacedAssets extends AbstractVisitor
*/
public function acceptSequence(Sequence $sequence)
{
if ($this->schema === null) {
return;
}
if ($sequence->isInDefaultNamespace($this->schema->getName())) {
return;
}
@@ -60,11 +68,16 @@ class RemoveNamespacedAssets extends AbstractVisitor
*/
public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint)
{
if ($this->schema === null) {
return;
}
// The table may already be deleted in a previous
// RemoveNamespacedAssets#acceptTable call. Removing Foreign keys that
// point to nowhere.
if (! $this->schema->hasTable($fkConstraint->getForeignTableName())) {
$localTable->removeForeignKey($fkConstraint->getName());
return;
}

View File

@@ -14,26 +14,37 @@ interface SchemaDiffVisitor
{
/**
* Visit an orphaned foreign key whose table was deleted.
*
* @return void
*/
public function visitOrphanedForeignKey(ForeignKeyConstraint $foreignKey);
/**
* Visit a sequence that has changed.
*
* @return void
*/
public function visitChangedSequence(Sequence $sequence);
/**
* Visit a sequence that has been removed.
*
* @return void
*/
public function visitRemovedSequence(Sequence $sequence);
/** @return void */
public function visitNewSequence(Sequence $sequence);
/** @return void */
public function visitNewTable(Table $table);
/** @return void */
public function visitNewTableForeignKey(Table $table, ForeignKeyConstraint $foreignKey);
/** @return void */
public function visitRemovedTable(Table $table);
/** @return void */
public function visitChangedTable(TableDiff $tableDiff);
}