From 0074e7e95135f3893904fe38325197114e4fe4fb Mon Sep 17 00:00:00 2001 From: tpetry Date: Tue, 16 Jul 2024 11:28:08 +0200 Subject: [PATCH] conflicts with upstream Laravel changes (resolves #85, resolves #87) Co-authored-by: Hafez Divandari --- src/Schema/Grammars/GrammarTable.php | 39 ++++++++++------------ src/Schema/Grammars/GrammarTypes.php | 50 ++++++++++++---------------- tests/CompatibilityTest.php | 39 ++++++++++++++++++++++ 3 files changed, 79 insertions(+), 49 deletions(-) create mode 100644 tests/CompatibilityTest.php diff --git a/src/Schema/Grammars/GrammarTable.php b/src/Schema/Grammars/GrammarTable.php index ea43814..a10ea46 100644 --- a/src/Schema/Grammars/GrammarTable.php +++ b/src/Schema/Grammars/GrammarTable.php @@ -4,6 +4,7 @@ namespace Tpetry\PostgresqlEnhanced\Schema\Grammars; +use Arr; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Fluent; @@ -16,36 +17,32 @@ trait GrammarTable */ public function compileAdd(Blueprint $blueprint, Fluent $command): array { - $sql = []; + // In Laravel 11.15.0 the logic was changed that compileAdd is only for one column (the one in the command) of + // the blueprint and not all ones of the blueprint as before. + /** @var \Illuminate\Database\Schema\ColumnDefinition[] $columns */ + $columns = isset($command['column']) ? [$command['column']] : $blueprint->getAddedColumns(); - foreach (array_reverse($blueprint->getAddedColumns()) as $column) { + $sqlChangeDefault = []; + foreach ($columns as $column) { $attributes = $column->getAttributes(); if (!\array_key_exists('initial', $attributes)) { continue; } - if (\array_key_exists('default', $attributes)) { - $sql[] = sprintf('alter table %s alter column %s set default %s', - $this->wrapTable($blueprint), - $this->wrap($column), - $this->getDefaultValue($column['default']) - ); - } else { - $sql[] = sprintf('alter table %s alter column %s drop default', - $this->wrapTable($blueprint), - $this->wrap($column), - ); - } - + // Transform the column definition to a standard one understood by Laravel: + // - The `initial` modifier is saved to the `default` modifier to set the initial value when creating the column. + // - A SQL query is created to reset the `default` value afterward to NULL or the specified value. + $sqlChangeDefault[] = match (\array_key_exists('default', $attributes)) { + true => "alter table {$this->wrapTable($blueprint)} alter column {$this->wrap($column)} set default {$this->getDefaultValue($column['default'])}", + false => "alter table {$this->wrapTable($blueprint)} alter column {$this->wrap($column)} drop default", + }; $column['default'] = $column['initial']; } - $sql[] = sprintf('alter table %s %s', - $this->wrapTable($blueprint), - implode(', ', $this->prefixArray('add column', $this->getColumns($blueprint))) - ); - - return array_reverse($sql); + return [ + ...Arr::wrap(parent::compileAdd($blueprint, $command)), // Some Laravel versions produce a single string while others an array. + ...$sqlChangeDefault, + ]; } /** diff --git a/src/Schema/Grammars/GrammarTypes.php b/src/Schema/Grammars/GrammarTypes.php index 65794bb..ce91f8c 100644 --- a/src/Schema/Grammars/GrammarTypes.php +++ b/src/Schema/Grammars/GrammarTypes.php @@ -17,34 +17,33 @@ trait GrammarTypes */ public function compileChange(BaseBlueprint $blueprint, Fluent $command, Connection $connection): array { - $queries = []; - // The table prefix is accessed differently based on Laravel version. In old version the $prefix was public, // while with new ones the $blueprint->prefix() method should be used. The issue is solved by invading the // object and getting the property directly. $prefix = (fn () => $this->prefix)->call($blueprint); - foreach ($blueprint->getChangedColumns() as $changedColumn) { - $blueprintColumn = new BaseBlueprint($blueprint->getTable(), null, $prefix); - $blueprintColumn->addColumn( - $changedColumn['type'], - $changedColumn['name'], - Arr::except($changedColumn->toArray(), ['compression']), - ); - - // Remove Compression modifier because Laravel 11 won't work correctly with it (migrator builts incorrectly SQL). - $_modifiers = $this->modifiers; - $this->modifiers = array_filter($this->modifiers, fn ($str) => !\in_array($str, ['Compression'])); - $changes = Arr::wrap(parent::compileChange($blueprintColumn, $command, $connection)); - $this->modifiers = $_modifiers; - - foreach ($changes as $sql) { + // In Laravel 11.15.0 the logic was changed that compileChange is only for one column (the one in the command) + // of the blueprint and not all ones of the blueprint as before. + /** @var \Illuminate\Database\Schema\ColumnDefinition[] $columns */ + $columns = isset($command['column']) ? [$command['column']] : $blueprint->getChangedColumns(); + + $queries = []; + foreach ($columns as $column) { + $modifierCompression = $column['compression']; + $modifierUsing = $column['using']; + unset($column['compression'], $column['using']); + + $blueprintColumnExtract = new BaseBlueprint($blueprint->getTable(), null, $prefix); + $blueprintColumnExtract->addColumn($column['type'], $column['name'], $column->toArray()); + $blueprintColumnExtractQueries = Arr::wrap(parent::compileChange($blueprint, $command, $connection)); + + foreach ($blueprintColumnExtractQueries as $sql) { $regex = Regex::match('/^ALTER table (?P.*?) alter (column )?(?P.*?) type (?P\w+)(?P,.*)?/i', $sql); - if (filled($changedColumn['using']) && $regex->hasMatch()) { - $using = match ($connection->getSchemaGrammar()->isExpression($changedColumn['using'])) { - true => $connection->getSchemaGrammar()->getValue($changedColumn['using']), - false => $changedColumn['using'], + if (filled($modifierUsing) && $regex->hasMatch()) { + $using = match ($connection->getSchemaGrammar()->isExpression($modifierUsing)) { + true => $connection->getSchemaGrammar()->getValue($modifierUsing), + false => $modifierUsing, }; $queries[] = match (filled($modifiers = $regex->groupOr('modifiers', ''))) { @@ -56,13 +55,8 @@ public function compileChange(BaseBlueprint $blueprint, Fluent $command, Connect } } - if (filled($changedColumn['compression'])) { - $queries[] = sprintf( - 'alter table %s alter %s set compression %s', - $this->wrapTable($blueprint->getTable()), - $this->wrap($changedColumn['name']), - $this->wrap($changedColumn['compression']), - ); + if (filled($modifierCompression)) { + $queries[] = "alter table {$this->wrapTable($blueprint)} alter {$this->wrap($column['name'])} set compression {$this->wrap($modifierCompression)}"; } } diff --git a/tests/CompatibilityTest.php b/tests/CompatibilityTest.php new file mode 100644 index 0000000..16c8d04 --- /dev/null +++ b/tests/CompatibilityTest.php @@ -0,0 +1,39 @@ +withQueryLog(function (): void { + Schema::table('test', static function (Blueprint $table): void { + $table->text('column_one'); + $table->text('column_two'); + }); + }); + + $expected = match (true) { + version_compare(App::version(), '11.15.0', '>=') => [ + 'alter table "test" add column "column_one" text not null', + 'alter table "test" add column "column_two" text not null', + ], + default => [ + 'alter table "test" add column "column_one" text not null, add column "column_two" text not null', + ], + }; + + $this->assertEquals($expected, array_column($queries, 'query')); + } +}