First Commit

This commit is contained in:
marco
2026-09-01 10:46:29 -03:00
commit bf96b77e9c
10000 changed files with 3508600 additions and 0 deletions
+1889
View File
File diff suppressed because it is too large Load Diff
+658
View File
@@ -0,0 +1,658 @@
<?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';
/**
* Indicates whether Doctrine DBAL usage will be prevented if possible when dropping, renaming, and modifying columns.
*
* @var bool
*/
public static $alwaysUsesNativeSchemaOperationsIfPossible = false;
/**
* 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()
{
return static::defaultMorphKeyType('uuid');
}
/**
* Set the default morph key type for migrations to ULIDs.
*
* @return void
*/
public static function morphUsingUlids()
{
return static::defaultMorphKeyType('ulid');
}
/**
* Attempt to use native schema operations for dropping, renaming, and modifying columns, even if Doctrine DBAL is installed.
*
* @param bool $value
* @return void
*/
public static function useNativeSchemaOperationsIfPossible(bool $value = true)
{
static::$alwaysUsesNativeSchemaOperationsIfPossible = $value;
}
/**
* 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(false) 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.');
}
/**
* Get all of the table names for the database.
*
* @deprecated Will be removed in a future Laravel version.
*
* @return array
*
* @throws \LogicException
*/
public function getAllTables()
{
throw new LogicException('This database driver does not support getting all tables.');
}
/**
* 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)
{
if (! $this->connection->usingNativeSchemaOperations()) {
$table = $this->connection->getTablePrefix().$table;
return $this->connection->getDoctrineColumn($table, $column)->getType()->getName();
}
$columns = $this->getColumns($table);
foreach ($columns as $value) {
if (strtolower($value['name']) === $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 (MySQL/PostgreSQL/SQL Server)
* @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\Database\Query\Expression $expression = null) Create a SQL compliant identity column (PostgreSQL)
* @method $this index(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() Add a primary index
* @method $this fulltext(string $indexName = null) Add a fulltext index
* @method $this spatialIndex(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 $expression) Create a stored generated column (MySQL/PostgreSQL/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 useCurrentOnUpdate() Set the TIMESTAMP column to use CURRENT_TIMESTAMP when updating (MySQL)
* @method $this virtualAs(string $expression) Create a virtual generated column (MySQL/PostgreSQL/SQLite)
*/
class ColumnDefinition extends Fluent
{
//
}
+54
View File
@@ -0,0 +1,54 @@
<?php
namespace Illuminate\Database\Schema;
use Illuminate\Support\Str;
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 = 'id', $indexName = null)
{
return $this->references($column, $indexName)->on($table ?? Str::of($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);
}
}
+86
View File
@@ -0,0 +1,86 @@
<?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 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');
}
}
+238
View File
@@ -0,0 +1,238 @@
<?php
namespace Illuminate\Database\Schema\Grammars;
use Doctrine\DBAL\Schema\AbstractSchemaManager as SchemaManager;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Types\Type;
use Illuminate\Database\Connection;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Fluent;
use RuntimeException;
class ChangeColumn
{
/**
* Compile a change column command into a series of SQL statements.
*
* @param \Illuminate\Database\Schema\Grammars\Grammar $grammar
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $command
* @param \Illuminate\Database\Connection $connection
* @return array
*
* @throws \RuntimeException
*/
public static function compile($grammar, Blueprint $blueprint, Fluent $command, Connection $connection)
{
if (! $connection->isDoctrineAvailable()) {
throw new RuntimeException(sprintf(
'Changing columns for table "%s" requires Doctrine DBAL. Please install the doctrine/dbal package.',
$blueprint->getTable()
));
}
$schema = $connection->getDoctrineSchemaManager();
$databasePlatform = $connection->getDoctrineConnection()->getDatabasePlatform();
$databasePlatform->registerDoctrineTypeMapping('enum', 'string');
$tableDiff = static::getChangedDiff(
$grammar, $blueprint, $schema
);
if (! $tableDiff->isEmpty()) {
return (array) $databasePlatform->getAlterTableSQL($tableDiff);
}
return [];
}
/**
* Get the Doctrine table difference for the given changes.
*
* @param \Illuminate\Database\Schema\Grammars\Grammar $grammar
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Doctrine\DBAL\Schema\AbstractSchemaManager $schema
* @return \Doctrine\DBAL\Schema\TableDiff
*/
protected static function getChangedDiff($grammar, Blueprint $blueprint, SchemaManager $schema)
{
$current = $schema->introspectTable($grammar->getTablePrefix().$blueprint->getTable());
return $schema->createComparator()->compareTables(
$current, static::getTableWithColumnChanges($blueprint, $current)
);
}
/**
* Get a copy of the given Doctrine table after making the column changes.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Doctrine\DBAL\Schema\Table $table
* @return \Doctrine\DBAL\Schema\Table
*/
protected static function getTableWithColumnChanges(Blueprint $blueprint, Table $table)
{
$table = clone $table;
foreach ($blueprint->getChangedColumns() as $fluent) {
$column = static::getDoctrineColumn($table, $fluent);
// Here we will spin through each fluent column definition and map it to the proper
// Doctrine column definitions - which is necessary because Laravel and Doctrine
// use some different terminology for various column attributes on the tables.
foreach ($fluent->getAttributes() as $key => $value) {
if (! is_null($option = static::mapFluentOptionToDoctrine($key))) {
if (method_exists($column, $method = 'set'.ucfirst($option))) {
$column->{$method}(static::mapFluentValueToDoctrine($option, $value));
continue;
}
$column->setPlatformOption($option, static::mapFluentValueToDoctrine($option, $value));
}
}
}
return $table;
}
/**
* Get the Doctrine column instance for a column change.
*
* @param \Doctrine\DBAL\Schema\Table $table
* @param \Illuminate\Support\Fluent $fluent
* @return \Doctrine\DBAL\Schema\Column
*/
protected static function getDoctrineColumn(Table $table, Fluent $fluent)
{
return $table->modifyColumn(
$fluent['name'], static::getDoctrineColumnChangeOptions($fluent)
)->getColumn($fluent['name']);
}
/**
* Get the Doctrine column change options.
*
* @param \Illuminate\Support\Fluent $fluent
* @return array
*/
protected static function getDoctrineColumnChangeOptions(Fluent $fluent)
{
$options = ['type' => static::getDoctrineColumnType($fluent['type'])];
if (! in_array($fluent['type'], ['smallint', 'integer', 'bigint'])) {
$options['autoincrement'] = false;
}
if (in_array($fluent['type'], ['tinyText', 'text', 'mediumText', 'longText'])) {
$options['length'] = static::calculateDoctrineTextLength($fluent['type']);
}
if ($fluent['type'] === 'char') {
$options['fixed'] = true;
}
if (static::doesntNeedCharacterOptions($fluent['type'])) {
$options['customSchemaOptions'] = [
'collation' => '',
'charset' => '',
];
}
return $options;
}
/**
* Get the doctrine column type.
*
* @param string $type
* @return \Doctrine\DBAL\Types\Type
*/
protected static function getDoctrineColumnType($type)
{
$type = strtolower($type);
return Type::getType(match ($type) {
'biginteger' => 'bigint',
'smallinteger' => 'smallint',
'tinytext', 'mediumtext', 'longtext' => 'text',
'binary' => 'blob',
'uuid' => 'guid',
'char' => 'string',
'double' => 'float',
default => $type,
});
}
/**
* Calculate the proper column length to force the Doctrine text type.
*
* @param string $type
* @return int
*/
protected static function calculateDoctrineTextLength($type)
{
return match ($type) {
'tinyText' => 1,
'mediumText' => 65535 + 1,
'longText' => 16777215 + 1,
default => 255 + 1,
};
}
/**
* Determine if the given type does not need character / collation options.
*
* @param string $type
* @return bool
*/
protected static function doesntNeedCharacterOptions($type)
{
return in_array($type, [
'bigInteger',
'binary',
'boolean',
'date',
'dateTime',
'decimal',
'double',
'float',
'integer',
'json',
'mediumInteger',
'smallInteger',
'time',
'timestamp',
'tinyInteger',
]);
}
/**
* Get the matching Doctrine option for a given Fluent attribute name.
*
* @param string $attribute
* @return string|null
*/
protected static function mapFluentOptionToDoctrine($attribute)
{
return match ($attribute) {
'type', 'name' => null,
'nullable' => 'notnull',
'total' => 'precision',
'places' => 'scale',
default => $attribute,
};
}
/**
* Get the matching Doctrine value for a given Fluent attribute.
*
* @param string $option
* @param mixed $value
* @return mixed
*/
protected static function mapFluentValueToDoctrine($option, $value)
{
return $option === 'notnull' ? ! $value : $value;
}
}
+357
View File
@@ -0,0 +1,357 @@
<?php
namespace Illuminate\Database\Schema\Grammars;
use BackedEnum;
use Doctrine\DBAL\Schema\AbstractSchemaManager as SchemaManager;
use Doctrine\DBAL\Schema\TableDiff;
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 RenameColumn::compile($this, $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)
{
return ChangeColumn::compile($this, $blueprint, $command, $connection);
}
/**
* 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 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) {
// 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);
$columns[] = $this->addModifiers($sql, $blueprint, $column);
}
return $columns;
}
/**
* 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.');
}
/**
* 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 primary key command 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;
});
}
/**
* 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
* @param bool $prefixAlias
* @return string
*/
public function wrap($value, $prefixAlias = false)
{
return parent::wrap(
$value instanceof Fluent ? $value->name : $value, $prefixAlias
);
}
/**
* 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."'";
}
/**
* Create an empty Doctrine DBAL TableDiff from the Blueprint.
*
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Doctrine\DBAL\Schema\AbstractSchemaManager $schema
* @return \Doctrine\DBAL\Schema\TableDiff
*/
public function getDoctrineTableDiff(Blueprint $blueprint, SchemaManager $schema)
{
$tableName = $this->getTablePrefix().$blueprint->getTable();
$table = $schema->introspectTable($tableName);
return new TableDiff(tableName: $tableName, fromTable: $table);
}
/**
* 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;
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+84
View File
@@ -0,0 +1,84 @@
<?php
namespace Illuminate\Database\Schema\Grammars;
use Doctrine\DBAL\Schema\AbstractSchemaManager as SchemaManager;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\TableDiff;
use Illuminate\Database\Connection;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Fluent;
class RenameColumn
{
/**
* Compile a rename column command.
*
* @param \Illuminate\Database\Schema\Grammars\Grammar $grammar
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $command
* @param \Illuminate\Database\Connection $connection
* @return array
*/
public static function compile(Grammar $grammar, Blueprint $blueprint, Fluent $command, Connection $connection)
{
$schema = $connection->getDoctrineSchemaManager();
$databasePlatform = $connection->getDoctrineConnection()->getDatabasePlatform();
$databasePlatform->registerDoctrineTypeMapping('enum', 'string');
$column = $connection->getDoctrineColumn(
$grammar->getTablePrefix().$blueprint->getTable(), $command->from
);
return (array) $databasePlatform->getAlterTableSQL(static::getRenamedDiff(
$grammar, $blueprint, $command, $column, $schema
));
}
/**
* Get a new column instance with the new column name.
*
* @param \Illuminate\Database\Schema\Grammars\Grammar $grammar
* @param \Illuminate\Database\Schema\Blueprint $blueprint
* @param \Illuminate\Support\Fluent $command
* @param \Doctrine\DBAL\Schema\Column $column
* @param \Doctrine\DBAL\Schema\AbstractSchemaManager $schema
* @return \Doctrine\DBAL\Schema\TableDiff
*/
protected static function getRenamedDiff(Grammar $grammar, Blueprint $blueprint, Fluent $command, Column $column, SchemaManager $schema)
{
return static::setRenamedColumns(
$grammar->getDoctrineTableDiff($blueprint, $schema), $command, $column
);
}
/**
* Set the renamed columns on the table diff.
*
* @param \Doctrine\DBAL\Schema\TableDiff $tableDiff
* @param \Illuminate\Support\Fluent $command
* @param \Doctrine\DBAL\Schema\Column $column
* @return \Doctrine\DBAL\Schema\TableDiff
*/
protected static function setRenamedColumns(TableDiff $tableDiff, Fluent $command, Column $column)
{
$tableDiff->renamedColumns = [
$command->from => new Column($command->to, $column->getType(), self::getWritableColumnOptions($column)),
];
return $tableDiff;
}
/**
* Get the writable column options.
*
* @param \Doctrine\DBAL\Schema\Column $column
* @return array
*/
private static function getWritableColumnOptions(Column $column)
{
return array_filter($column->toArray(), function (string $name) use ($column) {
return method_exists($column, 'set'.$name);
}, ARRAY_FILTER_USE_KEY);
}
}
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
{
//
}
+179
View File
@@ -0,0 +1,179 @@
<?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)
);
}
/**
* 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 all of the table names for the database.
*
* @deprecated Will be removed in a future Laravel version.
*
* @return array
*/
public function getAllTables()
{
return $this->connection->select(
$this->grammar->compileGetAllTables()
);
}
/**
* Get all of the view names for the database.
*
* @deprecated Will be removed in a future Laravel version.
*
* @return array
*/
public function getAllViews()
{
return $this->connection->select(
$this->grammar->compileGetAllViews()
);
}
/**
* 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)
);
}
}
+172
View File
@@ -0,0 +1,172 @@
<?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->migrationTable.' --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
* @return \Symfony\Component\Process\Process
*/
protected function executeDumpProcess(Process $process, $output, array $variables)
{
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);
}
if (str_contains($e->getMessage(), 'set-gtid-purged')) {
return $this->executeDumpProcess(Process::fromShellCommandLine(
str_replace(' --set-gtid-purged=OFF', '', $process->getCommandLine())
), $output, $variables);
}
throw $e;
}
return $process;
}
}
+320
View File
@@ -0,0 +1,320 @@
<?php
namespace Illuminate\Database\Schema;
use Illuminate\Database\Concerns\ParsesSearchPath;
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)
{
[$database, $schema, $table] = $this->parseSchemaAndTable($table);
$table = $this->connection->getTablePrefix().$table;
return count($this->connection->selectFromWriteConnection(
$this->grammar->compileTableExists(), [$database, $schema, $table]
)) > 0;
}
/**
* 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())
);
}
/**
* Get all of the table names for the database.
*
* @deprecated Will be removed in a future Laravel version.
*
* @return array
*/
public function getAllTables()
{
return $this->connection->select(
$this->grammar->compileGetAllTables(
$this->parseSearchPath(
$this->connection->getConfig('search_path') ?: $this->connection->getConfig('schema')
)
)
);
}
/**
* Get all of the view names for the database.
*
* @deprecated Will be removed in a future Laravel version.
*
* @return array
*/
public function getAllViews()
{
return $this->connection->select(
$this->grammar->compileGetAllViews(
$this->parseSearchPath(
$this->connection->getConfig('search_path') ?: $this->connection->getConfig('schema')
)
)
);
}
/**
* Drop all tables from the database.
*
* @return void
*/
public function dropAllTables()
{
$tables = [];
$excludedTables = $this->grammar->escapeNames(
$this->connection->getConfig('dont_drop') ?? ['spatial_ref_sys']
);
$schemas = $this->grammar->escapeNames($this->getSchemas());
foreach ($this->getTables() as $table) {
$qualifiedName = $table['schema'].'.'.$table['name'];
if (empty(array_intersect($this->grammar->escapeNames([$table['name'], $qualifiedName]), $excludedTables))
&& in_array($this->grammar->escapeNames([$table['schema']])[0], $schemas)) {
$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->grammar->escapeNames($this->getSchemas());
foreach ($this->getViews() as $view) {
if (in_array($this->grammar->escapeNames([$view['schema']])[0], $schemas)) {
$views[] = $view['schema'].'.'.$view['name'];
}
}
if (empty($views)) {
return;
}
$this->connection->statement(
$this->grammar->compileDropAllViews($views)
);
}
/**
* Get all of the type names for the database.
*
* @deprecated Will be removed in a future Laravel version.
*
* @return array
*/
public function getAllTypes()
{
return $this->connection->select(
$this->grammar->compileGetAllTypes()
);
}
/**
* Drop all types from the database.
*
* @return void
*/
public function dropAllTypes()
{
$types = [];
$domains = [];
$schemas = $this->grammar->escapeNames($this->getSchemas());
foreach ($this->getTypes() as $type) {
if (! $type['implicit'] && in_array($this->grammar->escapeNames([$type['schema']])[0], $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)
{
[$database, $schema, $table] = $this->parseSchemaAndTable($table);
$table = $this->connection->getTablePrefix().$table;
$results = $this->connection->selectFromWriteConnection(
$this->grammar->compileColumns($database, $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
*/
protected function getSchemas()
{
return $this->parseSearchPath(
$this->connection->getConfig('search_path') ?: $this->connection->getConfig('schema') ?: 'public'
);
}
/**
* Parse the database object reference and extract the database, schema, and table.
*
* @param string $reference
* @return array
*/
protected function parseSchemaAndTable($reference)
{
$parts = explode('.', $reference);
$database = $this->connection->getConfig('database');
// If the reference contains a database name, we will use that instead of the
// default database name for the connection. This allows the database name
// to be specified in the query instead of at the full connection level.
if (count($parts) === 3) {
$database = $parts[0];
array_shift($parts);
}
// 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 [$database, $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));
}
}
+82
View File
@@ -0,0 +1,82 @@
<?php
namespace Illuminate\Database\Schema;
use Illuminate\Database\Connection;
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 = collect([
$this->baseDumpCommand().' --schema-only > '.$path,
]);
if ($this->hasMigrationTable()) {
$commands->push($this->baseDumpCommand().' -t '.$this->migrationTable.' --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 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'],
];
}
}
+128
View File
@@ -0,0 +1,128 @@
<?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;
}
/**
* 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 all of the table names for the database.
*
* @deprecated Will be removed in a future Laravel version.
*
* @return array
*/
public function getAllTables()
{
return $this->connection->select(
$this->grammar->compileGetAllTables()
);
}
/**
* Get all of the view names for the database.
*
* @deprecated Will be removed in a future Laravel version.
*
* @return array
*/
public function getAllViews()
{
return $this->connection->select(
$this->grammar->compileGetAllViews()
);
}
/**
* Drop all tables from the database.
*
* @return void
*/
public function dropAllTables()
{
if ($this->connection->getDatabaseName() !== ':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());
}
/**
* Empty the database file.
*
* @return void
*/
public function refreshDatabaseFile()
{
file_put_contents($this->connection->getDatabaseName(), '');
}
}
+132
View File
@@ -0,0 +1,132 @@
<?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);
}
/**
* 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;
}
}
+82
View File
@@ -0,0 +1,82 @@
<?php
namespace Illuminate\Database\Schema;
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)
);
}
/**
* 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());
}
/**
* Drop all tables from the database.
*
* @deprecated Will be removed in a future Laravel version.
*
* @return array
*/
public function getAllTables()
{
return $this->connection->select(
$this->grammar->compileGetAllTables()
);
}
/**
* Get all of the view names for the database.
*
* @deprecated Will be removed in a future Laravel version.
*
* @return array
*/
public function getAllViews()
{
return $this->connection->select(
$this->grammar->compileGetAllViews()
);
}
}
+101
View File
@@ -0,0 +1,101 @@
<?php
namespace Illuminate\Database\Schema;
use Illuminate\Database\Connection;
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'
))->setTimeout(null)->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [
//
]));
$migrations = collect(preg_split("/\r\n|\n|\r/", $process->getOutput()))->filter(function ($line) {
return stripos($line, 'sqlite_sequence') === false &&
strlen($line) > 0;
})->all();
$this->files->put($path, implode(PHP_EOL, $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->migrationTable.'\'"'
))->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [
//
]));
$migrations = collect(preg_split("/\r\n|\n|\r/", $process->getOutput()))->filter(function ($line) {
return 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)
{
if ($this->connection->getDatabaseName() === ':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'],
];
}
}