Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] Add computed column support (storedAs, virtualAs) to SQLite #33618

Merged
merged 3 commits into from
Jul 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/Illuminate/Database/Schema/ColumnDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace Illuminate\Database\Schema;

use Illuminate\Database\Query\Expression;
use Illuminate\Support\Fluent;

/**
Expand All @@ -21,12 +20,12 @@
* @method $this persisted() Mark the computed generated column as persistent (SQL Server)
* @method $this primary() Add a primary index
* @method $this spatialIndex() Add a spatial index
* @method $this storedAs(string $expression) Create a stored generated column (MySQL)
* @method $this storedAs(string $expression) Create a stored generated column (MySQL/SQLite)
* @method $this type(string $type) Specify a type for the column
* @method $this unique(string $indexName = null) Add a unique index
* @method $this unsigned() Set the INTEGER column as UNSIGNED (MySQL)
* @method $this useCurrent() Set the TIMESTAMP column to use CURRENT_TIMESTAMP as default value
* @method $this virtualAs(string $expression) Create a virtual generated column (MySQL)
* @method $this virtualAs(string $expression) Create a virtual generated column (MySQL/SQLite)
*/
class ColumnDefinition extends Fluent
{
Expand Down
57 changes: 53 additions & 4 deletions src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class SQLiteGrammar extends Grammar
*
* @var array
*/
protected $modifiers = ['Nullable', 'Default', 'Increment'];
protected $modifiers = ['VirtualAs', 'StoredAs', 'Nullable', 'Default', 'Increment'];

/**
* The columns available as serials.
Expand Down Expand Up @@ -137,7 +137,9 @@ public function compileAdd(Blueprint $blueprint, Fluent $command)
{
$columns = $this->prefixArray('add column', $this->getColumns($blueprint));

return collect($columns)->map(function ($column) use ($blueprint) {
return collect($columns)->reject(function ($column) {
return preg_match('/as \(.*\) stored/', $column) > 0;
})->map(function ($column) use ($blueprint) {
return 'alter table '.$this->wrapTable($blueprint).' '.$column;
})->all();
}
Expand Down Expand Up @@ -822,6 +824,47 @@ public function typeMultiPolygon(Fluent $column)
return 'multipolygon';
}

/**
* Create the column definition for a generated, computed column type.
*
* @param \Illuminate\Support\Fluent $column
* @return void
*
* @throws \RuntimeException
*/
protected function typeComputed(Fluent $column)
{
throw new RuntimeException('This database driver requires a type, see the virtualAs / storedAs modifiers.');
}

/**
* Get the SQL for a generated virtual column modifier.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $column
* @return string|null
*/
protected function modifyVirtualAs(Blueprint $blueprint, Fluent $column)
{
if (! is_null($column->virtualAs)) {
return " as ({$column->virtualAs})";
}
}

/**
* Get the SQL for a generated stored column modifier.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $column
* @return string|null
*/
protected function modifyStoredAs(Blueprint $blueprint, Fluent $column)
{
if (! is_null($column->storedAs)) {
return " as ({$column->storedAs}) stored";
}
}

/**
* Get the SQL for a nullable column modifier.
*
Expand All @@ -831,7 +874,13 @@ public function typeMultiPolygon(Fluent $column)
*/
protected function modifyNullable(Blueprint $blueprint, Fluent $column)
{
return $column->nullable ? ' null' : ' not null';
if (is_null($column->virtualAs) && is_null($column->storedAs)) {
return $column->nullable ? ' null' : ' not null';
}

if ($column->nullable === false) {
return ' not null';
}
}

/**
Expand All @@ -843,7 +892,7 @@ protected function modifyNullable(Blueprint $blueprint, Fluent $column)
*/
protected function modifyDefault(Blueprint $blueprint, Fluent $column)
{
if (! is_null($column->default)) {
if (! is_null($column->default) && is_null($column->virtualAs) && is_null($column->storedAs)) {
return ' default '.$this->getDefaultValue($column->default);
}
}
Expand Down
26 changes: 26 additions & 0 deletions tests/Database/DatabaseSQLiteSchemaGrammarTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,32 @@ public function testAddingMultiPolygon()
$this->assertSame('alter table "geo" add column "coordinates" multipolygon not null', $statements[0]);
}

public function testAddingGeneratedColumn()
{
$blueprint = new Blueprint('products');
$blueprint->create();
$blueprint->integer('price');
$blueprint->integer('discounted_virtual')->virtualAs('"price" - 5');
$blueprint->integer('discounted_stored')->storedAs('"price" - 5');
$statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());

$this->assertCount(1, $statements);
$this->assertSame('create table "products" ("price" integer not null, "discounted_virtual" integer as ("price" - 5), "discounted_stored" integer as ("price" - 5) stored)', $statements[0]);

$blueprint = new Blueprint('products');
$blueprint->integer('price');
$blueprint->integer('discounted_virtual')->virtualAs('"price" - 5')->nullable(false);
$blueprint->integer('discounted_stored')->storedAs('"price" - 5')->nullable(false);
$statements = $blueprint->toSql($this->getConnection(), $this->getGrammar());

$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',
];
$this->assertSame($expected, $statements);
}

public function testGrammarsAreMacroable()
{
// compileReplace macro.
Expand Down