From 1842d9f15c30db481a0e2f7518937de5f84c541c Mon Sep 17 00:00:00 2001 From: tpetry Date: Tue, 7 Oct 2025 15:56:13 +0200 Subject: [PATCH] [12.x] PostgreSQL virtual columns --- .../Query/Processors/PostgresProcessor.php | 1 + .../Schema/Grammars/PostgresGrammar.php | 2 +- .../DatabasePostgresSchemaGrammarTest.php | 4 +-- .../Database/SchemaBuilderTest.php | 29 +++++++++---------- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/Illuminate/Database/Query/Processors/PostgresProcessor.php b/src/Illuminate/Database/Query/Processors/PostgresProcessor.php index 871575a5c488..2818f91d8c1f 100755 --- a/src/Illuminate/Database/Query/Processors/PostgresProcessor.php +++ b/src/Illuminate/Database/Query/Processors/PostgresProcessor.php @@ -94,6 +94,7 @@ public function processColumns($results) 'generation' => $result->generated ? [ 'type' => match ($result->generated) { 's' => 'stored', + 'v' => 'virtual', default => null, }, 'expression' => $result->default, diff --git a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php index fa95e2a50fd9..73e78071a33e 100755 --- a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php @@ -1257,7 +1257,7 @@ protected function modifyVirtualAs(Blueprint $blueprint, Fluent $column) } if (! is_null($column->virtualAs)) { - return " generated always as ({$this->getValue($column->virtualAs)})"; + return " generated always as ({$this->getValue($column->virtualAs)}) virtual"; } } diff --git a/tests/Database/DatabasePostgresSchemaGrammarTest.php b/tests/Database/DatabasePostgresSchemaGrammarTest.php index cf2ea7d63f4a..ee6667ca064f 100755 --- a/tests/Database/DatabasePostgresSchemaGrammarTest.php +++ b/tests/Database/DatabasePostgresSchemaGrammarTest.php @@ -966,7 +966,7 @@ public function testAddingVirtualAs() $this->assertCount(2, $statements); $this->assertSame([ 'alter table "users" add column "foo" integer null', - 'alter table "users" add column "bar" boolean not null generated always as (foo is not null)', + 'alter table "users" add column "bar" boolean not null generated always as (foo is not null) virtual', ], $statements); $blueprint = new Blueprint($this->getConnection(), 'users'); @@ -976,7 +976,7 @@ public function testAddingVirtualAs() $this->assertCount(2, $statements); $this->assertSame([ 'alter table "users" add column "foo" integer null', - 'alter table "users" add column "bar" boolean not null generated always as (foo is not null)', + 'alter table "users" add column "bar" boolean not null generated always as (foo is not null) virtual', ], $statements); } diff --git a/tests/Integration/Database/SchemaBuilderTest.php b/tests/Integration/Database/SchemaBuilderTest.php index 6fca5a006116..1628a5b7ac76 100644 --- a/tests/Integration/Database/SchemaBuilderTest.php +++ b/tests/Integration/Database/SchemaBuilderTest.php @@ -636,7 +636,7 @@ public function testModifyingStoredColumnOnSqlite() )); } - #[RequiresDatabase('pgsql', '>=12.0')] + #[RequiresDatabase('pgsql', '>=18')] public function testGettingGeneratedColumns() { Schema::create('test', function (Blueprint $table) { @@ -646,9 +646,7 @@ public function testGettingGeneratedColumns() $table->computed('virtual_price', 'price - 5'); $table->computed('stored_price', 'price - 10')->persisted(); } else { - if ($this->driver !== 'pgsql') { - $table->integer('virtual_price')->virtualAs('price - 5'); - } + $table->integer('virtual_price')->virtualAs('price - 5'); $table->integer('stored_price')->storedAs('price - 10'); } }); @@ -658,18 +656,17 @@ public function testGettingGeneratedColumns() $this->assertTrue(collect($columns)->contains( fn ($column) => $column['name'] === 'price' && is_null($column['generation']) )); - if ($this->driver !== 'pgsql') { - $this->assertTrue(collect($columns)->contains( - fn ($column) => $column['name'] === 'virtual_price' - && $column['generation']['type'] === 'virtual' - && match ($this->driver) { - 'mysql' => $column['generation']['expression'] === '(`price` - 5)', - 'mariadb' => $column['generation']['expression'] === '`price` - 5', - 'sqlsrv' => $column['generation']['expression'] === '([price]-(5))', - default => $column['generation']['expression'] === 'price - 5', - } - )); - } + $this->assertTrue(collect($columns)->contains( + fn ($column) => $column['name'] === 'virtual_price' + && $column['generation']['type'] === 'virtual' + && match ($this->driver) { + 'mysql' => $column['generation']['expression'] === '(`price` - 5)', + 'mariadb' => $column['generation']['expression'] === '`price` - 5', + 'sqlsrv' => $column['generation']['expression'] === '([price]-(5))', + 'pgsql' => $column['generation']['expression'] === '(price - 5)', + default => $column['generation']['expression'] === 'price - 5', + } + )); $this->assertTrue(collect($columns)->contains( fn ($column) => $column['name'] === 'stored_price' && $column['generation']['type'] === 'stored'