updated-packages
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Doctrine\DBAL\Schema;
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
|
||||
use function explode;
|
||||
use function strtolower;
|
||||
use function trim;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,8 @@ use Doctrine\DBAL\Schema\Schema;
|
||||
/**
|
||||
* The synchronizer knows how to synchronize a schema with the configured
|
||||
* database.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
interface SchemaSynchronizer
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user