Skip to content

Commit

Permalink
[10.x] Add support for native column modifying (#45487)
Browse files Browse the repository at this point in the history
* compile auto increment starting value as fluent command

* separate types and modifiers

* support native column modifying on MySQL, PostgreSQL and SQL Server

* fix tests

* Update Connection.php

* formatting

Co-authored-by: Taylor Otwell <taylor@laravel.com>
  • Loading branch information
hafezdivandari and taylorotwell committed Jan 4, 2023
1 parent eb04fb4 commit e901b02
Show file tree
Hide file tree
Showing 15 changed files with 573 additions and 219 deletions.
2 changes: 1 addition & 1 deletion src/Illuminate/Database/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -1077,7 +1077,7 @@ public function isDoctrineAvailable()
}

/**
* Indicates whether native alter operations will be used when dropping or renaming columns, even if Doctrine DBAL is installed.
* Indicates whether native alter operations will be used when dropping, renaming, or modifying columns, even if Doctrine DBAL is installed.
*
* @return bool
*/
Expand Down
56 changes: 11 additions & 45 deletions src/Illuminate/Database/Schema/Blueprint.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public function build(Connection $connection, Grammar $grammar)
*/
public function toSql(Connection $connection, Grammar $grammar)
{
$this->addImpliedCommands($grammar);
$this->addImpliedCommands($connection, $grammar);

$statements = [];

Expand Down Expand Up @@ -183,10 +183,11 @@ protected function commandsNamed(array $names)
/**
* Add the commands that are implied by the blueprint's state.
*
* @param \Illuminate\Database\Connection $connection
* @param \Illuminate\Database\Schema\Grammars\Grammar $grammar
* @return void
*/
protected function addImpliedCommands(Grammar $grammar)
protected function addImpliedCommands(Connection $connection, Grammar $grammar)
{
if (count($this->getAddedColumns()) > 0 && ! $this->creating()) {
array_unshift($this->commands, $this->createCommand('add'));
Expand All @@ -198,7 +199,7 @@ protected function addImpliedCommands(Grammar $grammar)

$this->addFluentIndexes();

$this->addFluentCommands($grammar);
$this->addFluentCommands($connection, $grammar);
}

/**
Expand Down Expand Up @@ -236,24 +237,19 @@ protected function addFluentIndexes()
/**
* Add the fluent commands specified on any columns.
*
* @param \Illuminate\Database\Connection $connection
* @param \Illuminate\Database\Schema\Grammars\Grammar $grammar
* @return void
*/
public function addFluentCommands(Grammar $grammar)
public function addFluentCommands(Connection $connection, Grammar $grammar)
{
foreach ($this->columns as $column) {
foreach ($grammar->getFluentCommands() as $commandName) {
$attributeName = lcfirst($commandName);

if (! isset($column->{$attributeName})) {
continue;
}

$value = $column->{$attributeName};
if ($column->change && ! $connection->usingNativeSchemaOperations()) {
continue;
}

$this->addCommand(
$commandName, compact('value', 'column')
);
foreach ($grammar->getFluentCommands() as $commandName) {
$this->addCommand($commandName, compact('column'));
}
}
}
Expand Down Expand Up @@ -1786,34 +1782,4 @@ public function getChangedColumns()
return (bool) $column->change;
});
}

/**
* Determine if the blueprint has auto-increment columns.
*
* @return bool
*/
public function hasAutoIncrementColumn()
{
return ! is_null(collect($this->getAddedColumns())->first(function ($column) {
return $column->autoIncrement === true;
}));
}

/**
* Get the auto-increment column starting values.
*
* @return array
*/
public function autoIncrementingStartingValues()
{
if (! $this->hasAutoIncrementColumn()) {
return [];
}

return collect($this->getAddedColumns())->mapWithKeys(function ($column) {
return $column->autoIncrement === true
? [$column->name => $column->get('startingValue', $column->get('from'))]
: [$column->name => null];
})->filter()->all();
}
}
4 changes: 2 additions & 2 deletions src/Illuminate/Database/Schema/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class Builder
public static $defaultMorphKeyType = 'int';

/**
* Indicates whether Doctrine DBAL usage will be prevented if possible when dropping and renaming columns.
* Indicates whether Doctrine DBAL usage will be prevented if possible when dropping, renaming, and modifying columns.
*
* @var bool
*/
Expand Down Expand Up @@ -113,7 +113,7 @@ public static function morphUsingUlids()
}

/**
* Attempt to use native schema operations for dropping and renaming columns, even if Doctrine DBAL is installed.
* Attempt to use native schema operations for dropping, renaming, and modifying columns, even if Doctrine DBAL is installed.
*
* @param bool $value
* @return void
Expand Down
6 changes: 3 additions & 3 deletions src/Illuminate/Database/Schema/Grammars/Grammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Conne
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $command
* @param \Illuminate\Database\Connection $connection
* @return array
* @return array|string
*
* @throws \RuntimeException
*/
Expand Down Expand Up @@ -162,7 +162,7 @@ public function compileForeign(Blueprint $blueprint, Fluent $command)
}

/**
* Compile the blueprint's column definitions.
* Compile the blueprint's added column definitions.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @return array
Expand Down Expand Up @@ -286,7 +286,7 @@ public function wrapTable($table)
/**
* Wrap a value in keyword identifiers.
*
* @param \Illuminate\Database\Query\Expression|string $value
* @param \Illuminate\Support\Fluent|\Illuminate\Database\Query\Expression|string $value
* @param bool $prefixAlias
* @return string
*/
Expand Down
113 changes: 86 additions & 27 deletions src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Illuminate\Database\Schema\Grammars;

use Illuminate\Database\Connection;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Fluent;
use RuntimeException;
Expand All @@ -15,8 +16,8 @@ class MySqlGrammar extends Grammar
* @var string[]
*/
protected $modifiers = [
'Unsigned', 'Charset', 'Collate', 'VirtualAs', 'StoredAs', 'Nullable', 'Invisible',
'Srid', 'Default', 'Increment', 'Comment', 'After', 'First',
'Unsigned', 'Charset', 'Collate', 'VirtualAs', 'StoredAs', 'Nullable',
'Srid', 'Default', 'OnUpdate', 'Invisible', 'Increment', 'Comment', 'After', 'First',
];

/**
Expand All @@ -26,6 +27,13 @@ class MySqlGrammar extends Grammar
*/
protected $serials = ['bigInteger', 'integer', 'mediumInteger', 'smallInteger', 'tinyInteger'];

/**
* The commands to be executed outside of create or alter command.
*
* @var string[]
*/
protected $fluentCommands = ['AutoIncrementStartingValues'];

/**
* Compile a create database command.
*
Expand Down Expand Up @@ -83,7 +91,7 @@ public function compileColumnListing()
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $command
* @param \Illuminate\Database\Connection $connection
* @return array
* @return string
*/
public function compileCreate(Blueprint $blueprint, Fluent $command, Connection $connection)
{
Expand All @@ -101,9 +109,7 @@ public function compileCreate(Blueprint $blueprint, Fluent $command, Connection
// Finally, we will append the engine configuration onto this SQL statement as
// the final thing we do before returning this finished SQL. Once this gets
// added the query will be ready to execute against the real connections.
return array_values(array_filter(array_merge([$this->compileCreateEngine(
$sql, $connection, $blueprint
)], $this->compileAutoIncrementStartingValues($blueprint))));
return $this->compileCreateEngine($sql, $connection, $blueprint);
}

/**
Expand All @@ -112,15 +118,15 @@ public function compileCreate(Blueprint $blueprint, Fluent $command, Connection
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $command
* @param \Illuminate\Database\Connection $connection
* @return array
* @return string
*/
protected function compileCreateTable($blueprint, $command, $connection)
{
return trim(sprintf('%s table %s (%s)',
return sprintf('%s table %s (%s)',
$blueprint->temporary ? 'create temporary' : 'create',
$this->wrapTable($blueprint),
implode(', ', $this->getColumns($blueprint))
));
);
}

/**
Expand Down Expand Up @@ -178,29 +184,28 @@ protected function compileCreateEngine($sql, Connection $connection, Blueprint $
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $command
* @return array
* @return string
*/
public function compileAdd(Blueprint $blueprint, Fluent $command)
{
$columns = $this->prefixArray('add', $this->getColumns($blueprint));

return array_values(array_merge(
['alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns)],
$this->compileAutoIncrementStartingValues($blueprint)
));
return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns);
}

/**
* Compile the auto-incrementing column starting values.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @return array
* @param \Illuminate\Support\Fluent $command
* @return string
*/
public function compileAutoIncrementStartingValues(Blueprint $blueprint)
public function compileAutoIncrementStartingValues(Blueprint $blueprint, Fluent $command)
{
return collect($blueprint->autoIncrementingStartingValues())->map(function ($value, $column) use ($blueprint) {
return 'alter table '.$this->wrapTable($blueprint->getTable()).' auto_increment = '.$value;
})->all();
if ($command->column->autoIncrement
&& $value = $command->column->get('startingValue', $command->column->get('from'))) {
return 'alter table '.$this->wrapTable($blueprint).' auto_increment = '.$value;
}
}

/**
Expand All @@ -222,6 +227,38 @@ public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Conne
: parent::compileRenameColumn($blueprint, $command, $connection);
}

/**
* Compile a change column command into a series of SQL statements.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $command
* @param \Illuminate\Database\Connection $connection
* @return array|string
*
* @throws \RuntimeException
*/
public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection)
{
if (! $connection->usingNativeSchemaOperations()) {
return parent::compileChange($blueprint, $command, $connection);
}

$columns = [];

foreach ($blueprint->getChangedColumns() as $column) {
$sql = sprintf('%s %s%s %s',
is_null($column->renameTo) ? 'modify' : 'change',
$this->wrap($column),
is_null($column->renameTo) ? '' : ' '.$this->wrap($column->renameTo),
$this->getType($column)
);

$columns[] = $this->addModifiers($sql, $blueprint, $column);
}

return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns);
}

/**
* Compile a primary key command.
*
Expand Down Expand Up @@ -758,13 +795,17 @@ protected function typeDate(Fluent $column)
*/
protected function typeDateTime(Fluent $column)
{
$columnType = $column->precision ? "datetime($column->precision)" : 'datetime';

$current = $column->precision ? "CURRENT_TIMESTAMP($column->precision)" : 'CURRENT_TIMESTAMP';

$columnType = $column->useCurrent ? "$columnType default $current" : $columnType;
if ($column->useCurrent) {
$column->default(new Expression($current));
}

if ($column->useCurrentOnUpdate) {
$column->onUpdate(new Expression($current));
}

return $column->useCurrentOnUpdate ? "$columnType on update $current" : $columnType;
return $column->precision ? "datetime($column->precision)" : 'datetime';
}

/**
Expand Down Expand Up @@ -808,13 +849,17 @@ protected function typeTimeTz(Fluent $column)
*/
protected function typeTimestamp(Fluent $column)
{
$columnType = $column->precision ? "timestamp($column->precision)" : 'timestamp';

$current = $column->precision ? "CURRENT_TIMESTAMP($column->precision)" : 'CURRENT_TIMESTAMP';

$columnType = $column->useCurrent ? "$columnType default $current" : $columnType;
if ($column->useCurrent) {
$column->default(new Expression($current));
}

return $column->useCurrentOnUpdate ? "$columnType on update $current" : $columnType;
if ($column->useCurrentOnUpdate) {
$column->onUpdate(new Expression($current));
}

return $column->precision ? "timestamp($column->precision)" : 'timestamp';
}

/**
Expand Down Expand Up @@ -1119,6 +1164,20 @@ protected function modifyDefault(Blueprint $blueprint, Fluent $column)
}
}

/**
* Get the SQL for an "on update" column modifier.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $column
* @return string|null
*/
protected function modifyOnUpdate(Blueprint $blueprint, Fluent $column)
{
if (! is_null($column->onUpdate)) {
return ' on update '.$column->onUpdate;
}
}

/**
* Get the SQL for an auto-increment column modifier.
*
Expand Down

0 comments on commit e901b02

Please sign in to comment.