diff --git a/src/Illuminate/Database/Connection.php b/src/Illuminate/Database/Connection.php index 5ab8305a98ea..e1fd9ceb51e1 100755 --- a/src/Illuminate/Database/Connection.php +++ b/src/Illuminate/Database/Connection.php @@ -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 */ diff --git a/src/Illuminate/Database/Schema/Blueprint.php b/src/Illuminate/Database/Schema/Blueprint.php index ee0988d5273a..be6ba252a32a 100755 --- a/src/Illuminate/Database/Schema/Blueprint.php +++ b/src/Illuminate/Database/Schema/Blueprint.php @@ -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 = []; @@ -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')); @@ -198,7 +199,7 @@ protected function addImpliedCommands(Grammar $grammar) $this->addFluentIndexes(); - $this->addFluentCommands($grammar); + $this->addFluentCommands($connection, $grammar); } /** @@ -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')); } } } @@ -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(); - } } diff --git a/src/Illuminate/Database/Schema/Builder.php b/src/Illuminate/Database/Schema/Builder.php index 2788a124fdb5..6bc36df1ec4a 100755 --- a/src/Illuminate/Database/Schema/Builder.php +++ b/src/Illuminate/Database/Schema/Builder.php @@ -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 */ @@ -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 diff --git a/src/Illuminate/Database/Schema/Grammars/Grammar.php b/src/Illuminate/Database/Schema/Grammars/Grammar.php index 0e067c56a1cd..2a5ebe2665f5 100755 --- a/src/Illuminate/Database/Schema/Grammars/Grammar.php +++ b/src/Illuminate/Database/Schema/Grammars/Grammar.php @@ -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 */ @@ -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 @@ -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 */ diff --git a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php index 40ee3c8b873d..c0bed9aeb287 100755 --- a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php @@ -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; @@ -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', ]; /** @@ -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. * @@ -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) { @@ -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); } /** @@ -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)) - )); + ); } /** @@ -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; + } } /** @@ -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. * @@ -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'; } /** @@ -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'; } /** @@ -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. * diff --git a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php index ef60d0ff820f..e01aa947fe35 100755 --- a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php @@ -3,8 +3,10 @@ namespace Illuminate\Database\Schema\Grammars; use Illuminate\Database\Connection; +use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Fluent; +use LogicException; class PostgresGrammar extends Grammar { @@ -20,7 +22,7 @@ class PostgresGrammar extends Grammar * * @var string[] */ - protected $modifiers = ['Collate', 'Increment', 'Nullable', 'Default', 'VirtualAs', 'StoredAs']; + protected $modifiers = ['Collate', 'Nullable', 'Default', 'VirtualAs', 'StoredAs', 'GeneratedAs', 'Increment']; /** * The columns available as serials. @@ -34,7 +36,7 @@ class PostgresGrammar extends Grammar * * @var string[] */ - protected $fluentCommands = ['Comment']; + protected $fluentCommands = ['AutoIncrementStartingValues', 'Comment']; /** * Compile a create database command. @@ -91,15 +93,15 @@ public function compileColumnListing() * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Illuminate\Support\Fluent $command - * @return array + * @return string */ public function compileCreate(Blueprint $blueprint, Fluent $command) { - return array_values(array_filter(array_merge([sprintf('%s table %s (%s)', + return sprintf('%s table %s (%s)', $blueprint->temporary ? 'create temporary' : 'create', $this->wrapTable($blueprint), implode(', ', $this->getColumns($blueprint)) - )], $this->compileAutoIncrementStartingValues($blueprint)))); + ); } /** @@ -111,23 +113,25 @@ public function compileCreate(Blueprint $blueprint, Fluent $command) */ public function compileAdd(Blueprint $blueprint, Fluent $command) { - return array_values(array_filter(array_merge([sprintf('alter table %s %s', + return sprintf('alter table %s %s', $this->wrapTable($blueprint), implode(', ', $this->prefixArray('add column', $this->getColumns($blueprint))) - )], $this->compileAutoIncrementStartingValues($blueprint)))); + ); } /** * 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 sequence '.$blueprint->getTable().'_'.$column.'_seq restart with '.$value; - })->all(); + if ($command->column->autoIncrement + && $value = $command->column->get('startingValue', $command->column->get('from'))) { + return 'alter sequence '.$blueprint->getTable().'_'.$command->column->name.'_seq restart with '.$value; + } } /** @@ -149,6 +153,47 @@ 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) { + $changes = ['type '.$this->getType($column).$this->modifyCollate($blueprint, $column)]; + + foreach ($this->modifiers as $modifier) { + if ($modifier === 'Collate') { + continue; + } + + if (method_exists($this, $method = "modify{$modifier}")) { + $constraints = (array) $this->{$method}($blueprint, $column); + + foreach ($constraints as $constraint) { + $changes[] = $constraint; + } + } + } + + $columns[] = implode(', ', $this->prefixArray('alter column '.$this->wrap($column), $changes)); + } + + return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns); + } + /** * Compile a primary key command. * @@ -509,11 +554,13 @@ public function compileDisableForeignKeyConstraints() */ public function compileComment(Blueprint $blueprint, Fluent $command) { - return sprintf('comment on column %s.%s is %s', - $this->wrapTable($blueprint), - $this->wrap($command->column->name), - "'".str_replace("'", "''", $command->value)."'" - ); + if (! is_null($comment = $command->column->comment) || $command->column->change) { + return sprintf('comment on column %s.%s is %s', + $this->wrapTable($blueprint), + $this->wrap($command->column->name), + is_null($comment) ? 'NULL' : "'".str_replace("'", "''", $comment)."'" + ); + } } /** @@ -628,7 +675,7 @@ protected function typeLongText(Fluent $column) */ protected function typeInteger(Fluent $column) { - return $this->generatableColumn('integer', $column); + return $column->autoIncrement && is_null($column->generatedAs) ? 'serial' : 'integer'; } /** @@ -639,7 +686,7 @@ protected function typeInteger(Fluent $column) */ protected function typeBigInteger(Fluent $column) { - return $this->generatableColumn('bigint', $column); + return $column->autoIncrement && is_null($column->generatedAs) ? 'bigserial' : 'bigint'; } /** @@ -650,7 +697,7 @@ protected function typeBigInteger(Fluent $column) */ protected function typeMediumInteger(Fluent $column) { - return $this->generatableColumn('integer', $column); + return $this->typeInteger($column); } /** @@ -661,7 +708,7 @@ protected function typeMediumInteger(Fluent $column) */ protected function typeTinyInteger(Fluent $column) { - return $this->generatableColumn('smallint', $column); + return $this->typeSmallInteger($column); } /** @@ -672,42 +719,7 @@ protected function typeTinyInteger(Fluent $column) */ protected function typeSmallInteger(Fluent $column) { - return $this->generatableColumn('smallint', $column); - } - - /** - * Create the column definition for a generatable column. - * - * @param string $type - * @param \Illuminate\Support\Fluent $column - * @return string - */ - protected function generatableColumn($type, Fluent $column) - { - if (! $column->autoIncrement && is_null($column->generatedAs)) { - return $type; - } - - if ($column->autoIncrement && is_null($column->generatedAs)) { - return with([ - 'integer' => 'serial', - 'bigint' => 'bigserial', - 'smallint' => 'smallserial', - ])[$type]; - } - - $options = ''; - - if (! is_bool($column->generatedAs) && ! empty($column->generatedAs)) { - $options = sprintf(' (%s)', $column->generatedAs); - } - - return sprintf( - '%s generated %s as identity%s', - $type, - $column->always ? 'always' : 'by default', - $options - ); + return $column->autoIncrement && is_null($column->generatedAs) ? 'smallserial' : 'smallint'; } /** @@ -865,9 +877,11 @@ protected function typeTimeTz(Fluent $column) */ protected function typeTimestamp(Fluent $column) { - $columnType = 'timestamp'.(is_null($column->precision) ? '' : "($column->precision)").' without time zone'; + if ($column->useCurrent) { + $column->default(new Expression('CURRENT_TIMESTAMP')); + } - return $column->useCurrent ? "$columnType default CURRENT_TIMESTAMP" : $columnType; + return 'timestamp'.(is_null($column->precision) ? '' : "($column->precision)").' without time zone'; } /** @@ -878,9 +892,11 @@ protected function typeTimestamp(Fluent $column) */ protected function typeTimestampTz(Fluent $column) { - $columnType = 'timestamp'.(is_null($column->precision) ? '' : "($column->precision)").' with time zone'; + if ($column->useCurrent) { + $column->default(new Expression('CURRENT_TIMESTAMP')); + } - return $column->useCurrent ? "$columnType default CURRENT_TIMESTAMP" : $columnType; + return 'timestamp'.(is_null($column->precision) ? '' : "($column->precision)").' with time zone'; } /** @@ -1080,6 +1096,10 @@ protected function modifyCollate(Blueprint $blueprint, Fluent $column) */ protected function modifyNullable(Blueprint $blueprint, Fluent $column) { + if ($column->change) { + return $column->nullable ? 'drop not null' : 'set not null'; + } + return $column->nullable ? ' null' : ' not null'; } @@ -1092,6 +1112,10 @@ protected function modifyNullable(Blueprint $blueprint, Fluent $column) */ protected function modifyDefault(Blueprint $blueprint, Fluent $column) { + if ($column->change) { + return is_null($column->default) ? 'drop default' : 'set default '.$this->getDefaultValue($column->default); + } + if (! is_null($column->default)) { return ' default '.$this->getDefaultValue($column->default); } @@ -1106,7 +1130,9 @@ protected function modifyDefault(Blueprint $blueprint, Fluent $column) */ protected function modifyIncrement(Blueprint $blueprint, Fluent $column) { - if ((in_array($column->type, $this->serials) || ($column->generatedAs !== null)) && $column->autoIncrement) { + if (! $column->change + && (in_array($column->type, $this->serials) || ($column->generatedAs !== null)) + && $column->autoIncrement) { return ' primary key'; } } @@ -1120,7 +1146,17 @@ protected function modifyIncrement(Blueprint $blueprint, Fluent $column) */ protected function modifyVirtualAs(Blueprint $blueprint, Fluent $column) { - if ($column->virtualAs !== null) { + if ($column->change) { + if (array_key_exists('virtualAs', $column->getAttributes())) { + return is_null($column->virtualAs) + ? 'drop expression if exists' + : throw new LogicException('This database driver does not support modifying generated columns.'); + } + + return null; + } + + if (! is_null($column->virtualAs)) { return " generated always as ({$column->virtualAs})"; } } @@ -1134,8 +1170,50 @@ protected function modifyVirtualAs(Blueprint $blueprint, Fluent $column) */ protected function modifyStoredAs(Blueprint $blueprint, Fluent $column) { - if ($column->storedAs !== null) { + if ($column->change) { + if (array_key_exists('storedAs', $column->getAttributes())) { + return is_null($column->storedAs) + ? 'drop expression if exists' + : throw new LogicException('This database driver does not support modifying generated columns.'); + } + + return null; + } + + if (! is_null($column->storedAs)) { return " generated always as ({$column->storedAs}) stored"; } } + + /** + * Get the SQL for an identity column modifier. + * + * @param \Illuminate\Database\Schema\Blueprint $blueprint + * @param \Illuminate\Support\Fluent $column + * @return string|array|null + */ + protected function modifyGeneratedAs(Blueprint $blueprint, Fluent $column) + { + $sql = null; + + if (! is_null($column->generatedAs)) { + $sql = sprintf( + ' generated %s as identity%s', + $column->always ? 'always' : 'by default', + ! is_bool($column->generatedAs) && ! empty($column->generatedAs) ? " ({$column->generatedAs})" : '' + ); + } + + if ($column->change) { + $changes = ['drop identity if exists']; + + if (! is_null($sql)) { + $changes[] = 'add '.$sql; + } + + return $changes; + } + + return $sql; + } } diff --git a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php index 552babad805a..9c3b1c9fb61e 100755 --- a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php @@ -4,6 +4,7 @@ use Doctrine\DBAL\Schema\Index; use Illuminate\Database\Connection; +use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Arr; use Illuminate\Support\Fluent; @@ -16,7 +17,7 @@ class SQLiteGrammar extends Grammar * * @var string[] */ - protected $modifiers = ['VirtualAs', 'StoredAs', 'Nullable', 'Default', 'Increment']; + protected $modifiers = ['Increment', 'Nullable', 'Default', 'VirtualAs', 'StoredAs']; /** * The columns available as serials. @@ -727,7 +728,11 @@ protected function typeTimeTz(Fluent $column) */ protected function typeTimestamp(Fluent $column) { - return $column->useCurrent ? 'datetime default CURRENT_TIMESTAMP' : 'datetime'; + if ($column->useCurrent) { + $column->default(new Expression('CURRENT_TIMESTAMP')); + } + + return 'datetime'; } /** diff --git a/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php b/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php index 4d7271ca3308..2bf048832830 100755 --- a/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php @@ -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; @@ -20,7 +21,7 @@ class SqlServerGrammar extends Grammar * * @var string[] */ - protected $modifiers = ['Increment', 'Collate', 'Nullable', 'Default', 'Persisted']; + protected $modifiers = ['Collate', 'Nullable', 'Default', 'Persisted', 'Increment']; /** * The columns available as serials. @@ -29,6 +30,13 @@ class SqlServerGrammar extends Grammar */ protected $serials = ['tinyInteger', 'smallInteger', 'mediumInteger', 'integer', 'bigInteger']; + /** + * The commands to be executed outside of create or alter command. + * + * @var string[] + */ + protected $fluentCommands = ['Default']; + /** * Compile a create database command. * @@ -126,6 +134,43 @@ 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); + } + + $changes = [$this->compileDropDefaultConstraint($blueprint, $command)]; + + foreach ($blueprint->getChangedColumns() as $column) { + $sql = sprintf('alter table %s alter column %s %s', + $this->wrapTable($blueprint), + $this->wrap($column), + $this->getType($column) + ); + + foreach ($this->modifiers as $modifier) { + if (method_exists($this, $method = "modify{$modifier}")) { + $sql .= $this->{$method}($blueprint, $column); + } + } + + $changes[] = $sql; + } + + return $changes; + } + /** * Compile a primary key command. * @@ -190,6 +235,24 @@ public function compileSpatialIndex(Blueprint $blueprint, Fluent $command) ); } + /** + * Compile a default command. + * + * @param \Illuminate\Database\Schema\Blueprint $blueprint + * @param \Illuminate\Support\Fluent $command + * @return string|null + */ + public function compileDefault(Blueprint $blueprint, Fluent $command) + { + if ($command->column->change && ! is_null($command->column->default)) { + return sprintf('alter table %s add default %s for %s', + $this->wrapTable($blueprint), + $this->getDefaultValue($command->column->default), + $this->wrap($command->column) + ); + } + } + /** * Compile a drop table command. * @@ -252,7 +315,9 @@ public function compileDropColumn(Blueprint $blueprint, Fluent $command) */ public function compileDropDefaultConstraint(Blueprint $blueprint, Fluent $command) { - $columns = "'".implode("','", $command->columns)."'"; + $columns = $command->name === 'change' + ? "'".collect($blueprint->getChangedColumns())->pluck('name')->implode("','")."'" + : "'".implode("','", $command->columns)."'"; $tableName = $this->getTablePrefix().$blueprint->getTable(); @@ -697,9 +762,11 @@ protected function typeTimeTz(Fluent $column) */ protected function typeTimestamp(Fluent $column) { - $columnType = $column->precision ? "datetime2($column->precision)" : 'datetime'; + if ($column->useCurrent) { + $column->default(new Expression('CURRENT_TIMESTAMP')); + } - return $column->useCurrent ? "$columnType default CURRENT_TIMESTAMP" : $columnType; + return $column->precision ? "datetime2($column->precision)" : 'datetime'; } /** @@ -712,9 +779,11 @@ protected function typeTimestamp(Fluent $column) */ protected function typeTimestampTz(Fluent $column) { - $columnType = $column->precision ? "datetimeoffset($column->precision)" : 'datetimeoffset'; + if ($column->useCurrent) { + $column->default(new Expression('CURRENT_TIMESTAMP')); + } - return $column->useCurrent ? "$columnType default CURRENT_TIMESTAMP" : $columnType; + return $column->precision ? "datetimeoffset($column->precision)" : 'datetimeoffset'; } /** @@ -908,7 +977,7 @@ protected function modifyNullable(Blueprint $blueprint, Fluent $column) */ protected function modifyDefault(Blueprint $blueprint, Fluent $column) { - if (! is_null($column->default)) { + if (! $column->change && ! is_null($column->default)) { return ' default '.$this->getDefaultValue($column->default); } } @@ -922,7 +991,7 @@ protected function modifyDefault(Blueprint $blueprint, Fluent $column) */ protected function modifyIncrement(Blueprint $blueprint, Fluent $column) { - if (in_array($column->type, $this->serials) && $column->autoIncrement) { + if (! $column->change && in_array($column->type, $this->serials) && $column->autoIncrement) { return ' identity primary key'; } } @@ -936,6 +1005,14 @@ protected function modifyIncrement(Blueprint $blueprint, Fluent $column) */ protected function modifyPersisted(Blueprint $blueprint, Fluent $column) { + if ($column->change) { + if ($column->type === 'computed') { + return $column->persisted ? ' add persisted' : ' drop persisted'; + } + + return null; + } + if ($column->persisted) { return ' persisted'; } @@ -944,7 +1021,7 @@ protected function modifyPersisted(Blueprint $blueprint, Fluent $column) /** * Wrap a table in keyword identifiers. * - * @param \Illuminate\Database\Query\Expression|string $table + * @param \Illuminate\Database\Schema\Blueprint|\Illuminate\Database\Query\Expression|string $table * @return string */ public function wrapTable($table) diff --git a/tests/Database/DatabaseMySqlSchemaGrammarTest.php b/tests/Database/DatabaseMySqlSchemaGrammarTest.php index 61ac2224caf8..9fa1f60b720f 100755 --- a/tests/Database/DatabaseMySqlSchemaGrammarTest.php +++ b/tests/Database/DatabaseMySqlSchemaGrammarTest.php @@ -66,6 +66,19 @@ public function testAutoIncrementStartingValue() $this->assertSame('alter table `users` auto_increment = 1000', $statements[1]); } + public function testAddColumnsWithMultipleAutoIncrementStartingValue() + { + $blueprint = new Blueprint('users'); + $blueprint->id()->from(100); + $blueprint->string('name')->from(200); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertEquals([ + 'alter table `users` add `id` bigint unsigned not null auto_increment primary key, add `name` varchar(255) not null', + 'alter table `users` auto_increment = 100', + ], $statements); + } + public function testEngineCreateTable() { $blueprint = new Blueprint('users'); @@ -837,7 +850,7 @@ public function testAddingDateTimeWithDefaultCurrent() $blueprint->dateTime('foo')->useCurrent(); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` datetime default CURRENT_TIMESTAMP not null', $statements[0]); + $this->assertSame('alter table `users` add `foo` datetime not null default CURRENT_TIMESTAMP', $statements[0]); } public function testAddingDateTimeWithOnUpdateCurrent() @@ -846,7 +859,7 @@ public function testAddingDateTimeWithOnUpdateCurrent() $blueprint->dateTime('foo')->useCurrentOnUpdate(); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` datetime on update CURRENT_TIMESTAMP not null', $statements[0]); + $this->assertSame('alter table `users` add `foo` datetime not null on update CURRENT_TIMESTAMP', $statements[0]); } public function testAddingDateTimeWithDefaultCurrentAndOnUpdateCurrent() @@ -855,7 +868,7 @@ public function testAddingDateTimeWithDefaultCurrentAndOnUpdateCurrent() $blueprint->dateTime('foo')->useCurrent()->useCurrentOnUpdate(); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` datetime default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP not null', $statements[0]); + $this->assertSame('alter table `users` add `foo` datetime not null default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP', $statements[0]); } public function testAddingDateTimeWithDefaultCurrentOnUpdateCurrentAndPrecision() @@ -864,7 +877,7 @@ public function testAddingDateTimeWithDefaultCurrentOnUpdateCurrentAndPrecision( $blueprint->dateTime('foo', 3)->useCurrent()->useCurrentOnUpdate(); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` datetime(3) default CURRENT_TIMESTAMP(3) on update CURRENT_TIMESTAMP(3) not null', $statements[0]); + $this->assertSame('alter table `users` add `foo` datetime(3) not null default CURRENT_TIMESTAMP(3) on update CURRENT_TIMESTAMP(3)', $statements[0]); } public function testAddingDateTimeTz() @@ -951,7 +964,7 @@ public function testAddingTimestampWithDefaultCurrentSpecifyingPrecision() $blueprint->timestamp('created_at', 1)->useCurrent(); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` timestamp(1) default CURRENT_TIMESTAMP(1) not null', $statements[0]); + $this->assertSame('alter table `users` add `created_at` timestamp(1) not null default CURRENT_TIMESTAMP(1)', $statements[0]); } public function testAddingTimestampWithOnUpdateCurrentSpecifyingPrecision() @@ -960,7 +973,7 @@ public function testAddingTimestampWithOnUpdateCurrentSpecifyingPrecision() $blueprint->timestamp('created_at', 1)->useCurrentOnUpdate(); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` timestamp(1) on update CURRENT_TIMESTAMP(1) not null', $statements[0]); + $this->assertSame('alter table `users` add `created_at` timestamp(1) not null on update CURRENT_TIMESTAMP(1)', $statements[0]); } public function testAddingTimestampWithDefaultCurrentAndOnUpdateCurrentSpecifyingPrecision() @@ -969,7 +982,7 @@ public function testAddingTimestampWithDefaultCurrentAndOnUpdateCurrentSpecifyin $blueprint->timestamp('created_at', 1)->useCurrent()->useCurrentOnUpdate(); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` timestamp(1) default CURRENT_TIMESTAMP(1) on update CURRENT_TIMESTAMP(1) not null', $statements[0]); + $this->assertSame('alter table `users` add `created_at` timestamp(1) not null default CURRENT_TIMESTAMP(1) on update CURRENT_TIMESTAMP(1)', $statements[0]); } public function testAddingTimestampTz() diff --git a/tests/Database/DatabasePostgresSchemaGrammarTest.php b/tests/Database/DatabasePostgresSchemaGrammarTest.php index 98066f4f90eb..8d0a942ef9eb 100755 --- a/tests/Database/DatabasePostgresSchemaGrammarTest.php +++ b/tests/Database/DatabasePostgresSchemaGrammarTest.php @@ -27,7 +27,7 @@ public function testBasicCreateTable() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('create table "users" ("id" serial primary key not null, "email" varchar(255) not null, "name" varchar(255) collate "nb_NO.utf8" not null)', $statements[0]); + $this->assertSame('create table "users" ("id" serial not null primary key, "email" varchar(255) not null, "name" varchar(255) collate "nb_NO.utf8" not null)', $statements[0]); $blueprint = new Blueprint('users'); $blueprint->increments('id'); @@ -35,7 +35,7 @@ public function testBasicCreateTable() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" serial primary key not null, add column "email" varchar(255) not null', $statements[0]); + $this->assertSame('alter table "users" add column "id" serial not null primary key, add column "email" varchar(255) not null', $statements[0]); } public function testCreateTableWithAutoIncrementStartingValue() @@ -48,10 +48,25 @@ public function testCreateTableWithAutoIncrementStartingValue() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(2, $statements); - $this->assertSame('create table "users" ("id" serial primary key not null, "email" varchar(255) not null, "name" varchar(255) collate "nb_NO.utf8" not null)', $statements[0]); + $this->assertSame('create table "users" ("id" serial not null primary key, "email" varchar(255) not null, "name" varchar(255) collate "nb_NO.utf8" not null)', $statements[0]); $this->assertSame('alter sequence users_id_seq restart with 1000', $statements[1]); } + public function testAddColumnsWithMultipleAutoIncrementStartingValue() + { + $blueprint = new Blueprint('users'); + $blueprint->id()->from(100); + $blueprint->increments('code')->from(200); + $blueprint->string('name')->from(300); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertEquals([ + 'alter table "users" add column "id" bigserial not null primary key, add column "code" serial not null primary key, add column "name" varchar(255) not null', + 'alter sequence users_id_seq restart with 100', + 'alter sequence users_code_seq restart with 200', + ], $statements); + } + public function testCreateTableAndCommentColumn() { $blueprint = new Blueprint('users'); @@ -61,7 +76,7 @@ public function testCreateTableAndCommentColumn() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(2, $statements); - $this->assertSame('create table "users" ("id" serial primary key not null, "email" varchar(255) not null)', $statements[0]); + $this->assertSame('create table "users" ("id" serial not null primary key, "email" varchar(255) not null)', $statements[0]); $this->assertSame('comment on column "users"."email" is \'my first comment\'', $statements[1]); } @@ -75,7 +90,7 @@ public function testCreateTemporaryTable() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('create temporary table "users" ("id" serial primary key not null, "email" varchar(255) not null)', $statements[0]); + $this->assertSame('create temporary table "users" ("id" serial not null primary key, "email" varchar(255) not null)', $statements[0]); } public function testDropTable() @@ -340,7 +355,7 @@ public function testAddingIncrementingID() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" serial primary key not null', $statements[0]); + $this->assertSame('alter table "users" add column "id" serial not null primary key', $statements[0]); } public function testAddingSmallIncrementingID() @@ -350,7 +365,7 @@ public function testAddingSmallIncrementingID() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" smallserial primary key not null', $statements[0]); + $this->assertSame('alter table "users" add column "id" smallserial not null primary key', $statements[0]); } public function testAddingMediumIncrementingID() @@ -360,7 +375,7 @@ public function testAddingMediumIncrementingID() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" serial primary key not null', $statements[0]); + $this->assertSame('alter table "users" add column "id" serial not null primary key', $statements[0]); } public function testAddingID() @@ -370,14 +385,14 @@ public function testAddingID() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" bigserial primary key not null', $statements[0]); + $this->assertSame('alter table "users" add column "id" bigserial not null primary key', $statements[0]); $blueprint = new Blueprint('users'); $blueprint->id('foo'); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" bigserial primary key not null', $statements[0]); + $this->assertSame('alter table "users" add column "foo" bigserial not null primary key', $statements[0]); } public function testAddingForeignID() @@ -408,7 +423,7 @@ public function testAddingBigIncrementingID() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" bigserial primary key not null', $statements[0]); + $this->assertSame('alter table "users" add column "id" bigserial not null primary key', $statements[0]); } public function testAddingString() @@ -505,7 +520,7 @@ public function testAddingBigInteger() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" bigserial primary key not null', $statements[0]); + $this->assertSame('alter table "users" add column "foo" bigserial not null primary key', $statements[0]); } public function testAddingInteger() @@ -522,7 +537,7 @@ public function testAddingInteger() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" serial primary key not null', $statements[0]); + $this->assertSame('alter table "users" add column "foo" serial not null primary key', $statements[0]); } public function testAddingMediumInteger() @@ -539,7 +554,7 @@ public function testAddingMediumInteger() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" serial primary key not null', $statements[0]); + $this->assertSame('alter table "users" add column "foo" serial not null primary key', $statements[0]); } public function testAddingTinyInteger() @@ -556,7 +571,7 @@ public function testAddingTinyInteger() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" smallserial primary key not null', $statements[0]); + $this->assertSame('alter table "users" add column "foo" smallserial not null primary key', $statements[0]); } public function testAddingSmallInteger() @@ -573,7 +588,7 @@ public function testAddingSmallInteger() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" smallserial primary key not null', $statements[0]); + $this->assertSame('alter table "users" add column "foo" smallserial not null primary key', $statements[0]); } public function testAddingFloat() @@ -902,25 +917,25 @@ public function testAddingGeneratedAs() $blueprint->increments('foo')->generatedAs(); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer generated by default as identity primary key not null', $statements[0]); + $this->assertSame('alter table "users" add column "foo" integer not null generated by default as identity primary key', $statements[0]); // With always modifier $blueprint = new Blueprint('users'); $blueprint->increments('foo')->generatedAs()->always(); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer generated always as identity primary key not null', $statements[0]); + $this->assertSame('alter table "users" add column "foo" integer not null generated always as identity primary key', $statements[0]); // With sequence options $blueprint = new Blueprint('users'); $blueprint->increments('foo')->generatedAs('increment by 10 start with 100'); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer generated by default as identity (increment by 10 start with 100) primary key not null', $statements[0]); + $this->assertSame('alter table "users" add column "foo" integer not null generated by default as identity (increment by 10 start with 100) primary key', $statements[0]); // Not a primary key $blueprint = new Blueprint('users'); $blueprint->integer('foo')->generatedAs(); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer generated by default as identity not null', $statements[0]); + $this->assertSame('alter table "users" add column "foo" integer not null generated by default as identity', $statements[0]); } public function testAddingVirtualAs() diff --git a/tests/Database/DatabaseSQLiteSchemaGrammarTest.php b/tests/Database/DatabaseSQLiteSchemaGrammarTest.php index 3546ddc9db57..1dfd63c54fa7 100755 --- a/tests/Database/DatabaseSQLiteSchemaGrammarTest.php +++ b/tests/Database/DatabaseSQLiteSchemaGrammarTest.php @@ -27,7 +27,7 @@ public function testBasicCreateTable() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('create table "users" ("id" integer not null primary key autoincrement, "email" varchar not null)', $statements[0]); + $this->assertSame('create table "users" ("id" integer primary key autoincrement not null, "email" varchar not null)', $statements[0]); $blueprint = new Blueprint('users'); $blueprint->increments('id'); @@ -36,7 +36,7 @@ public function testBasicCreateTable() $this->assertCount(2, $statements); $expected = [ - 'alter table "users" add column "id" integer not null primary key autoincrement', + 'alter table "users" add column "id" integer primary key autoincrement not null', 'alter table "users" add column "email" varchar not null', ]; $this->assertEquals($expected, $statements); @@ -52,7 +52,7 @@ public function testCreateTemporaryTable() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('create temporary table "users" ("id" integer not null primary key autoincrement, "email" varchar not null)', $statements[0]); + $this->assertSame('create temporary table "users" ("id" integer primary key autoincrement not null, "email" varchar not null)', $statements[0]); } public function testDropTable() @@ -260,7 +260,7 @@ public function testAddingIncrementingID() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" integer not null primary key autoincrement', $statements[0]); + $this->assertSame('alter table "users" add column "id" integer primary key autoincrement not null', $statements[0]); } public function testAddingSmallIncrementingID() @@ -270,7 +270,7 @@ public function testAddingSmallIncrementingID() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" integer not null primary key autoincrement', $statements[0]); + $this->assertSame('alter table "users" add column "id" integer primary key autoincrement not null', $statements[0]); } public function testAddingMediumIncrementingID() @@ -280,7 +280,7 @@ public function testAddingMediumIncrementingID() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" integer not null primary key autoincrement', $statements[0]); + $this->assertSame('alter table "users" add column "id" integer primary key autoincrement not null', $statements[0]); } public function testAddingID() @@ -290,14 +290,14 @@ public function testAddingID() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" integer not null primary key autoincrement', $statements[0]); + $this->assertSame('alter table "users" add column "id" integer primary key autoincrement not null', $statements[0]); $blueprint = new Blueprint('users'); $blueprint->id('foo'); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null primary key autoincrement', $statements[0]); + $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); } public function testAddingForeignID() @@ -328,7 +328,7 @@ public function testAddingBigIncrementingID() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" integer not null primary key autoincrement', $statements[0]); + $this->assertSame('alter table "users" add column "id" integer primary key autoincrement not null', $statements[0]); } public function testAddingString() @@ -379,7 +379,7 @@ public function testAddingBigInteger() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null primary key autoincrement', $statements[0]); + $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); } public function testAddingInteger() @@ -396,7 +396,7 @@ public function testAddingInteger() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null primary key autoincrement', $statements[0]); + $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); } public function testAddingMediumInteger() @@ -413,7 +413,7 @@ public function testAddingMediumInteger() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null primary key autoincrement', $statements[0]); + $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); } public function testAddingTinyInteger() @@ -430,7 +430,7 @@ public function testAddingTinyInteger() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null primary key autoincrement', $statements[0]); + $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); } public function testAddingSmallInteger() @@ -447,7 +447,7 @@ public function testAddingSmallInteger() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null primary key autoincrement', $statements[0]); + $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); } public function testAddingFloat() @@ -873,7 +873,7 @@ public function testAddingGeneratedColumn() $this->assertCount(2, $statements); $expected = [ 'alter table "products" add column "price" integer not null', - 'alter table "products" add column "discounted_virtual" integer as ("price" - 5) not null', + 'alter table "products" add column "discounted_virtual" integer not null as ("price" - 5)', ]; $this->assertSame($expected, $statements); } diff --git a/tests/Database/DatabaseSchemaBlueprintIntegrationTest.php b/tests/Database/DatabaseSchemaBlueprintIntegrationTest.php index 1d97005dae9e..f97e7c0183e4 100644 --- a/tests/Database/DatabaseSchemaBlueprintIntegrationTest.php +++ b/tests/Database/DatabaseSchemaBlueprintIntegrationTest.php @@ -130,6 +130,147 @@ public function testDroppingColumnsWithoutDoctrineWorks() $this->assertEquals(['alter table "users" drop column "name"'], $blueprint->toSql($connection, new SQLiteGrammar)); } + public function testNativeColumnModifyingOnMySql() + { + $connection = $this->db->connection(); + $schema = $connection->getSchemaBuilder(); + + $schema->useNativeSchemaOperationsIfPossible(); + + $blueprint = new Blueprint('users', function ($table) { + $table->double('amount', 6, 2)->nullable()->invisible()->after('name')->change(); + $table->timestamp('added_at', 4)->nullable(false)->useCurrent()->useCurrentOnUpdate()->change(); + $table->enum('difficulty', ['easy', 'hard'])->default('easy')->charset('utf8mb4')->collation('unicode')->change(); + $table->multiPolygon('positions')->srid(1234)->storedAs('expression')->change(); + $table->string('old_name', 50)->renameTo('new_name')->change(); + $table->bigIncrements('id')->first()->from(10)->comment('my comment')->change(); + }); + + $this->assertEquals([ + 'alter table `users` ' + .'modify `amount` double(6, 2) null invisible after `name`, ' + .'modify `added_at` timestamp(4) not null default CURRENT_TIMESTAMP(4) on update CURRENT_TIMESTAMP(4), ' + ."modify `difficulty` enum('easy', 'hard') character set utf8mb4 collate 'unicode' not null default 'easy', " + .'modify `positions` multipolygon as (expression) stored srid 1234, ' + .'change `old_name` `new_name` varchar(50) not null, ' + ."modify `id` bigint unsigned not null auto_increment primary key comment 'my comment' first", + 'alter table `users` auto_increment = 10', + ], $blueprint->toSql($connection, new MySqlGrammar)); + } + + public function testNativeColumnModifyingOnPostgreSql() + { + $connection = $this->db->connection(); + $schema = $connection->getSchemaBuilder(); + + $schema->useNativeSchemaOperationsIfPossible(); + + $blueprint = new Blueprint('users', function ($table) { + $table->integer('code')->autoIncrement()->from(10)->comment('my comment')->change(); + }); + + $this->assertEquals([ + 'alter table "users" ' + .'alter column "code" type serial, ' + .'alter column "code" set not null, ' + .'alter column "code" drop default, ' + .'alter column "code" drop identity if exists', + 'alter sequence users_code_seq restart with 10', + 'comment on column "users"."code" is \'my comment\'', + ], $blueprint->toSql($connection, new PostgresGrammar)); + + $blueprint = new Blueprint('users', function ($table) { + $table->char('name', 40)->nullable()->default('easy')->collation('unicode')->change(); + }); + + $this->assertEquals([ + 'alter table "users" ' + .'alter column "name" type char(40) collate "unicode", ' + .'alter column "name" drop not null, ' + .'alter column "name" set default \'easy\', ' + .'alter column "name" drop identity if exists', + 'comment on column "users"."name" is NULL', + ], $blueprint->toSql($connection, new PostgresGrammar)); + + $blueprint = new Blueprint('users', function ($table) { + $table->integer('foo')->generatedAs('expression')->always()->change(); + }); + + $this->assertEquals([ + 'alter table "users" ' + .'alter column "foo" type integer, ' + .'alter column "foo" set not null, ' + .'alter column "foo" drop default, ' + .'alter column "foo" drop identity if exists, ' + .'alter column "foo" add generated always as identity (expression)', + 'comment on column "users"."foo" is NULL', + ], $blueprint->toSql($connection, new PostgresGrammar)); + + $blueprint = new Blueprint('users', function ($table) { + $table->point('foo')->isGeometry()->projection(1234)->change(); + }); + + $this->assertEquals([ + 'alter table "users" ' + .'alter column "foo" type geometry(point, 1234), ' + .'alter column "foo" set not null, ' + .'alter column "foo" drop default, ' + .'alter column "foo" drop identity if exists', + 'comment on column "users"."foo" is NULL', + ], $blueprint->toSql($connection, new PostgresGrammar)); + + $blueprint = new Blueprint('users', function ($table) { + $table->timestamp('added_at', 2)->useCurrent()->storedAs(null)->change(); + }); + + $this->assertEquals([ + 'alter table "users" ' + .'alter column "added_at" type timestamp(2) without time zone, ' + .'alter column "added_at" set not null, ' + .'alter column "added_at" set default CURRENT_TIMESTAMP, ' + .'alter column "added_at" drop expression if exists, ' + .'alter column "added_at" drop identity if exists', + 'comment on column "users"."added_at" is NULL', + ], $blueprint->toSql($connection, new PostgresGrammar)); + } + + public function testNativeColumnModifyingOnSqlServer() + { + $connection = $this->db->connection(); + $schema = $connection->getSchemaBuilder(); + + $schema->useNativeSchemaOperationsIfPossible(); + + $blueprint = new Blueprint('users', function ($table) { + $table->timestamp('added_at', 4)->nullable(false)->useCurrent()->change(); + }); + + $this->assertEquals([ + "DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE [dbo].[users] DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID('[dbo].[users]') AND [name] in ('added_at') AND [default_object_id] <> 0;EXEC(@sql)", + 'alter table "users" alter column "added_at" datetime2(4) not null', + 'alter table "users" add default CURRENT_TIMESTAMP for "added_at"', + ], $blueprint->toSql($connection, new SqlServerGrammar)); + + $blueprint = new Blueprint('users', function ($table) { + $table->char('name', 40)->nullable()->default('easy')->collation('unicode')->change(); + }); + + $this->assertEquals([ + "DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE [dbo].[users] DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID('[dbo].[users]') AND [name] in ('name') AND [default_object_id] <> 0;EXEC(@sql)", + 'alter table "users" alter column "name" nchar(40) collate unicode null', + 'alter table "users" add default \'easy\' for "name"', + ], $blueprint->toSql($connection, new SqlServerGrammar)); + + $blueprint = new Blueprint('users', function ($table) { + $table->integer('foo')->change(); + }); + + $this->assertEquals([ + "DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE [dbo].[users] DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID('[dbo].[users]') AND [name] in ('foo') AND [default_object_id] <> 0;EXEC(@sql)", + 'alter table "users" alter column "foo" int not null', + ], $blueprint->toSql($connection, new SqlServerGrammar)); + } + public function testChangingColumnWithCollationWorks() { $this->db->connection()->getSchemaBuilder()->create('users', function ($table) { diff --git a/tests/Database/DatabaseSchemaBlueprintTest.php b/tests/Database/DatabaseSchemaBlueprintTest.php index 3e6ce17f5a43..45a72191faa6 100755 --- a/tests/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Database/DatabaseSchemaBlueprintTest.php @@ -113,16 +113,16 @@ public function testDefaultCurrentDateTime() $connection = m::mock(Connection::class); $blueprint = clone $base; - $this->assertEquals(['alter table `users` add `created` datetime default CURRENT_TIMESTAMP not null'], $blueprint->toSql($connection, new MySqlGrammar)); + $this->assertEquals(['alter table `users` add `created` datetime not null default CURRENT_TIMESTAMP'], $blueprint->toSql($connection, new MySqlGrammar)); $blueprint = clone $base; - $this->assertEquals(['alter table "users" add column "created" timestamp(0) without time zone default CURRENT_TIMESTAMP not null'], $blueprint->toSql($connection, new PostgresGrammar)); + $this->assertEquals(['alter table "users" add column "created" timestamp(0) without time zone not null default CURRENT_TIMESTAMP'], $blueprint->toSql($connection, new PostgresGrammar)); $blueprint = clone $base; - $this->assertEquals(['alter table "users" add column "created" datetime default CURRENT_TIMESTAMP not null'], $blueprint->toSql($connection, new SQLiteGrammar)); + $this->assertEquals(['alter table "users" add column "created" datetime not null default CURRENT_TIMESTAMP'], $blueprint->toSql($connection, new SQLiteGrammar)); $blueprint = clone $base; - $this->assertEquals(['alter table "users" add "created" datetime default CURRENT_TIMESTAMP not null'], $blueprint->toSql($connection, new SqlServerGrammar)); + $this->assertEquals(['alter table "users" add "created" datetime not null default CURRENT_TIMESTAMP'], $blueprint->toSql($connection, new SqlServerGrammar)); } public function testDefaultCurrentTimestamp() @@ -134,16 +134,16 @@ public function testDefaultCurrentTimestamp() $connection = m::mock(Connection::class); $blueprint = clone $base; - $this->assertEquals(['alter table `users` add `created` timestamp default CURRENT_TIMESTAMP not null'], $blueprint->toSql($connection, new MySqlGrammar)); + $this->assertEquals(['alter table `users` add `created` timestamp not null default CURRENT_TIMESTAMP'], $blueprint->toSql($connection, new MySqlGrammar)); $blueprint = clone $base; - $this->assertEquals(['alter table "users" add column "created" timestamp(0) without time zone default CURRENT_TIMESTAMP not null'], $blueprint->toSql($connection, new PostgresGrammar)); + $this->assertEquals(['alter table "users" add column "created" timestamp(0) without time zone not null default CURRENT_TIMESTAMP'], $blueprint->toSql($connection, new PostgresGrammar)); $blueprint = clone $base; - $this->assertEquals(['alter table "users" add column "created" datetime default CURRENT_TIMESTAMP not null'], $blueprint->toSql($connection, new SQLiteGrammar)); + $this->assertEquals(['alter table "users" add column "created" datetime not null default CURRENT_TIMESTAMP'], $blueprint->toSql($connection, new SQLiteGrammar)); $blueprint = clone $base; - $this->assertEquals(['alter table "users" add "created" datetime default CURRENT_TIMESTAMP not null'], $blueprint->toSql($connection, new SqlServerGrammar)); + $this->assertEquals(['alter table "users" add "created" datetime not null default CURRENT_TIMESTAMP'], $blueprint->toSql($connection, new SqlServerGrammar)); } public function testUnsignedDecimalTable() diff --git a/tests/Database/DatabaseSqlServerSchemaGrammarTest.php b/tests/Database/DatabaseSqlServerSchemaGrammarTest.php index 4966278360b0..40c0ff6ba61a 100755 --- a/tests/Database/DatabaseSqlServerSchemaGrammarTest.php +++ b/tests/Database/DatabaseSqlServerSchemaGrammarTest.php @@ -25,7 +25,7 @@ public function testBasicCreateTable() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('create table "users" ("id" int identity primary key not null, "email" nvarchar(255) not null)', $statements[0]); + $this->assertSame('create table "users" ("id" int not null identity primary key, "email" nvarchar(255) not null)', $statements[0]); $blueprint = new Blueprint('users'); $blueprint->increments('id'); @@ -33,7 +33,7 @@ public function testBasicCreateTable() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "id" int identity primary key not null, "email" nvarchar(255) not null', $statements[0]); + $this->assertSame('alter table "users" add "id" int not null identity primary key, "email" nvarchar(255) not null', $statements[0]); $blueprint = new Blueprint('users'); $blueprint->create(); @@ -42,7 +42,7 @@ public function testBasicCreateTable() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()->setTablePrefix('prefix_')); $this->assertCount(1, $statements); - $this->assertSame('create table "prefix_users" ("id" int identity primary key not null, "email" nvarchar(255) not null)', $statements[0]); + $this->assertSame('create table "prefix_users" ("id" int not null identity primary key, "email" nvarchar(255) not null)', $statements[0]); } public function testCreateTemporaryTable() @@ -55,7 +55,7 @@ public function testCreateTemporaryTable() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('create table "#users" ("id" int identity primary key not null, "email" nvarchar(255) not null)', $statements[0]); + $this->assertSame('create table "#users" ("id" int not null identity primary key, "email" nvarchar(255) not null)', $statements[0]); } public function testDropTable() @@ -305,7 +305,7 @@ public function testAddingIncrementingID() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "id" int identity primary key not null', $statements[0]); + $this->assertSame('alter table "users" add "id" int not null identity primary key', $statements[0]); } public function testAddingSmallIncrementingID() @@ -315,7 +315,7 @@ public function testAddingSmallIncrementingID() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "id" smallint identity primary key not null', $statements[0]); + $this->assertSame('alter table "users" add "id" smallint not null identity primary key', $statements[0]); } public function testAddingMediumIncrementingID() @@ -325,7 +325,7 @@ public function testAddingMediumIncrementingID() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "id" int identity primary key not null', $statements[0]); + $this->assertSame('alter table "users" add "id" int not null identity primary key', $statements[0]); } public function testAddingID() @@ -335,14 +335,14 @@ public function testAddingID() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "id" bigint identity primary key not null', $statements[0]); + $this->assertSame('alter table "users" add "id" bigint not null identity primary key', $statements[0]); $blueprint = new Blueprint('users'); $blueprint->id('foo'); $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" bigint identity primary key not null', $statements[0]); + $this->assertSame('alter table "users" add "foo" bigint not null identity primary key', $statements[0]); } public function testAddingForeignID() @@ -373,7 +373,7 @@ public function testAddingBigIncrementingID() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "id" bigint identity primary key not null', $statements[0]); + $this->assertSame('alter table "users" add "id" bigint not null identity primary key', $statements[0]); } public function testAddingString() @@ -424,7 +424,7 @@ public function testAddingBigInteger() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" bigint identity primary key not null', $statements[0]); + $this->assertSame('alter table "users" add "foo" bigint not null identity primary key', $statements[0]); } public function testAddingInteger() @@ -441,7 +441,7 @@ public function testAddingInteger() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" int identity primary key not null', $statements[0]); + $this->assertSame('alter table "users" add "foo" int not null identity primary key', $statements[0]); } public function testAddingMediumInteger() @@ -458,7 +458,7 @@ public function testAddingMediumInteger() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" int identity primary key not null', $statements[0]); + $this->assertSame('alter table "users" add "foo" int not null identity primary key', $statements[0]); } public function testAddingTinyInteger() @@ -475,7 +475,7 @@ public function testAddingTinyInteger() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" tinyint identity primary key not null', $statements[0]); + $this->assertSame('alter table "users" add "foo" tinyint not null identity primary key', $statements[0]); } public function testAddingSmallInteger() @@ -492,7 +492,7 @@ public function testAddingSmallInteger() $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" smallint identity primary key not null', $statements[0]); + $this->assertSame('alter table "users" add "foo" smallint not null identity primary key', $statements[0]); } public function testAddingFloat() diff --git a/tests/Integration/Migration/MigratorTest.php b/tests/Integration/Migration/MigratorTest.php index 28ae835e8a95..f4210789ada5 100644 --- a/tests/Integration/Migration/MigratorTest.php +++ b/tests/Integration/Migration/MigratorTest.php @@ -81,7 +81,7 @@ public function testPretendMigrate() $this->expectTwoColumnDetail('CreatePeopleTable'); $this->expectBulletList([ - 'create table "people" ("id" integer not null primary key autoincrement, "name" varchar not null, "email" varchar not null, "password" varchar not null, "remember_token" varchar, "created_at" datetime, "updated_at" datetime)', + 'create table "people" ("id" integer primary key autoincrement not null, "name" varchar not null, "email" varchar not null, "password" varchar not null, "remember_token" varchar, "created_at" datetime, "updated_at" datetime)', 'create unique index "people_email_unique" on "people" ("email")', ]);