This commit is contained in:
LayFi.de
2025-01-27 20:49:47 +08:00
commit 4f3242bca7
2745 changed files with 309418 additions and 0 deletions
+1889
View File
File diff suppressed because it is too large Load Diff
+256
View File
@@ -0,0 +1,256 @@
<?php
namespace Illuminate\Database\Schema;
use Illuminate\Database\Connection;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Schema\Grammars\Grammar;
use Illuminate\Support\Collection;
use Illuminate\Support\Fluent;
use Illuminate\Support\Str;
class BlueprintState
{
/**
* The blueprint instance.
*
* @var \Illuminate\Database\Schema\Blueprint
*/
protected $blueprint;
/**
* The connection instance.
*
* @var \Illuminate\Database\Connection
*/
protected $connection;
/**
* The grammar instance.
*
* @var \Illuminate\Database\Schema\Grammars\Grammar
*/
protected $grammar;
/**
* The columns.
*
* @var \Illuminate\Database\Schema\ColumnDefinition[]
*/
private $columns;
/**
* The primary key.
*
* @var \Illuminate\Database\Schema\IndexDefinition|null
*/
private $primaryKey;
/**
* The indexes.
*
* @var \Illuminate\Database\Schema\IndexDefinition[]
*/
private $indexes;
/**
* The foreign keys.
*
* @var \Illuminate\Database\Schema\ForeignKeyDefinition[]
*/
private $foreignKeys;
/**
* Create a new blueprint state instance.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Database\Connection $connection
* @param \Illuminate\Database\Schema\Grammars\Grammar $grammar
* @return void
*/
public function __construct(Blueprint $blueprint, Connection $connection, Grammar $grammar)
{
$this->blueprint = $blueprint;
$this->connection = $connection;
$this->grammar = $grammar;
$schema = $connection->getSchemaBuilder();
$table = $blueprint->getTable();
$this->columns = (new Collection($schema->getColumns($table)))->map(fn ($column) => new ColumnDefinition([
'name' => $column['name'],
'type' => $column['type_name'],
'full_type_definition' => $column['type'],
'nullable' => $column['nullable'],
'default' => is_null($column['default']) ? null : new Expression(Str::wrap($column['default'], '(', ')')),
'autoIncrement' => $column['auto_increment'],
'collation' => $column['collation'],
'comment' => $column['comment'],
'virtualAs' => ! is_null($column['generation']) && $column['generation']['type'] === 'virtual'
? $column['generation']['expression'] : null,
'storedAs' => ! is_null($column['generation']) && $column['generation']['type'] === 'stored'
? $column['generation']['expression'] : null,
]))->all();
[$primary, $indexes] = (new Collection($schema->getIndexes($table)))->map(fn ($index) => new IndexDefinition([
'name' => match (true) {
$index['primary'] => 'primary',
$index['unique'] => 'unique',
default => 'index',
},
'index' => $index['name'],
'columns' => $index['columns'],
]))->partition(fn ($index) => $index->name === 'primary');
$this->indexes = $indexes->all();
$this->primaryKey = $primary->first();
$this->foreignKeys = (new Collection($schema->getForeignKeys($table)))->map(fn ($foreignKey) => new ForeignKeyDefinition([
'columns' => $foreignKey['columns'],
'on' => new Expression($foreignKey['foreign_table']),
'references' => $foreignKey['foreign_columns'],
'onUpdate' => $foreignKey['on_update'],
'onDelete' => $foreignKey['on_delete'],
]))->all();
}
/**
* Get the primary key.
*
* @return \Illuminate\Database\Schema\IndexDefinition|null
*/
public function getPrimaryKey()
{
return $this->primaryKey;
}
/**
* Get the columns.
*
* @return \Illuminate\Database\Schema\ColumnDefinition[]
*/
public function getColumns()
{
return $this->columns;
}
/**
* Get the indexes.
*
* @return \Illuminate\Database\Schema\IndexDefinition[]
*/
public function getIndexes()
{
return $this->indexes;
}
/**
* Get the foreign keys.
*
* @return \Illuminate\Database\Schema\ForeignKeyDefinition[]
*/
public function getForeignKeys()
{
return $this->foreignKeys;
}
/*
* Update the blueprint's state.
*
* @param \Illuminate\Support\Fluent $command
* @return void
*/
public function update(Fluent $command)
{
switch ($command->name) {
case 'alter':
// Already handled...
break;
case 'add':
$this->columns[] = $command->column;
break;
case 'change':
foreach ($this->columns as &$column) {
if ($column->name === $command->column->name) {
$column = $command->column;
break;
}
}
break;
case 'renameColumn':
foreach ($this->columns as $column) {
if ($column->name === $command->from) {
$column->name = $command->to;
break;
}
}
if ($this->primaryKey) {
$this->primaryKey->columns = str_replace($command->from, $command->to, $this->primaryKey->columns);
}
foreach ($this->indexes as $index) {
$index->columns = str_replace($command->from, $command->to, $index->columns);
}
foreach ($this->foreignKeys as $foreignKey) {
$foreignKey->columns = str_replace($command->from, $command->to, $foreignKey->columns);
}
break;
case 'dropColumn':
$this->columns = array_values(
array_filter($this->columns, fn ($column) => ! in_array($column->name, $command->columns))
);
break;
case 'primary':
$this->primaryKey = $command;
break;
case 'unique':
case 'index':
$this->indexes[] = $command;
break;
case 'renameIndex':
foreach ($this->indexes as $index) {
if ($index->index === $command->from) {
$index->index = $command->to;
break;
}
}
break;
case 'foreign':
$this->foreignKeys[] = $command;
break;
case 'dropPrimary':
$this->primaryKey = null;
break;
case 'dropIndex':
case 'dropUnique':
$this->indexes = array_values(
array_filter($this->indexes, fn ($index) => $index->index !== $command->index)
);
break;
case 'dropForeign':
$this->foreignKeys = array_values(
array_filter($this->foreignKeys, fn ($fk) => $fk->columns !== $command->columns)
);
break;
}
}
}
+620
View File
@@ -0,0 +1,620 @@
<?php
namespace Illuminate\Database\Schema;
use Closure;
use Illuminate\Container\Container;
use Illuminate\Database\Connection;
use Illuminate\Support\Traits\Macroable;
use InvalidArgumentException;
use LogicException;
class Builder
{
use Macroable;
/**
* The database connection instance.
*
* @var \Illuminate\Database\Connection
*/
protected $connection;
/**
* The schema grammar instance.
*
* @var \Illuminate\Database\Schema\Grammars\Grammar
*/
protected $grammar;
/**
* The Blueprint resolver callback.
*
* @var \Closure
*/
protected $resolver;
/**
* The default string length for migrations.
*
* @var int|null
*/
public static $defaultStringLength = 255;
/**
* The default relationship morph key type.
*
* @var string
*/
public static $defaultMorphKeyType = 'int';
/**
* Create a new database Schema manager.
*
* @param \Illuminate\Database\Connection $connection
* @return void
*/
public function __construct(Connection $connection)
{
$this->connection = $connection;
$this->grammar = $connection->getSchemaGrammar();
}
/**
* Set the default string length for migrations.
*
* @param int $length
* @return void
*/
public static function defaultStringLength($length)
{
static::$defaultStringLength = $length;
}
/**
* Set the default morph key type for migrations.
*
* @param string $type
* @return void
*
* @throws \InvalidArgumentException
*/
public static function defaultMorphKeyType(string $type)
{
if (! in_array($type, ['int', 'uuid', 'ulid'])) {
throw new InvalidArgumentException("Morph key type must be 'int', 'uuid', or 'ulid'.");
}
static::$defaultMorphKeyType = $type;
}
/**
* Set the default morph key type for migrations to UUIDs.
*
* @return void
*/
public static function morphUsingUuids()
{
static::defaultMorphKeyType('uuid');
}
/**
* Set the default morph key type for migrations to ULIDs.
*
* @return void
*/
public static function morphUsingUlids()
{
static::defaultMorphKeyType('ulid');
}
/**
* Create a database in the schema.
*
* @param string $name
* @return bool
*
* @throws \LogicException
*/
public function createDatabase($name)
{
throw new LogicException('This database driver does not support creating databases.');
}
/**
* Drop a database from the schema if the database exists.
*
* @param string $name
* @return bool
*
* @throws \LogicException
*/
public function dropDatabaseIfExists($name)
{
throw new LogicException('This database driver does not support dropping databases.');
}
/**
* Determine if the given table exists.
*
* @param string $table
* @return bool
*/
public function hasTable($table)
{
$table = $this->connection->getTablePrefix().$table;
foreach ($this->getTables() as $value) {
if (strtolower($table) === strtolower($value['name'])) {
return true;
}
}
return false;
}
/**
* Determine if the given view exists.
*
* @param string $view
* @return bool
*/
public function hasView($view)
{
$view = $this->connection->getTablePrefix().$view;
foreach ($this->getViews() as $value) {
if (strtolower($view) === strtolower($value['name'])) {
return true;
}
}
return false;
}
/**
* Get the tables that belong to the database.
*
* @return array
*/
public function getTables()
{
return $this->connection->getPostProcessor()->processTables(
$this->connection->selectFromWriteConnection($this->grammar->compileTables())
);
}
/**
* Get the names of the tables that belong to the database.
*
* @return array
*/
public function getTableListing()
{
return array_column($this->getTables(), 'name');
}
/**
* Get the views that belong to the database.
*
* @return array
*/
public function getViews()
{
return $this->connection->getPostProcessor()->processViews(
$this->connection->selectFromWriteConnection($this->grammar->compileViews())
);
}
/**
* Get the user-defined types that belong to the database.
*
* @return array
*/
public function getTypes()
{
throw new LogicException('This database driver does not support user-defined types.');
}
/**
* Determine if the given table has a given column.
*
* @param string $table
* @param string $column
* @return bool
*/
public function hasColumn($table, $column)
{
return in_array(
strtolower($column), array_map('strtolower', $this->getColumnListing($table))
);
}
/**
* Determine if the given table has given columns.
*
* @param string $table
* @param array $columns
* @return bool
*/
public function hasColumns($table, array $columns)
{
$tableColumns = array_map('strtolower', $this->getColumnListing($table));
foreach ($columns as $column) {
if (! in_array(strtolower($column), $tableColumns)) {
return false;
}
}
return true;
}
/**
* Execute a table builder callback if the given table has a given column.
*
* @param string $table
* @param string $column
* @param \Closure $callback
* @return void
*/
public function whenTableHasColumn(string $table, string $column, Closure $callback)
{
if ($this->hasColumn($table, $column)) {
$this->table($table, fn (Blueprint $table) => $callback($table));
}
}
/**
* Execute a table builder callback if the given table doesn't have a given column.
*
* @param string $table
* @param string $column
* @param \Closure $callback
* @return void
*/
public function whenTableDoesntHaveColumn(string $table, string $column, Closure $callback)
{
if (! $this->hasColumn($table, $column)) {
$this->table($table, fn (Blueprint $table) => $callback($table));
}
}
/**
* Get the data type for the given column name.
*
* @param string $table
* @param string $column
* @param bool $fullDefinition
* @return string
*/
public function getColumnType($table, $column, $fullDefinition = false)
{
$columns = $this->getColumns($table);
foreach ($columns as $value) {
if (strtolower($value['name']) === strtolower($column)) {
return $fullDefinition ? $value['type'] : $value['type_name'];
}
}
throw new InvalidArgumentException("There is no column with name '$column' on table '$table'.");
}
/**
* Get the column listing for a given table.
*
* @param string $table
* @return array
*/
public function getColumnListing($table)
{
return array_column($this->getColumns($table), 'name');
}
/**
* Get the columns for a given table.
*
* @param string $table
* @return array
*/
public function getColumns($table)
{
$table = $this->connection->getTablePrefix().$table;
return $this->connection->getPostProcessor()->processColumns(
$this->connection->selectFromWriteConnection($this->grammar->compileColumns($table))
);
}
/**
* Get the indexes for a given table.
*
* @param string $table
* @return array
*/
public function getIndexes($table)
{
$table = $this->connection->getTablePrefix().$table;
return $this->connection->getPostProcessor()->processIndexes(
$this->connection->selectFromWriteConnection($this->grammar->compileIndexes($table))
);
}
/**
* Get the names of the indexes for a given table.
*
* @param string $table
* @return array
*/
public function getIndexListing($table)
{
return array_column($this->getIndexes($table), 'name');
}
/**
* Determine if the given table has a given index.
*
* @param string $table
* @param string|array $index
* @param string|null $type
* @return bool
*/
public function hasIndex($table, $index, $type = null)
{
$type = is_null($type) ? $type : strtolower($type);
foreach ($this->getIndexes($table) as $value) {
$typeMatches = is_null($type)
|| ($type === 'primary' && $value['primary'])
|| ($type === 'unique' && $value['unique'])
|| $type === $value['type'];
if (($value['name'] === $index || $value['columns'] === $index) && $typeMatches) {
return true;
}
}
return false;
}
/**
* Get the foreign keys for a given table.
*
* @param string $table
* @return array
*/
public function getForeignKeys($table)
{
$table = $this->connection->getTablePrefix().$table;
return $this->connection->getPostProcessor()->processForeignKeys(
$this->connection->selectFromWriteConnection($this->grammar->compileForeignKeys($table))
);
}
/**
* Modify a table on the schema.
*
* @param string $table
* @param \Closure $callback
* @return void
*/
public function table($table, Closure $callback)
{
$this->build($this->createBlueprint($table, $callback));
}
/**
* Create a new table on the schema.
*
* @param string $table
* @param \Closure $callback
* @return void
*/
public function create($table, Closure $callback)
{
$this->build(tap($this->createBlueprint($table), function ($blueprint) use ($callback) {
$blueprint->create();
$callback($blueprint);
}));
}
/**
* Drop a table from the schema.
*
* @param string $table
* @return void
*/
public function drop($table)
{
$this->build(tap($this->createBlueprint($table), function ($blueprint) {
$blueprint->drop();
}));
}
/**
* Drop a table from the schema if it exists.
*
* @param string $table
* @return void
*/
public function dropIfExists($table)
{
$this->build(tap($this->createBlueprint($table), function ($blueprint) {
$blueprint->dropIfExists();
}));
}
/**
* Drop columns from a table schema.
*
* @param string $table
* @param string|array $columns
* @return void
*/
public function dropColumns($table, $columns)
{
$this->table($table, function (Blueprint $blueprint) use ($columns) {
$blueprint->dropColumn($columns);
});
}
/**
* Drop all tables from the database.
*
* @return void
*
* @throws \LogicException
*/
public function dropAllTables()
{
throw new LogicException('This database driver does not support dropping all tables.');
}
/**
* Drop all views from the database.
*
* @return void
*
* @throws \LogicException
*/
public function dropAllViews()
{
throw new LogicException('This database driver does not support dropping all views.');
}
/**
* Drop all types from the database.
*
* @return void
*
* @throws \LogicException
*/
public function dropAllTypes()
{
throw new LogicException('This database driver does not support dropping all types.');
}
/**
* Rename a table on the schema.
*
* @param string $from
* @param string $to
* @return void
*/
public function rename($from, $to)
{
$this->build(tap($this->createBlueprint($from), function ($blueprint) use ($to) {
$blueprint->rename($to);
}));
}
/**
* Enable foreign key constraints.
*
* @return bool
*/
public function enableForeignKeyConstraints()
{
return $this->connection->statement(
$this->grammar->compileEnableForeignKeyConstraints()
);
}
/**
* Disable foreign key constraints.
*
* @return bool
*/
public function disableForeignKeyConstraints()
{
return $this->connection->statement(
$this->grammar->compileDisableForeignKeyConstraints()
);
}
/**
* Disable foreign key constraints during the execution of a callback.
*
* @param \Closure $callback
* @return mixed
*/
public function withoutForeignKeyConstraints(Closure $callback)
{
$this->disableForeignKeyConstraints();
try {
return $callback();
} finally {
$this->enableForeignKeyConstraints();
}
}
/**
* Execute the blueprint to build / modify the table.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @return void
*/
protected function build(Blueprint $blueprint)
{
$blueprint->build($this->connection, $this->grammar);
}
/**
* Create a new command set with a Closure.
*
* @param string $table
* @param \Closure|null $callback
* @return \Illuminate\Database\Schema\Blueprint
*/
protected function createBlueprint($table, ?Closure $callback = null)
{
$prefix = $this->connection->getConfig('prefix_indexes')
? $this->connection->getConfig('prefix')
: '';
if (isset($this->resolver)) {
return call_user_func($this->resolver, $table, $callback, $prefix);
}
return Container::getInstance()->make(Blueprint::class, compact('table', 'callback', 'prefix'));
}
/**
* Get the database connection instance.
*
* @return \Illuminate\Database\Connection
*/
public function getConnection()
{
return $this->connection;
}
/**
* Set the database connection instance.
*
* @param \Illuminate\Database\Connection $connection
* @return $this
*/
public function setConnection(Connection $connection)
{
$this->connection = $connection;
return $this;
}
/**
* Set the Schema Blueprint resolver callback.
*
* @param \Closure $resolver
* @return void
*/
public function blueprintResolver(Closure $resolver)
{
$this->resolver = $resolver;
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace Illuminate\Database\Schema;
use Illuminate\Support\Fluent;
/**
* @method $this after(string $column) Place the column "after" another column (MySQL)
* @method $this always(bool $value = true) Used as a modifier for generatedAs() (PostgreSQL)
* @method $this autoIncrement() Set INTEGER columns as auto-increment (primary key)
* @method $this change() Change the column
* @method $this charset(string $charset) Specify a character set for the column (MySQL)
* @method $this collation(string $collation) Specify a collation for the column
* @method $this comment(string $comment) Add a comment to the column (MySQL/PostgreSQL)
* @method $this default(mixed $value) Specify a "default" value for the column
* @method $this first() Place the column "first" in the table (MySQL)
* @method $this from(int $startingValue) Set the starting value of an auto-incrementing field (MySQL / PostgreSQL)
* @method $this generatedAs(string|\Illuminate\Contracts\Database\Query\Expression $expression = null) Create a SQL compliant identity column (PostgreSQL)
* @method $this index(bool|string $indexName = null) Add an index
* @method $this invisible() Specify that the column should be invisible to "SELECT *" (MySQL)
* @method $this nullable(bool $value = true) Allow NULL values to be inserted into the column
* @method $this persisted() Mark the computed generated column as persistent (SQL Server)
* @method $this primary(bool $value = true) Add a primary index
* @method $this fulltext(bool|string $indexName = null) Add a fulltext index
* @method $this spatialIndex(bool|string $indexName = null) Add a spatial index
* @method $this startingValue(int $startingValue) Set the starting value of an auto-incrementing field (MySQL/PostgreSQL)
* @method $this storedAs(string|\Illuminate\Contracts\Database\Query\Expression $expression) Create a stored generated column (MySQL/PostgreSQL/SQLite)
* @method $this type(string $type) Specify a type for the column
* @method $this unique(bool|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 useCurrentOnUpdate() Set the TIMESTAMP column to use CURRENT_TIMESTAMP when updating (MySQL)
* @method $this virtualAs(string|\Illuminate\Contracts\Database\Query\Expression $expression) Create a virtual generated column (MySQL/PostgreSQL/SQLite)
*/
class ColumnDefinition extends Fluent
{
//
}
@@ -0,0 +1,57 @@
<?php
namespace Illuminate\Database\Schema;
use Illuminate\Support\Stringable;
class ForeignIdColumnDefinition extends ColumnDefinition
{
/**
* The schema builder blueprint instance.
*
* @var \Illuminate\Database\Schema\Blueprint
*/
protected $blueprint;
/**
* Create a new foreign ID column definition.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param array $attributes
* @return void
*/
public function __construct(Blueprint $blueprint, $attributes = [])
{
parent::__construct($attributes);
$this->blueprint = $blueprint;
}
/**
* Create a foreign key constraint on this column referencing the "id" column of the conventionally related table.
*
* @param string|null $table
* @param string|null $column
* @param string|null $indexName
* @return \Illuminate\Database\Schema\ForeignKeyDefinition
*/
public function constrained($table = null, $column = null, $indexName = null)
{
$table ??= $this->table;
$column ??= $this->referencesModelColumn ?? 'id';
return $this->references($column, $indexName)->on($table ?? (new Stringable($this->name))->beforeLast('_'.$column)->plural());
}
/**
* Specify which column this foreign ID references on another table.
*
* @param string $column
* @param string $indexName
* @return \Illuminate\Database\Schema\ForeignKeyDefinition
*/
public function references($column, $indexName = null)
{
return $this->blueprint->foreign($this->name, $indexName)->references($column);
}
}
@@ -0,0 +1,96 @@
<?php
namespace Illuminate\Database\Schema;
use Illuminate\Support\Fluent;
/**
* @method ForeignKeyDefinition deferrable(bool $value = true) Set the foreign key as deferrable (PostgreSQL)
* @method ForeignKeyDefinition initiallyImmediate(bool $value = true) Set the default time to check the constraint (PostgreSQL)
* @method ForeignKeyDefinition on(string $table) Specify the referenced table
* @method ForeignKeyDefinition onDelete(string $action) Add an ON DELETE action
* @method ForeignKeyDefinition onUpdate(string $action) Add an ON UPDATE action
* @method ForeignKeyDefinition references(string|array $columns) Specify the referenced column(s)
*/
class ForeignKeyDefinition extends Fluent
{
/**
* Indicate that updates should cascade.
*
* @return $this
*/
public function cascadeOnUpdate()
{
return $this->onUpdate('cascade');
}
/**
* Indicate that updates should be restricted.
*
* @return $this
*/
public function restrictOnUpdate()
{
return $this->onUpdate('restrict');
}
/**
* Indicate that updates should set the foreign key value to null.
*
* @return $this
*/
public function nullOnUpdate()
{
return $this->onUpdate('set null');
}
/**
* Indicate that updates should have "no action".
*
* @return $this
*/
public function noActionOnUpdate()
{
return $this->onUpdate('no action');
}
/**
* Indicate that deletes should cascade.
*
* @return $this
*/
public function cascadeOnDelete()
{
return $this->onDelete('cascade');
}
/**
* Indicate that deletes should be restricted.
*
* @return $this
*/
public function restrictOnDelete()
{
return $this->onDelete('restrict');
}
/**
* Indicate that deletes should set the foreign key value to null.
*
* @return $this
*/
public function nullOnDelete()
{
return $this->onDelete('set null');
}
/**
* Indicate that deletes should have "no action".
*
* @return $this
*/
public function noActionOnDelete()
{
return $this->onDelete('no action');
}
}
+408
View File
@@ -0,0 +1,408 @@
<?php
namespace Illuminate\Database\Schema\Grammars;
use BackedEnum;
use Illuminate\Contracts\Database\Query\Expression;
use Illuminate\Database\Concerns\CompilesJsonPaths;
use Illuminate\Database\Connection;
use Illuminate\Database\Grammar as BaseGrammar;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Fluent;
use LogicException;
use RuntimeException;
abstract class Grammar extends BaseGrammar
{
use CompilesJsonPaths;
/**
* The possible column modifiers.
*
* @var string[]
*/
protected $modifiers = [];
/**
* If this Grammar supports schema changes wrapped in a transaction.
*
* @var bool
*/
protected $transactions = false;
/**
* The commands to be executed outside of create or alter command.
*
* @var array
*/
protected $fluentCommands = [];
/**
* Compile a create database command.
*
* @param string $name
* @param \Illuminate\Database\Connection $connection
* @return void
*
* @throws \LogicException
*/
public function compileCreateDatabase($name, $connection)
{
throw new LogicException('This database driver does not support creating databases.');
}
/**
* Compile a drop database if exists command.
*
* @param string $name
* @return void
*
* @throws \LogicException
*/
public function compileDropDatabaseIfExists($name)
{
throw new LogicException('This database driver does not support dropping databases.');
}
/**
* Compile a rename column command.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $command
* @param \Illuminate\Database\Connection $connection
* @return array|string
*/
public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Connection $connection)
{
return sprintf('alter table %s rename column %s to %s',
$this->wrapTable($blueprint),
$this->wrap($command->from),
$this->wrap($command->to)
);
}
/**
* 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)
{
throw new LogicException('This database driver does not support modifying columns.');
}
/**
* Compile a fulltext index key command.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $command
* @return string
*
* @throws \RuntimeException
*/
public function compileFulltext(Blueprint $blueprint, Fluent $command)
{
throw new RuntimeException('This database driver does not support fulltext index creation.');
}
/**
* Compile a drop fulltext index command.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $command
* @return string
*
* @throws \RuntimeException
*/
public function compileDropFullText(Blueprint $blueprint, Fluent $command)
{
throw new RuntimeException('This database driver does not support fulltext index removal.');
}
/**
* Compile a foreign key command.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $command
* @return string
*/
public function compileForeign(Blueprint $blueprint, Fluent $command)
{
// We need to prepare several of the elements of the foreign key definition
// before we can create the SQL, such as wrapping the tables and convert
// an array of columns to comma-delimited strings for the SQL queries.
$sql = sprintf('alter table %s add constraint %s ',
$this->wrapTable($blueprint),
$this->wrap($command->index)
);
// Once we have the initial portion of the SQL statement we will add on the
// key name, table name, and referenced columns. These will complete the
// main portion of the SQL statement and this SQL will almost be done.
$sql .= sprintf('foreign key (%s) references %s (%s)',
$this->columnize($command->columns),
$this->wrapTable($command->on),
$this->columnize((array) $command->references)
);
// Once we have the basic foreign key creation statement constructed we can
// build out the syntax for what should happen on an update or delete of
// the affected columns, which will get something like "cascade", etc.
if (! is_null($command->onDelete)) {
$sql .= " on delete {$command->onDelete}";
}
if (! is_null($command->onUpdate)) {
$sql .= " on update {$command->onUpdate}";
}
return $sql;
}
/**
* Compile a drop foreign key command.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $command
* @return string
*/
public function compileDropForeign(Blueprint $blueprint, Fluent $command)
{
throw new RuntimeException('This database driver does not support dropping foreign keys.');
}
/**
* Compile the blueprint's added column definitions.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @return array
*/
protected function getColumns(Blueprint $blueprint)
{
$columns = [];
foreach ($blueprint->getAddedColumns() as $column) {
$columns[] = $this->getColumn($blueprint, $column);
}
return $columns;
}
/**
* Compile the column definition.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Database\Schema\ColumnDefinition $column
* @return string
*/
protected function getColumn(Blueprint $blueprint, $column)
{
// Each of the column types has their own compiler functions, which are tasked
// with turning the column definition into its SQL format for this platform
// used by the connection. The column's modifiers are compiled and added.
$sql = $this->wrap($column).' '.$this->getType($column);
return $this->addModifiers($sql, $blueprint, $column);
}
/**
* Get the SQL for the column data type.
*
* @param \Illuminate\Support\Fluent $column
* @return string
*/
protected function getType(Fluent $column)
{
return $this->{'type'.ucfirst($column->type)}($column);
}
/**
* 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 does not support the computed type.');
}
/**
* Create the column definition for a vector type.
*
* @param \Illuminate\Support\Fluent $column
* @return string
*
* @throws \RuntimeException
*/
protected function typeVector(Fluent $column)
{
throw new RuntimeException('This database driver does not support the vector type.');
}
/**
* Create the column definition for a raw column type.
*
* @param \Illuminate\Support\Fluent $column
* @return string
*/
protected function typeRaw(Fluent $column)
{
return $column->offsetGet('definition');
}
/**
* Add the column modifiers to the definition.
*
* @param string $sql
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $column
* @return string
*/
protected function addModifiers($sql, Blueprint $blueprint, Fluent $column)
{
foreach ($this->modifiers as $modifier) {
if (method_exists($this, $method = "modify{$modifier}")) {
$sql .= $this->{$method}($blueprint, $column);
}
}
return $sql;
}
/**
* Get the command with a given name if it exists on the blueprint.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param string $name
* @return \Illuminate\Support\Fluent|null
*/
protected function getCommandByName(Blueprint $blueprint, $name)
{
$commands = $this->getCommandsByName($blueprint, $name);
if (count($commands) > 0) {
return reset($commands);
}
}
/**
* Get all of the commands with a given name.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param string $name
* @return array
*/
protected function getCommandsByName(Blueprint $blueprint, $name)
{
return array_filter($blueprint->getCommands(), function ($value) use ($name) {
return $value->name == $name;
});
}
/*
* Determine if a command with a given name exists on the blueprint.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param string $name
* @return bool
*/
protected function hasCommand(Blueprint $blueprint, $name)
{
foreach ($blueprint->getCommands() as $command) {
if ($command->name === $name) {
return true;
}
}
return false;
}
/**
* Add a prefix to an array of values.
*
* @param string $prefix
* @param array $values
* @return array
*/
public function prefixArray($prefix, array $values)
{
return array_map(function ($value) use ($prefix) {
return $prefix.' '.$value;
}, $values);
}
/**
* Wrap a table in keyword identifiers.
*
* @param mixed $table
* @return string
*/
public function wrapTable($table)
{
return parent::wrapTable(
$table instanceof Blueprint ? $table->getTable() : $table
);
}
/**
* Wrap a value in keyword identifiers.
*
* @param \Illuminate\Support\Fluent|\Illuminate\Contracts\Database\Query\Expression|string $value
* @return string
*/
public function wrap($value)
{
return parent::wrap(
$value instanceof Fluent ? $value->name : $value,
);
}
/**
* Format a value so that it can be used in "default" clauses.
*
* @param mixed $value
* @return string
*/
protected function getDefaultValue($value)
{
if ($value instanceof Expression) {
return $this->getValue($value);
}
if ($value instanceof BackedEnum) {
return "'{$value->value}'";
}
return is_bool($value)
? "'".(int) $value."'"
: "'".(string) $value."'";
}
/**
* Get the fluent commands for the grammar.
*
* @return array
*/
public function getFluentCommands()
{
return $this->fluentCommands;
}
/**
* Check if this Grammar supports schema changes wrapped in a transaction.
*
* @return bool
*/
public function supportsSchemaTransactions()
{
return $this->transactions;
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace Illuminate\Database\Schema\Grammars;
use Illuminate\Database\Connection;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Fluent;
class MariaDbGrammar extends MySqlGrammar
{
/**
* Compile a rename column command.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $command
* @param \Illuminate\Database\Connection $connection
* @return array|string
*/
public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Connection $connection)
{
if (version_compare($connection->getServerVersion(), '10.5.2', '<')) {
return $this->compileLegacyRenameColumn($blueprint, $command, $connection);
}
return parent::compileRenameColumn($blueprint, $command, $connection);
}
/**
* Create the column definition for a uuid type.
*
* @param \Illuminate\Support\Fluent $column
* @return string
*/
protected function typeUuid(Fluent $column)
{
return 'uuid';
}
/**
* Create the column definition for a spatial Geometry type.
*
* @param \Illuminate\Support\Fluent $column
* @return string
*/
protected function typeGeometry(Fluent $column)
{
$subtype = $column->subtype ? strtolower($column->subtype) : null;
if (! in_array($subtype, ['point', 'linestring', 'polygon', 'geometrycollection', 'multipoint', 'multilinestring', 'multipolygon'])) {
$subtype = null;
}
return sprintf('%s%s',
$subtype ?? 'geometry',
$column->srid ? ' ref_system_id='.$column->srid : ''
);
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+16
View File
@@ -0,0 +1,16 @@
<?php
namespace Illuminate\Database\Schema;
use Illuminate\Support\Fluent;
/**
* @method $this algorithm(string $algorithm) Specify an algorithm for the index (MySQL/PostgreSQL)
* @method $this language(string $language) Specify a language for the full text index (PostgreSQL)
* @method $this deferrable(bool $value = true) Specify that the unique index is deferrable (PostgreSQL)
* @method $this initiallyImmediate(bool $value = true) Specify the default time to check the unique index constraint (PostgreSQL)
*/
class IndexDefinition extends Fluent
{
//
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Illuminate\Database\Schema;
class MariaDbBuilder extends MySqlBuilder
{
//
}
@@ -0,0 +1,18 @@
<?php
namespace Illuminate\Database\Schema;
class MariaDbSchemaState extends MySqlSchemaState
{
/**
* Get the base dump command arguments for MariaDB as a string.
*
* @return string
*/
protected function baseDumpCommand()
{
$command = 'mysqldump '.$this->connectionString().' --no-tablespaces --skip-add-locks --skip-comments --skip-set-charset --tz-utc --column-statistics=0';
return $command.' "${:LARAVEL_LOAD_DATABASE}"';
}
}
+168
View File
@@ -0,0 +1,168 @@
<?php
namespace Illuminate\Database\Schema;
class MySqlBuilder extends Builder
{
/**
* Create a database in the schema.
*
* @param string $name
* @return bool
*/
public function createDatabase($name)
{
return $this->connection->statement(
$this->grammar->compileCreateDatabase($name, $this->connection)
);
}
/**
* Drop a database from the schema if the database exists.
*
* @param string $name
* @return bool
*/
public function dropDatabaseIfExists($name)
{
return $this->connection->statement(
$this->grammar->compileDropDatabaseIfExists($name)
);
}
/**
* Determine if the given table exists.
*
* @param string $table
* @return bool
*/
public function hasTable($table)
{
$table = $this->connection->getTablePrefix().$table;
$database = $this->connection->getDatabaseName();
return (bool) $this->connection->scalar(
$this->grammar->compileTableExists($database, $table)
);
}
/**
* Get the tables for the database.
*
* @return array
*/
public function getTables()
{
return $this->connection->getPostProcessor()->processTables(
$this->connection->selectFromWriteConnection(
$this->grammar->compileTables($this->connection->getDatabaseName())
)
);
}
/**
* Get the views for the database.
*
* @return array
*/
public function getViews()
{
return $this->connection->getPostProcessor()->processViews(
$this->connection->selectFromWriteConnection(
$this->grammar->compileViews($this->connection->getDatabaseName())
)
);
}
/**
* Get the columns for a given table.
*
* @param string $table
* @return array
*/
public function getColumns($table)
{
$table = $this->connection->getTablePrefix().$table;
$results = $this->connection->selectFromWriteConnection(
$this->grammar->compileColumns($this->connection->getDatabaseName(), $table)
);
return $this->connection->getPostProcessor()->processColumns($results);
}
/**
* Get the indexes for a given table.
*
* @param string $table
* @return array
*/
public function getIndexes($table)
{
$table = $this->connection->getTablePrefix().$table;
return $this->connection->getPostProcessor()->processIndexes(
$this->connection->selectFromWriteConnection(
$this->grammar->compileIndexes($this->connection->getDatabaseName(), $table)
)
);
}
/**
* Get the foreign keys for a given table.
*
* @param string $table
* @return array
*/
public function getForeignKeys($table)
{
$table = $this->connection->getTablePrefix().$table;
return $this->connection->getPostProcessor()->processForeignKeys(
$this->connection->selectFromWriteConnection(
$this->grammar->compileForeignKeys($this->connection->getDatabaseName(), $table)
)
);
}
/**
* Drop all tables from the database.
*
* @return void
*/
public function dropAllTables()
{
$tables = array_column($this->getTables(), 'name');
if (empty($tables)) {
return;
}
$this->disableForeignKeyConstraints();
$this->connection->statement(
$this->grammar->compileDropAllTables($tables)
);
$this->enableForeignKeyConstraints();
}
/**
* Drop all views from the database.
*
* @return void
*/
public function dropAllViews()
{
$views = array_column($this->getViews(), 'name');
if (empty($views)) {
return;
}
$this->connection->statement(
$this->grammar->compileDropAllViews($views)
);
}
}
+177
View File
@@ -0,0 +1,177 @@
<?php
namespace Illuminate\Database\Schema;
use Exception;
use Illuminate\Database\Connection;
use Illuminate\Support\Str;
use Symfony\Component\Process\Process;
class MySqlSchemaState extends SchemaState
{
/**
* Dump the database's schema into a file.
*
* @param \Illuminate\Database\Connection $connection
* @param string $path
* @return void
*/
public function dump(Connection $connection, $path)
{
$this->executeDumpProcess($this->makeProcess(
$this->baseDumpCommand().' --routines --result-file="${:LARAVEL_LOAD_PATH}" --no-data'
), $this->output, array_merge($this->baseVariables($this->connection->getConfig()), [
'LARAVEL_LOAD_PATH' => $path,
]));
$this->removeAutoIncrementingState($path);
if ($this->hasMigrationTable()) {
$this->appendMigrationData($path);
}
}
/**
* Remove the auto-incrementing state from the given schema dump.
*
* @param string $path
* @return void
*/
protected function removeAutoIncrementingState(string $path)
{
$this->files->put($path, preg_replace(
'/\s+AUTO_INCREMENT=[0-9]+/iu',
'',
$this->files->get($path)
));
}
/**
* Append the migration data to the schema dump.
*
* @param string $path
* @return void
*/
protected function appendMigrationData(string $path)
{
$process = $this->executeDumpProcess($this->makeProcess(
$this->baseDumpCommand().' '.$this->getMigrationTable().' --no-create-info --skip-extended-insert --skip-routines --compact --complete-insert'
), null, array_merge($this->baseVariables($this->connection->getConfig()), [
//
]));
$this->files->append($path, $process->getOutput());
}
/**
* Load the given schema file into the database.
*
* @param string $path
* @return void
*/
public function load($path)
{
$command = 'mysql '.$this->connectionString().' --database="${:LARAVEL_LOAD_DATABASE}" < "${:LARAVEL_LOAD_PATH}"';
$process = $this->makeProcess($command)->setTimeout(null);
$process->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [
'LARAVEL_LOAD_PATH' => $path,
]));
}
/**
* Get the base dump command arguments for MySQL as a string.
*
* @return string
*/
protected function baseDumpCommand()
{
$command = 'mysqldump '.$this->connectionString().' --no-tablespaces --skip-add-locks --skip-comments --skip-set-charset --tz-utc --column-statistics=0';
if (! $this->connection->isMaria()) {
$command .= ' --set-gtid-purged=OFF';
}
return $command.' "${:LARAVEL_LOAD_DATABASE}"';
}
/**
* Generate a basic connection string (--socket, --host, --port, --user, --password) for the database.
*
* @return string
*/
protected function connectionString()
{
$value = ' --user="${:LARAVEL_LOAD_USER}" --password="${:LARAVEL_LOAD_PASSWORD}"';
$config = $this->connection->getConfig();
$value .= $config['unix_socket'] ?? false
? ' --socket="${:LARAVEL_LOAD_SOCKET}"'
: ' --host="${:LARAVEL_LOAD_HOST}" --port="${:LARAVEL_LOAD_PORT}"';
if (isset($config['options'][\PDO::MYSQL_ATTR_SSL_CA])) {
$value .= ' --ssl-ca="${:LARAVEL_LOAD_SSL_CA}"';
}
return $value;
}
/**
* Get the base variables for a dump / load command.
*
* @param array $config
* @return array
*/
protected function baseVariables(array $config)
{
$config['host'] ??= '';
return [
'LARAVEL_LOAD_SOCKET' => $config['unix_socket'] ?? '',
'LARAVEL_LOAD_HOST' => is_array($config['host']) ? $config['host'][0] : $config['host'],
'LARAVEL_LOAD_PORT' => $config['port'] ?? '',
'LARAVEL_LOAD_USER' => $config['username'],
'LARAVEL_LOAD_PASSWORD' => $config['password'] ?? '',
'LARAVEL_LOAD_DATABASE' => $config['database'],
'LARAVEL_LOAD_SSL_CA' => $config['options'][\PDO::MYSQL_ATTR_SSL_CA] ?? '',
];
}
/**
* Execute the given dump process.
*
* @param \Symfony\Component\Process\Process $process
* @param callable $output
* @param array $variables
* @param int $depth
* @return \Symfony\Component\Process\Process
*/
protected function executeDumpProcess(Process $process, $output, array $variables, int $depth = 0)
{
if ($depth > 30) {
throw new Exception('Dump execution exceeded maximum depth of 30.');
}
try {
$process->setTimeout(null)->mustRun($output, $variables);
} catch (Exception $e) {
if (Str::contains($e->getMessage(), ['column-statistics', 'column_statistics'])) {
return $this->executeDumpProcess(Process::fromShellCommandLine(
str_replace(' --column-statistics=0', '', $process->getCommandLine())
), $output, $variables, $depth + 1);
}
if (str_contains($e->getMessage(), 'set-gtid-purged')) {
return $this->executeDumpProcess(Process::fromShellCommandLine(
str_replace(' --set-gtid-purged=OFF', '', $process->getCommandLine())
), $output, $variables, $depth + 1);
}
throw $e;
}
return $process;
}
}
+287
View File
@@ -0,0 +1,287 @@
<?php
namespace Illuminate\Database\Schema;
use Illuminate\Database\Concerns\ParsesSearchPath;
use InvalidArgumentException;
class PostgresBuilder extends Builder
{
use ParsesSearchPath {
parseSearchPath as baseParseSearchPath;
}
/**
* Create a database in the schema.
*
* @param string $name
* @return bool
*/
public function createDatabase($name)
{
return $this->connection->statement(
$this->grammar->compileCreateDatabase($name, $this->connection)
);
}
/**
* Drop a database from the schema if the database exists.
*
* @param string $name
* @return bool
*/
public function dropDatabaseIfExists($name)
{
return $this->connection->statement(
$this->grammar->compileDropDatabaseIfExists($name)
);
}
/**
* Determine if the given table exists.
*
* @param string $table
* @return bool
*/
public function hasTable($table)
{
[$schema, $table] = $this->parseSchemaAndTable($table);
$table = $this->connection->getTablePrefix().$table;
return (bool) $this->connection->scalar(
$this->grammar->compileTableExists($schema, $table)
);
}
/**
* Determine if the given view exists.
*
* @param string $view
* @return bool
*/
public function hasView($view)
{
[$schema, $view] = $this->parseSchemaAndTable($view);
$view = $this->connection->getTablePrefix().$view;
foreach ($this->getViews() as $value) {
if (strtolower($view) === strtolower($value['name'])
&& strtolower($schema) === strtolower($value['schema'])) {
return true;
}
}
return false;
}
/**
* Get the user-defined types that belong to the database.
*
* @return array
*/
public function getTypes()
{
return $this->connection->getPostProcessor()->processTypes(
$this->connection->selectFromWriteConnection($this->grammar->compileTypes())
);
}
/**
* Drop all tables from the database.
*
* @return void
*/
public function dropAllTables()
{
$tables = [];
$excludedTables = $this->connection->getConfig('dont_drop') ?? ['spatial_ref_sys'];
$schemas = $this->getSchemas();
foreach ($this->getTables() as $table) {
$qualifiedName = $table['schema'].'.'.$table['name'];
if (in_array($table['schema'], $schemas) &&
empty(array_intersect([$table['name'], $qualifiedName], $excludedTables))) {
$tables[] = $qualifiedName;
}
}
if (empty($tables)) {
return;
}
$this->connection->statement(
$this->grammar->compileDropAllTables($tables)
);
}
/**
* Drop all views from the database.
*
* @return void
*/
public function dropAllViews()
{
$views = [];
$schemas = $this->getSchemas();
foreach ($this->getViews() as $view) {
if (in_array($view['schema'], $schemas)) {
$views[] = $view['schema'].'.'.$view['name'];
}
}
if (empty($views)) {
return;
}
$this->connection->statement(
$this->grammar->compileDropAllViews($views)
);
}
/**
* Drop all types from the database.
*
* @return void
*/
public function dropAllTypes()
{
$types = [];
$domains = [];
$schemas = $this->getSchemas();
foreach ($this->getTypes() as $type) {
if (! $type['implicit'] && in_array($type['schema'], $schemas)) {
if ($type['type'] === 'domain') {
$domains[] = $type['schema'].'.'.$type['name'];
} else {
$types[] = $type['schema'].'.'.$type['name'];
}
}
}
if (! empty($types)) {
$this->connection->statement($this->grammar->compileDropAllTypes($types));
}
if (! empty($domains)) {
$this->connection->statement($this->grammar->compileDropAllDomains($domains));
}
}
/**
* Get the columns for a given table.
*
* @param string $table
* @return array
*/
public function getColumns($table)
{
[$schema, $table] = $this->parseSchemaAndTable($table);
$table = $this->connection->getTablePrefix().$table;
$results = $this->connection->selectFromWriteConnection(
$this->grammar->compileColumns($schema, $table)
);
return $this->connection->getPostProcessor()->processColumns($results);
}
/**
* Get the indexes for a given table.
*
* @param string $table
* @return array
*/
public function getIndexes($table)
{
[$schema, $table] = $this->parseSchemaAndTable($table);
$table = $this->connection->getTablePrefix().$table;
return $this->connection->getPostProcessor()->processIndexes(
$this->connection->selectFromWriteConnection($this->grammar->compileIndexes($schema, $table))
);
}
/**
* Get the foreign keys for a given table.
*
* @param string $table
* @return array
*/
public function getForeignKeys($table)
{
[$schema, $table] = $this->parseSchemaAndTable($table);
$table = $this->connection->getTablePrefix().$table;
return $this->connection->getPostProcessor()->processForeignKeys(
$this->connection->selectFromWriteConnection($this->grammar->compileForeignKeys($schema, $table))
);
}
/**
* Get the schemas for the connection.
*
* @return array
*/
public function getSchemas()
{
return $this->parseSearchPath(
$this->connection->getConfig('search_path') ?: $this->connection->getConfig('schema') ?: 'public'
);
}
/**
* Parse the database object reference and extract the schema and table.
*
* @param string $reference
* @return array
*/
public function parseSchemaAndTable($reference)
{
$parts = explode('.', $reference);
if (count($parts) > 2) {
$database = $parts[0];
throw new InvalidArgumentException("Using three-part reference is not supported, you may use `Schema::connection('$database')` instead.");
}
// We will use the default schema unless the schema has been specified in the
// query. If the schema has been specified in the query then we can use it
// instead of a default schema configured in the connection search path.
$schema = $this->getSchemas()[0];
if (count($parts) === 2) {
$schema = $parts[0];
array_shift($parts);
}
return [$schema, $parts[0]];
}
/**
* Parse the "search_path" configuration value into an array.
*
* @param string|array|null $searchPath
* @return array
*/
protected function parseSearchPath($searchPath)
{
return array_map(function ($schema) {
return $schema === '$user'
? $this->connection->getConfig('username')
: $schema;
}, $this->baseParseSearchPath($searchPath));
}
}
@@ -0,0 +1,95 @@
<?php
namespace Illuminate\Database\Schema;
use Illuminate\Database\Connection;
use Illuminate\Support\Collection;
class PostgresSchemaState extends SchemaState
{
/**
* Dump the database's schema into a file.
*
* @param \Illuminate\Database\Connection $connection
* @param string $path
* @return void
*/
public function dump(Connection $connection, $path)
{
$commands = new Collection([
$this->baseDumpCommand().' --schema-only > '.$path,
]);
if ($this->hasMigrationTable()) {
$commands->push($this->baseDumpCommand().' -t '.$this->getMigrationTable().' --data-only >> '.$path);
}
$commands->map(function ($command, $path) {
$this->makeProcess($command)->mustRun($this->output, array_merge($this->baseVariables($this->connection->getConfig()), [
'LARAVEL_LOAD_PATH' => $path,
]));
});
}
/**
* Load the given schema file into the database.
*
* @param string $path
* @return void
*/
public function load($path)
{
$command = 'pg_restore --no-owner --no-acl --clean --if-exists --host="${:LARAVEL_LOAD_HOST}" --port="${:LARAVEL_LOAD_PORT}" --username="${:LARAVEL_LOAD_USER}" --dbname="${:LARAVEL_LOAD_DATABASE}" "${:LARAVEL_LOAD_PATH}"';
if (str_ends_with($path, '.sql')) {
$command = 'psql --file="${:LARAVEL_LOAD_PATH}" --host="${:LARAVEL_LOAD_HOST}" --port="${:LARAVEL_LOAD_PORT}" --username="${:LARAVEL_LOAD_USER}" --dbname="${:LARAVEL_LOAD_DATABASE}"';
}
$process = $this->makeProcess($command);
$process->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [
'LARAVEL_LOAD_PATH' => $path,
]));
}
/**
* Get the name of the application's migration table.
*
* @return string
*/
protected function getMigrationTable(): string
{
[$schema, $table] = $this->connection->getSchemaBuilder()->parseSchemaAndTable($this->migrationTable);
return $schema.'.'.$this->connection->getTablePrefix().$table;
}
/**
* Get the base dump command arguments for PostgreSQL as a string.
*
* @return string
*/
protected function baseDumpCommand()
{
return 'pg_dump --no-owner --no-acl --host="${:LARAVEL_LOAD_HOST}" --port="${:LARAVEL_LOAD_PORT}" --username="${:LARAVEL_LOAD_USER}" --dbname="${:LARAVEL_LOAD_DATABASE}"';
}
/**
* Get the base variables for a dump / load command.
*
* @param array $config
* @return array
*/
protected function baseVariables(array $config)
{
$config['host'] ??= '';
return [
'LARAVEL_LOAD_HOST' => is_array($config['host']) ? $config['host'][0] : $config['host'],
'LARAVEL_LOAD_PORT' => $config['port'] ?? '',
'LARAVEL_LOAD_USER' => $config['username'],
'PGPASSWORD' => $config['password'],
'LARAVEL_LOAD_DATABASE' => $config['database'],
];
}
}
+175
View File
@@ -0,0 +1,175 @@
<?php
namespace Illuminate\Database\Schema;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\File;
class SQLiteBuilder extends Builder
{
/**
* Create a database in the schema.
*
* @param string $name
* @return bool
*/
public function createDatabase($name)
{
return File::put($name, '') !== false;
}
/**
* Drop a database from the schema if the database exists.
*
* @param string $name
* @return bool
*/
public function dropDatabaseIfExists($name)
{
return File::exists($name)
? File::delete($name)
: true;
}
/**
* Determine if the given table exists.
*
* @param string $table
* @return bool
*/
public function hasTable($table)
{
$table = $this->connection->getTablePrefix().$table;
return (bool) $this->connection->scalar(
$this->grammar->compileTableExists($table)
);
}
/**
* Get the tables for the database.
*
* @param bool $withSize
* @return array
*/
public function getTables($withSize = true)
{
if ($withSize) {
try {
$withSize = $this->connection->scalar($this->grammar->compileDbstatExists());
} catch (QueryException $e) {
$withSize = false;
}
}
return $this->connection->getPostProcessor()->processTables(
$this->connection->selectFromWriteConnection($this->grammar->compileTables($withSize))
);
}
/**
* Get the columns for a given table.
*
* @param string $table
* @return array
*/
public function getColumns($table)
{
$table = $this->connection->getTablePrefix().$table;
return $this->connection->getPostProcessor()->processColumns(
$this->connection->selectFromWriteConnection($this->grammar->compileColumns($table)),
$this->connection->scalar($this->grammar->compileSqlCreateStatement($table))
);
}
/**
* Drop all tables from the database.
*
* @return void
*/
public function dropAllTables()
{
$database = $this->connection->getDatabaseName();
if ($database !== ':memory:' &&
! str_contains($database, '?mode=memory') &&
! str_contains($database, '&mode=memory')
) {
return $this->refreshDatabaseFile();
}
$this->connection->select($this->grammar->compileEnableWriteableSchema());
$this->connection->select($this->grammar->compileDropAllTables());
$this->connection->select($this->grammar->compileDisableWriteableSchema());
$this->connection->select($this->grammar->compileRebuild());
}
/**
* Drop all views from the database.
*
* @return void
*/
public function dropAllViews()
{
$this->connection->select($this->grammar->compileEnableWriteableSchema());
$this->connection->select($this->grammar->compileDropAllViews());
$this->connection->select($this->grammar->compileDisableWriteableSchema());
$this->connection->select($this->grammar->compileRebuild());
}
/**
* Set the busy timeout.
*
* @param int $milliseconds
* @return bool
*/
public function setBusyTimeout($milliseconds)
{
return $this->connection->statement(
$this->grammar->compileSetBusyTimeout($milliseconds)
);
}
/**
* Set the journal mode.
*
* @param string $mode
* @return bool
*/
public function setJournalMode($mode)
{
return $this->connection->statement(
$this->grammar->compileSetJournalMode($mode)
);
}
/**
* Set the synchronous mode.
*
* @param int $mode
* @return bool
*/
public function setSynchronous($mode)
{
return $this->connection->statement(
$this->grammar->compileSetSynchronous($mode)
);
}
/**
* Empty the database file.
*
* @return void
*/
public function refreshDatabaseFile()
{
file_put_contents($this->connection->getDatabaseName(), '');
}
}
+142
View File
@@ -0,0 +1,142 @@
<?php
namespace Illuminate\Database\Schema;
use Illuminate\Database\Connection;
use Illuminate\Filesystem\Filesystem;
use Symfony\Component\Process\Process;
abstract class SchemaState
{
/**
* The connection instance.
*
* @var \Illuminate\Database\Connection
*/
protected $connection;
/**
* The filesystem instance.
*
* @var \Illuminate\Filesystem\Filesystem
*/
protected $files;
/**
* The name of the application's migration table.
*
* @var string
*/
protected $migrationTable = 'migrations';
/**
* The process factory callback.
*
* @var callable
*/
protected $processFactory;
/**
* The output callable instance.
*
* @var callable
*/
protected $output;
/**
* Create a new dumper instance.
*
* @param \Illuminate\Database\Connection $connection
* @param \Illuminate\Filesystem\Filesystem|null $files
* @param callable|null $processFactory
* @return void
*/
public function __construct(Connection $connection, ?Filesystem $files = null, ?callable $processFactory = null)
{
$this->connection = $connection;
$this->files = $files ?: new Filesystem;
$this->processFactory = $processFactory ?: function (...$arguments) {
return Process::fromShellCommandline(...$arguments)->setTimeout(null);
};
$this->handleOutputUsing(function () {
//
});
}
/**
* Dump the database's schema into a file.
*
* @param \Illuminate\Database\Connection $connection
* @param string $path
* @return void
*/
abstract public function dump(Connection $connection, $path);
/**
* Load the given schema file into the database.
*
* @param string $path
* @return void
*/
abstract public function load($path);
/**
* Create a new process instance.
*
* @param mixed ...$arguments
* @return \Symfony\Component\Process\Process
*/
public function makeProcess(...$arguments)
{
return call_user_func($this->processFactory, ...$arguments);
}
/**
* Determine if the current connection has a migration table.
*
* @return bool
*/
public function hasMigrationTable(): bool
{
return $this->connection->getSchemaBuilder()->hasTable($this->migrationTable);
}
/**
* Get the name of the application's migration table.
*
* @return string
*/
protected function getMigrationTable(): string
{
return $this->connection->getTablePrefix().$this->migrationTable;
}
/**
* Specify the name of the application's migration table.
*
* @param string $table
* @return $this
*/
public function withMigrationTable(string $table)
{
$this->migrationTable = $table;
return $this;
}
/**
* Specify the callback that should be used to handle process output.
*
* @param callable $output
* @return $this
*/
public function handleOutputUsing(callable $output)
{
$this->output = $output;
return $this;
}
}
+178
View File
@@ -0,0 +1,178 @@
<?php
namespace Illuminate\Database\Schema;
use InvalidArgumentException;
class SqlServerBuilder extends Builder
{
/**
* Create a database in the schema.
*
* @param string $name
* @return bool
*/
public function createDatabase($name)
{
return $this->connection->statement(
$this->grammar->compileCreateDatabase($name, $this->connection)
);
}
/**
* Drop a database from the schema if the database exists.
*
* @param string $name
* @return bool
*/
public function dropDatabaseIfExists($name)
{
return $this->connection->statement(
$this->grammar->compileDropDatabaseIfExists($name)
);
}
/**
* Determine if the given table exists.
*
* @param string $table
* @return bool
*/
public function hasTable($table)
{
[$schema, $table] = $this->parseSchemaAndTable($table);
$table = $this->connection->getTablePrefix().$table;
return (bool) $this->connection->scalar(
$this->grammar->compileTableExists($schema, $table)
);
}
/**
* Determine if the given view exists.
*
* @param string $view
* @return bool
*/
public function hasView($view)
{
[$schema, $view] = $this->parseSchemaAndTable($view);
$schema ??= $this->getDefaultSchema();
$view = $this->connection->getTablePrefix().$view;
foreach ($this->getViews() as $value) {
if (strtolower($view) === strtolower($value['name'])
&& strtolower($schema) === strtolower($value['schema'])) {
return true;
}
}
return false;
}
/**
* Drop all tables from the database.
*
* @return void
*/
public function dropAllTables()
{
$this->connection->statement($this->grammar->compileDropAllForeignKeys());
$this->connection->statement($this->grammar->compileDropAllTables());
}
/**
* Drop all views from the database.
*
* @return void
*/
public function dropAllViews()
{
$this->connection->statement($this->grammar->compileDropAllViews());
}
/**
* Get the columns for a given table.
*
* @param string $table
* @return array
*/
public function getColumns($table)
{
[$schema, $table] = $this->parseSchemaAndTable($table);
$table = $this->connection->getTablePrefix().$table;
$results = $this->connection->selectFromWriteConnection(
$this->grammar->compileColumns($schema, $table)
);
return $this->connection->getPostProcessor()->processColumns($results);
}
/**
* Get the indexes for a given table.
*
* @param string $table
* @return array
*/
public function getIndexes($table)
{
[$schema, $table] = $this->parseSchemaAndTable($table);
$table = $this->connection->getTablePrefix().$table;
return $this->connection->getPostProcessor()->processIndexes(
$this->connection->selectFromWriteConnection($this->grammar->compileIndexes($schema, $table))
);
}
/**
* Get the foreign keys for a given table.
*
* @param string $table
* @return array
*/
public function getForeignKeys($table)
{
[$schema, $table] = $this->parseSchemaAndTable($table);
$table = $this->connection->getTablePrefix().$table;
return $this->connection->getPostProcessor()->processForeignKeys(
$this->connection->selectFromWriteConnection($this->grammar->compileForeignKeys($schema, $table))
);
}
/**
* Get the default schema for the connection.
*
* @return string
*/
protected function getDefaultSchema()
{
return $this->connection->scalar($this->grammar->compileDefaultSchema());
}
/**
* Parse the database object reference and extract the schema and table.
*
* @param string $reference
* @return array
*/
protected function parseSchemaAndTable($reference)
{
$parts = array_pad(explode('.', $reference, 2), -2, null);
if (str_contains($parts[1], '.')) {
$database = $parts[0];
throw new InvalidArgumentException("Using three-part reference is not supported, you may use `Schema::connection('$database')` instead.");
}
return $parts;
}
}
+103
View File
@@ -0,0 +1,103 @@
<?php
namespace Illuminate\Database\Schema;
use Illuminate\Database\Connection;
use Illuminate\Support\Collection;
class SqliteSchemaState extends SchemaState
{
/**
* Dump the database's schema into a file.
*
* @param \Illuminate\Database\Connection $connection
* @param string $path
* @return void
*/
public function dump(Connection $connection, $path)
{
with($process = $this->makeProcess(
$this->baseCommand().' ".schema --indent"'
))->setTimeout(null)->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [
//
]));
$migrations = preg_replace('/CREATE TABLE sqlite_.+?\);[\r\n]+/is', '', $process->getOutput());
$this->files->put($path, $migrations.PHP_EOL);
if ($this->hasMigrationTable()) {
$this->appendMigrationData($path);
}
}
/**
* Append the migration data to the schema dump.
*
* @param string $path
* @return void
*/
protected function appendMigrationData(string $path)
{
with($process = $this->makeProcess(
$this->baseCommand().' ".dump \''.$this->getMigrationTable().'\'"'
))->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [
//
]));
$migrations = (new Collection(preg_split("/\r\n|\n|\r/", $process->getOutput())))
->filter(fn ($line) => preg_match('/^\s*(--|INSERT\s)/iu', $line) === 1 && strlen($line) > 0)
->all();
$this->files->append($path, implode(PHP_EOL, $migrations).PHP_EOL);
}
/**
* Load the given schema file into the database.
*
* @param string $path
* @return void
*/
public function load($path)
{
$database = $this->connection->getDatabaseName();
if ($database === ':memory:' ||
str_contains($database, '?mode=memory') ||
str_contains($database, '&mode=memory')
) {
$this->connection->getPdo()->exec($this->files->get($path));
return;
}
$process = $this->makeProcess($this->baseCommand().' < "${:LARAVEL_LOAD_PATH}"');
$process->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [
'LARAVEL_LOAD_PATH' => $path,
]));
}
/**
* Get the base sqlite command arguments as a string.
*
* @return string
*/
protected function baseCommand()
{
return 'sqlite3 "${:LARAVEL_LOAD_DATABASE}"';
}
/**
* Get the base variables for a dump / load command.
*
* @param array $config
* @return array
*/
protected function baseVariables(array $config)
{
return [
'LARAVEL_LOAD_DATABASE' => $config['database'],
];
}
}