From f6538ad9bb87c36f3b0252668ec7cb25f8873291 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 31 Oct 2023 16:10:46 +0330 Subject: [PATCH 01/52] remove doctrine dbal from grammar --- composer.json | 2 +- src/Illuminate/Database/Connection.php | 14 --- src/Illuminate/Database/Schema/Blueprint.php | 7 -- src/Illuminate/Database/Schema/Builder.php | 8 -- .../Database/Schema/Grammars/Grammar.php | 24 +---- .../Database/Schema/Grammars/MySqlGrammar.php | 19 ---- .../Schema/Grammars/PostgresGrammar.php | 19 ---- .../Database/Schema/Grammars/RenameColumn.php | 93 ------------------- .../Schema/Grammars/SQLiteGrammar.php | 60 +----------- .../Schema/Grammars/SqlServerGrammar.php | 10 +- tests/Database/DatabaseSchemaBuilderTest.php | 12 +-- .../Database/DBAL/TimestampTypeTest.php | 22 +++-- ...SqlSchemaBuilderAlterTableWithEnumTest.php | 2 +- .../Database/SchemaBuilderTest.php | 4 +- 14 files changed, 38 insertions(+), 258 deletions(-) delete mode 100644 src/Illuminate/Database/Schema/Grammars/RenameColumn.php diff --git a/composer.json b/composer.json index 439d904e1aa3..877d5144c879 100644 --- a/composer.json +++ b/composer.json @@ -163,7 +163,7 @@ "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.235.5).", "brianium/paratest": "Required to run tests in parallel (^6.0).", - "doctrine/dbal": "Required to rename columns and drop SQLite columns (^4.0).", + "doctrine/dbal": "Required to modify SQLite columns (^4.0).", "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", "filp/whoops": "Required for friendly error pages in development (^2.14.3).", "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.6).", diff --git a/src/Illuminate/Database/Connection.php b/src/Illuminate/Database/Connection.php index 24583f4a4079..e73cce1035a4 100755 --- a/src/Illuminate/Database/Connection.php +++ b/src/Illuminate/Database/Connection.php @@ -1226,20 +1226,6 @@ public function usingNativeSchemaOperations() return ! $this->isDoctrineAvailable() || SchemaBuilder::$alwaysUsesNativeSchemaOperationsIfPossible; } - /** - * Get a Doctrine Schema Column instance. - * - * @param string $table - * @param string $column - * @return \Doctrine\DBAL\Schema\Column - */ - public function getDoctrineColumn($table, $column) - { - $schema = $this->getDoctrineSchemaManager(); - - return $schema->introspectTable($table)->getColumn($column); - } - /** * Get the Doctrine DBAL schema manager for the connection. * diff --git a/src/Illuminate/Database/Schema/Blueprint.php b/src/Illuminate/Database/Schema/Blueprint.php index ec5a87bb2399..84853dc502fa 100755 --- a/src/Illuminate/Database/Schema/Blueprint.php +++ b/src/Illuminate/Database/Schema/Blueprint.php @@ -153,13 +153,6 @@ public function toSql(Connection $connection, Grammar $grammar) protected function ensureCommandsAreValid(Connection $connection) { if ($connection instanceof SQLiteConnection) { - if ($this->commandsNamed(['dropColumn', 'renameColumn'])->count() > 1 - && ! $connection->usingNativeSchemaOperations()) { - throw new BadMethodCallException( - "SQLite doesn't support multiple calls to dropColumn / renameColumn in a single modification." - ); - } - if ($this->commandsNamed(['dropForeign'])->count() > 0) { throw new BadMethodCallException( "SQLite doesn't support dropping foreign keys (you would need to re-create the table)." diff --git a/src/Illuminate/Database/Schema/Builder.php b/src/Illuminate/Database/Schema/Builder.php index 7a1b58d4ddb1..ae8a6707c176 100755 --- a/src/Illuminate/Database/Schema/Builder.php +++ b/src/Illuminate/Database/Schema/Builder.php @@ -238,14 +238,6 @@ public function whenTableDoesntHaveColumn(string $table, string $column, Closure */ public function getColumnType($table, $column, $fullDefinition = false) { - $table = $this->connection->getTablePrefix().$table; - - if (! $this->connection->usingNativeSchemaOperations()) { - $type = $this->connection->getDoctrineColumn($table, $column)->getType(); - - return $type::lookupName($type); - } - $columns = $this->getColumns($table); foreach ($columns as $value) { diff --git a/src/Illuminate/Database/Schema/Grammars/Grammar.php b/src/Illuminate/Database/Schema/Grammars/Grammar.php index 9933a38be0fc..a9d1a64d7038 100755 --- a/src/Illuminate/Database/Schema/Grammars/Grammar.php +++ b/src/Illuminate/Database/Schema/Grammars/Grammar.php @@ -3,8 +3,6 @@ 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; @@ -76,7 +74,11 @@ public function compileDropDatabaseIfExists($name) */ public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Connection $connection) { - return RenameColumn::compile($this, $blueprint, $command, $connection); + return sprintf('alter table %s rename column %s to %s', + $this->wrapTable($blueprint), + $this->wrap($command->from), + $this->wrap($command->to) + ); } /** @@ -319,22 +321,6 @@ protected function getDefaultValue($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 $schema->createComparator()->compareTables(oldTable: $table, newTable: $table); - } - /** * Get the fluent commands for the grammar. * diff --git a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php index 7f940b95f975..2017f5dc0da4 100755 --- a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php @@ -230,25 +230,6 @@ public function compileAutoIncrementStartingValues(Blueprint $blueprint, Fluent } } - /** - * 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 $connection->usingNativeSchemaOperations() - ? sprintf('alter table %s rename column %s to %s', - $this->wrapTable($blueprint), - $this->wrap($command->from), - $this->wrap($command->to) - ) - : parent::compileRenameColumn($blueprint, $command, $connection); - } - /** * Compile a change column command into a series of SQL statements. * diff --git a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php index 845e818de542..63e31a3c5e0a 100755 --- a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php @@ -160,25 +160,6 @@ public function compileAutoIncrementStartingValues(Blueprint $blueprint, Fluent } } - /** - * 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 $connection->usingNativeSchemaOperations() - ? sprintf('alter table %s rename column %s to %s', - $this->wrapTable($blueprint), - $this->wrap($command->from), - $this->wrap($command->to) - ) - : parent::compileRenameColumn($blueprint, $command, $connection); - } - /** * Compile a change column command into a series of SQL statements. * diff --git a/src/Illuminate/Database/Schema/Grammars/RenameColumn.php b/src/Illuminate/Database/Schema/Grammars/RenameColumn.php deleted file mode 100644 index ff611c93160a..000000000000 --- a/src/Illuminate/Database/Schema/Grammars/RenameColumn.php +++ /dev/null @@ -1,93 +0,0 @@ -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) - { - return new TableDiff( - $tableDiff->getOldTable(), - $tableDiff->getAddedColumns(), - $tableDiff->getModifiedColumns(), - $tableDiff->getDroppedColumns(), - [$command->from => new Column($command->to, $column->getType(), self::getWritableColumnOptions($column))], - $tableDiff->getAddedIndexes(), - $tableDiff->getModifiedIndexes(), - $tableDiff->getDroppedIndexes(), - $tableDiff->getRenamedIndexes(), - $tableDiff->getAddedForeignKeys(), - $tableDiff->getModifiedColumns(), - $tableDiff->getDroppedForeignKeys(), - ); - } - - /** - * 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); - } -} diff --git a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php index 4a07a52ecc11..3b4096ee6531 100755 --- a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php @@ -3,7 +3,6 @@ namespace Illuminate\Database\Schema\Grammars; use Doctrine\DBAL\Schema\Index; -use Doctrine\DBAL\Schema\TableDiff; use Illuminate\Database\Connection; use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Blueprint; @@ -163,25 +162,6 @@ public function compileAdd(Blueprint $blueprint, Fluent $command) })->all(); } - /** - * 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 $connection->usingNativeSchemaOperations() - ? sprintf('alter table %s rename column %s to %s', - $this->wrapTable($blueprint), - $this->wrap($command->from), - $this->wrap($command->to) - ) - : parent::compileRenameColumn($blueprint, $command, $connection); - } - /** * Compile a unique key command. * @@ -324,45 +304,11 @@ public function compileRebuild() */ public function compileDropColumn(Blueprint $blueprint, Fluent $command, Connection $connection) { - if ($connection->usingNativeSchemaOperations()) { - $table = $this->wrapTable($blueprint); - - $columns = $this->prefixArray('drop column', $this->wrapArray($command->columns)); - - return collect($columns)->map(fn ($column) => 'alter table '.$table.' '.$column - )->all(); - } else { - $tableDiff = $this->getDoctrineTableDiff( - $blueprint, $schema = $connection->getDoctrineSchemaManager() - ); + $table = $this->wrapTable($blueprint); - $droppedColumns = []; + $columns = $this->prefixArray('drop column', $this->wrapArray($command->columns)); - foreach ($command->columns as $name) { - $droppedColumns[$name] = $connection->getDoctrineColumn( - $this->getTablePrefix().$blueprint->getTable(), $name - ); - } - - $platform = $connection->getDoctrineConnection()->getDatabasePlatform(); - - return (array) $platform->getAlterTableSQL( - new TableDiff( - $tableDiff->getOldTable(), - $tableDiff->getAddedColumns(), - $tableDiff->getModifiedColumns(), - $droppedColumns, - $tableDiff->getRenamedColumns(), - $tableDiff->getAddedIndexes(), - $tableDiff->getModifiedIndexes(), - $tableDiff->getDroppedIndexes(), - $tableDiff->getRenamedIndexes(), - $tableDiff->getAddedForeignKeys(), - $tableDiff->getModifiedColumns(), - $tableDiff->getDroppedForeignKeys(), - ) - ); - } + return collect($columns)->map(fn ($column) => 'alter table '.$table.' '.$column)->all(); } /** diff --git a/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php b/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php index d70aedadc0e9..91d65f57ec7b 100755 --- a/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php @@ -153,12 +153,10 @@ public function compileAdd(Blueprint $blueprint, Fluent $command) */ public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Connection $connection) { - return $connection->usingNativeSchemaOperations() - ? sprintf("sp_rename '%s', %s, 'COLUMN'", - $this->wrap($blueprint->getTable().'.'.$command->from), - $this->wrap($command->to) - ) - : parent::compileRenameColumn($blueprint, $command, $connection); + return sprintf("sp_rename '%s', %s, 'COLUMN'", + $this->wrap($blueprint->getTable().'.'.$command->from), + $this->wrap($command->to) + ); } /** diff --git a/tests/Database/DatabaseSchemaBuilderTest.php b/tests/Database/DatabaseSchemaBuilderTest.php index 53bd2693e058..6a516a4f5c2e 100755 --- a/tests/Database/DatabaseSchemaBuilderTest.php +++ b/tests/Database/DatabaseSchemaBuilderTest.php @@ -5,6 +5,7 @@ use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Types\Type; use Illuminate\Database\Connection; +use Illuminate\Database\Query\Processors\Processor; use Illuminate\Database\Schema\Builder; use Illuminate\Database\Schema\Grammars\Grammar; use LogicException; @@ -73,16 +74,15 @@ public function testTableHasColumns() public function testGetColumnTypeAddsPrefix() { $connection = m::mock(Connection::class); - $column = m::mock(Column::class); - $type = m::mock(Type::class); $grammar = m::mock(Grammar::class); + $processor = m::mock(Processor::class); $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $connection->shouldReceive('getPostProcessor')->andReturn($processor); + $processor->shouldReceive('processColumns')->once()->andReturn([['name' => 'id', 'type_name' => 'integer']]); $builder = new Builder($connection); $connection->shouldReceive('getTablePrefix')->once()->andReturn('prefix_'); - $connection->shouldReceive('getDoctrineColumn')->once()->with('prefix_users', 'id')->andReturn($column); - $connection->shouldReceive('usingNativeSchemaOperations')->once()->andReturn(false); - $column->shouldReceive('getType')->once()->andReturn($type); - $type->shouldReceive('lookupName')->once()->andReturn('integer'); + $grammar->shouldReceive('compileColumns')->once()->with('prefix_users')->andReturn('sql'); + $connection->shouldReceive('selectFromWriteConnection')->once()->with('sql')->andReturn([['name' => 'id', 'type_name' => 'integer']]); $this->assertSame('integer', $builder->getColumnType('users', 'id')); } diff --git a/tests/Integration/Database/DBAL/TimestampTypeTest.php b/tests/Integration/Database/DBAL/TimestampTypeTest.php index f53265cf8431..a29659febdae 100644 --- a/tests/Integration/Database/DBAL/TimestampTypeTest.php +++ b/tests/Integration/Database/DBAL/TimestampTypeTest.php @@ -38,9 +38,14 @@ public function testChangeDatetimeColumnToTimestampColumn() $this->assertTrue(Schema::hasColumn('test', 'datetime_to_timestamp')); // Only Postgres and MySQL actually have a timestamp type - in_array($this->driver, ['pgsql', 'mysql']) - ? $this->assertSame('timestamp', Schema::getColumnType('test', 'datetime_to_timestamp')) - : $this->assertSame('datetime', Schema::getColumnType('test', 'datetime_to_timestamp')); + $this->assertSame( + match ($this->driver) { + 'mysql' => 'timestamp', + 'pgsql' => 'timestamp without time zone', + default => 'datetime', + }, + Schema::getColumnType('test', 'datetime_to_timestamp') + ); } public function testChangeTimestampColumnToDatetimeColumn() @@ -55,9 +60,14 @@ public function testChangeTimestampColumnToDatetimeColumn() $this->assertTrue(Schema::hasColumn('test', 'timestamp_to_datetime')); // Postgres only has a timestamp type - $this->driver === 'pgsql' - ? $this->assertSame('timestamp', Schema::getColumnType('test', 'timestamp_to_datetime')) - : $this->assertSame('datetime', Schema::getColumnType('test', 'timestamp_to_datetime')); + $this->assertSame( + match ($this->driver) { + 'pgsql' => 'timestamp without time zone', + 'sqlsrv' => 'datetime2', + default => 'datetime', + }, + Schema::getColumnType('test', 'timestamp_to_datetime') + ); } public function testChangeStringColumnToTimestampColumn() diff --git a/tests/Integration/Database/MySql/DatabaseMySqlSchemaBuilderAlterTableWithEnumTest.php b/tests/Integration/Database/MySql/DatabaseMySqlSchemaBuilderAlterTableWithEnumTest.php index ad1290f384f8..907ba4998df4 100644 --- a/tests/Integration/Database/MySql/DatabaseMySqlSchemaBuilderAlterTableWithEnumTest.php +++ b/tests/Integration/Database/MySql/DatabaseMySqlSchemaBuilderAlterTableWithEnumTest.php @@ -42,7 +42,7 @@ public function testChangeColumnOnTableWithEnum() $table->unsignedInteger('age')->charset('')->change(); }); - $this->assertSame('integer', Schema::getColumnType('users', 'age')); + $this->assertSame('int', Schema::getColumnType('users', 'age')); } public function testGetAllTablesAndColumnListing() diff --git a/tests/Integration/Database/SchemaBuilderTest.php b/tests/Integration/Database/SchemaBuilderTest.php index 0a13f5812b18..0b21e7df84e9 100644 --- a/tests/Integration/Database/SchemaBuilderTest.php +++ b/tests/Integration/Database/SchemaBuilderTest.php @@ -63,7 +63,7 @@ public function testRegisterCustomDoctrineType() $blueprint->build($this->getConnection(), new SQLiteGrammar); $this->assertArrayHasKey(TinyInteger::NAME, Type::getTypesMap()); - $this->assertSame('tinyinteger', Schema::getColumnType('test', 'test_column')); + $this->assertSame('tinyint', Schema::getColumnType('test', 'test_column')); } public function testRegisterCustomDoctrineTypeASecondTime() @@ -85,7 +85,7 @@ public function testRegisterCustomDoctrineTypeASecondTime() $blueprint->build($this->getConnection(), new SQLiteGrammar); $this->assertArrayHasKey(TinyInteger::NAME, Type::getTypesMap()); - $this->assertSame('tinyinteger', Schema::getColumnType('test', 'test_column')); + $this->assertSame('tinyint', Schema::getColumnType('test', 'test_column')); } public function testChangeToTextColumn() From 8d882eb90115c3e4ff4fea2408a93293d54eeaf8 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 31 Oct 2023 16:14:00 +0330 Subject: [PATCH 02/52] remove unused imports --- tests/Database/DatabaseSchemaBuilderTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Database/DatabaseSchemaBuilderTest.php b/tests/Database/DatabaseSchemaBuilderTest.php index 6a516a4f5c2e..1abd8506a35d 100755 --- a/tests/Database/DatabaseSchemaBuilderTest.php +++ b/tests/Database/DatabaseSchemaBuilderTest.php @@ -2,8 +2,6 @@ namespace Illuminate\Tests\Database; -use Doctrine\DBAL\Schema\Column; -use Doctrine\DBAL\Types\Type; use Illuminate\Database\Connection; use Illuminate\Database\Query\Processors\Processor; use Illuminate\Database\Schema\Builder; From c37884d16ef2b9128f956019450840b447a89c87 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 31 Oct 2023 16:19:43 +0330 Subject: [PATCH 03/52] fix tests --- .../Database/DatabaseSchemaBlueprintTest.php | 39 +------------------ 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php index 66a217a02f15..5e12a450002f 100644 --- a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php @@ -52,11 +52,7 @@ public function testRenamingAndChangingColumnsWork() 'CREATE TABLE users (name VARCHAR NOT NULL, age INTEGER NOT NULL)', 'INSERT INTO users (name, age) SELECT name, age FROM __temp__users', 'DROP TABLE __temp__users', - 'CREATE TEMPORARY TABLE __temp__users AS SELECT name, age FROM users', - 'DROP TABLE users', - 'CREATE TABLE users (first_name VARCHAR NOT NULL, age VARCHAR NOT NULL)', - 'INSERT INTO users (first_name, age) SELECT name, age FROM __temp__users', - 'DROP TABLE __temp__users', + 'alter table "users" rename column "name" to "first_name"', ], ]; @@ -634,39 +630,6 @@ public function testDropIndexOnColumnChangeWorks() ); } - public function testItEnsuresDroppingMultipleColumnsIsAvailable() - { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessage("SQLite doesn't support multiple calls to dropColumn / renameColumn in a single modification."); - - Schema::table('users', function (Blueprint $table) { - $table->dropColumn('name'); - $table->dropColumn('email'); - }); - } - - public function testItEnsuresRenamingMultipleColumnsIsAvailable() - { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessage("SQLite doesn't support multiple calls to dropColumn / renameColumn in a single modification."); - - Schema::table('users', function (Blueprint $table) { - $table->renameColumn('name', 'first_name'); - $table->renameColumn('name2', 'last_name'); - }); - } - - public function testItEnsuresRenamingAndDroppingMultipleColumnsIsAvailable() - { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessage("SQLite doesn't support multiple calls to dropColumn / renameColumn in a single modification."); - - Schema::table('users', function (Blueprint $table) { - $table->dropColumn('name'); - $table->renameColumn('name2', 'last_name'); - }); - } - public function testItDoesNotSetPrecisionHigherThanSupportedWhenRenamingTimestamps() { Schema::create('users', function (Blueprint $table) { From 03469409f6dbd01376f9340c33812ae7aea64377 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 31 Oct 2023 16:23:20 +0330 Subject: [PATCH 04/52] fix tests --- tests/Integration/Database/DBAL/TimestampTypeTest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/Integration/Database/DBAL/TimestampTypeTest.php b/tests/Integration/Database/DBAL/TimestampTypeTest.php index a29659febdae..32676ebc82aa 100644 --- a/tests/Integration/Database/DBAL/TimestampTypeTest.php +++ b/tests/Integration/Database/DBAL/TimestampTypeTest.php @@ -40,8 +40,7 @@ public function testChangeDatetimeColumnToTimestampColumn() // Only Postgres and MySQL actually have a timestamp type $this->assertSame( match ($this->driver) { - 'mysql' => 'timestamp', - 'pgsql' => 'timestamp without time zone', + 'mysql', 'pgsql' => 'timestamp', default => 'datetime', }, Schema::getColumnType('test', 'datetime_to_timestamp') @@ -62,7 +61,7 @@ public function testChangeTimestampColumnToDatetimeColumn() // Postgres only has a timestamp type $this->assertSame( match ($this->driver) { - 'pgsql' => 'timestamp without time zone', + 'pgsql' => 'timestamp', 'sqlsrv' => 'datetime2', default => 'datetime', }, From ba31106361cb85493f02f9bb6043ec9aeda5e507 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 31 Oct 2023 16:46:50 +0330 Subject: [PATCH 05/52] remove mysql 5.7 tests --- .github/workflows/databases.yml | 45 --------------------------------- 1 file changed, 45 deletions(-) diff --git a/.github/workflows/databases.yml b/.github/workflows/databases.yml index 37ffbe3c2cae..5dd8e4046571 100644 --- a/.github/workflows/databases.yml +++ b/.github/workflows/databases.yml @@ -8,51 +8,6 @@ on: pull_request: jobs: - mysql_57: - runs-on: ubuntu-22.04 - - services: - mysql: - image: mysql:5.7 - env: - MYSQL_ALLOW_EMPTY_PASSWORD: yes - MYSQL_DATABASE: forge - ports: - - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 - - strategy: - fail-fast: true - - name: MySQL 5.7 - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 8.2 - extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_mysql - tools: composer:v2 - coverage: none - - - name: Install dependencies - uses: nick-fields/retry@v2 - with: - timeout_minutes: 5 - max_attempts: 5 - command: composer update --prefer-stable --prefer-dist --no-interaction --no-progress - - - name: Execute tests - run: vendor/bin/phpunit tests/Integration/Database - env: - DB_CONNECTION: mysql - DB_USERNAME: root - mysql_8: runs-on: ubuntu-22.04 From ca78589e17ffd16592d0aea42317c803de80ab1d Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 31 Oct 2023 19:26:43 +0330 Subject: [PATCH 06/52] fix facade doctype --- src/Illuminate/Support/Facades/DB.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Illuminate/Support/Facades/DB.php b/src/Illuminate/Support/Facades/DB.php index d9de5c9a4581..3ed7fa4d720a 100755 --- a/src/Illuminate/Support/Facades/DB.php +++ b/src/Illuminate/Support/Facades/DB.php @@ -62,7 +62,6 @@ * @method static \Illuminate\Database\Connection useWriteConnectionWhenReading(bool $value = true) * @method static bool isDoctrineAvailable() * @method static bool usingNativeSchemaOperations() - * @method static \Doctrine\DBAL\Schema\Column getDoctrineColumn(string $table, string $column) * @method static \Doctrine\DBAL\Schema\AbstractSchemaManager getDoctrineSchemaManager() * @method static \Doctrine\DBAL\Connection getDoctrineConnection() * @method static \PDO getPdo() From f55b31e2d0bc5c703250603619a56b56895ba12d Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 31 Oct 2023 21:43:15 +0330 Subject: [PATCH 07/52] use native column modifying by default --- src/Illuminate/Database/Schema/Builder.php | 4 +- .../Database/DBAL/TimestampTypeTest.php | 2 +- .../Database/DatabaseSchemaBlueprintTest.php | 55 ++++--------------- .../Database/SchemaBuilderTest.php | 8 +-- 4 files changed, 17 insertions(+), 52 deletions(-) diff --git a/src/Illuminate/Database/Schema/Builder.php b/src/Illuminate/Database/Schema/Builder.php index ae8a6707c176..b8c5ee8c9875 100755 --- a/src/Illuminate/Database/Schema/Builder.php +++ b/src/Illuminate/Database/Schema/Builder.php @@ -46,11 +46,11 @@ class Builder public static $defaultMorphKeyType = 'int'; /** - * Indicates whether Doctrine DBAL usage will be prevented if possible when dropping, renaming, and modifying columns. + * Indicates whether Doctrine DBAL usage will be prevented if possible when modifying columns. * * @var bool */ - public static $alwaysUsesNativeSchemaOperationsIfPossible = false; + public static $alwaysUsesNativeSchemaOperationsIfPossible = true; /** * Create a new database Schema manager. diff --git a/tests/Integration/Database/DBAL/TimestampTypeTest.php b/tests/Integration/Database/DBAL/TimestampTypeTest.php index 32676ebc82aa..dd4aab23f183 100644 --- a/tests/Integration/Database/DBAL/TimestampTypeTest.php +++ b/tests/Integration/Database/DBAL/TimestampTypeTest.php @@ -85,7 +85,7 @@ public function testChangeStringColumnToTimestampColumn() $queries = $blueprint->toSql($this->getConnection(), $this->getConnection()->getSchemaGrammar()); - $expected = ['ALTER TABLE test CHANGE string_to_timestamp string_to_timestamp TIMESTAMP NULL DEFAULT NULL']; + $expected = ['alter table `test` modify `string_to_timestamp` timestamp null']; $this->assertEquals($expected, $queries); } diff --git a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php index 5e12a450002f..e5375b7b7d16 100644 --- a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php @@ -16,10 +16,6 @@ class DatabaseSchemaBlueprintTest extends TestCase { protected function setUp(): void { - $this->beforeApplicationDestroyed(function () { - Schema::useNativeSchemaOperationsIfPossible(false); - }); - parent::setUp(); } @@ -64,8 +60,6 @@ public function testRenamingColumnsWithoutDoctrineWorks() $connection = DB::connection(); $schema = $connection->getSchemaBuilder(); - $schema->useNativeSchemaOperationsIfPossible(); - $base = new Blueprint('users', function ($table) { $table->renameColumn('name', 'new_name'); }); @@ -102,8 +96,6 @@ public function testDroppingColumnsWithoutDoctrineWorks() $connection = DB::connection(); $schema = $connection->getSchemaBuilder(); - $schema->useNativeSchemaOperationsIfPossible(); - $blueprint = new Blueprint('users', function ($table) { $table->dropColumn('name'); }); @@ -116,8 +108,6 @@ public function testNativeColumnModifyingOnMySql() $connection = DB::connection(); $schema = $connection->getSchemaBuilder(); - $schema->useNativeSchemaOperationsIfPossible(); - $blueprint = new Blueprint('users', function ($table) { $table->double('amount', 6, 2)->nullable()->invisible()->after('name')->change(); $table->timestamp('added_at', 4)->nullable(false)->useCurrent()->useCurrentOnUpdate()->change(); @@ -144,8 +134,6 @@ public function testNativeColumnModifyingOnPostgreSql() $connection = DB::connection(); $schema = $connection->getSchemaBuilder(); - $schema->useNativeSchemaOperationsIfPossible(); - $blueprint = new Blueprint('users', function ($table) { $table->integer('code')->autoIncrement()->from(10)->comment('my comment')->change(); }); @@ -220,8 +208,6 @@ public function testNativeColumnModifyingOnSqlServer() $connection = DB::connection(); $schema = $connection->getSchemaBuilder(); - $schema->useNativeSchemaOperationsIfPossible(); - $blueprint = new Blueprint('users', function ($table) { $table->timestamp('added_at', 4)->nullable(false)->useCurrent()->change(); }); @@ -438,11 +424,7 @@ public function testAddUniqueIndexWithoutNameWorks() $expected = [ [ - 'CREATE TEMPORARY TABLE __temp__users AS SELECT name FROM users', - 'DROP TABLE users', - 'CREATE TABLE users (name VARCHAR(255) DEFAULT NULL)', - 'INSERT INTO users (name) SELECT name FROM __temp__users', - 'DROP TABLE __temp__users', + 'alter table `users` modify `name` varchar(255) null', 'alter table `users` add unique `users_name_unique`(`name`)', ], ]; @@ -457,13 +439,9 @@ public function testAddUniqueIndexWithoutNameWorks() $expected = [ [ - - 'CREATE TEMPORARY TABLE __temp__users AS SELECT name FROM users', - 'DROP TABLE users', - 'CREATE TABLE users (name VARCHAR(255) DEFAULT NULL)', - 'INSERT INTO users (name) SELECT name FROM __temp__users', - 'DROP TABLE __temp__users', + 'alter table "users" alter column "name" type varchar(255), alter column "name" drop not null, alter column "name" drop default, alter column "name" drop identity if exists', 'alter table "users" add constraint "users_name_unique" unique ("name")', + 'comment on column "users"."name" is NULL', ], ]; @@ -496,11 +474,8 @@ public function testAddUniqueIndexWithoutNameWorks() $expected = [ [ - 'CREATE TEMPORARY TABLE __temp__users AS SELECT name FROM users', - 'DROP TABLE users', - 'CREATE TABLE users (name VARCHAR(255) DEFAULT NULL)', - 'INSERT INTO users (name) SELECT name FROM __temp__users', - 'DROP TABLE __temp__users', + "DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE [dbo].[users] DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID('[dbo].[users]') AND [name] in ('name') AND [default_object_id] <> 0;EXEC(@sql)", + 'alter table "users" alter column "name" nvarchar(255) null', 'create unique index "users_name_unique" on "users" ("name")', ], ]; @@ -522,11 +497,7 @@ public function testAddUniqueIndexWithNameWorks() $expected = [ [ - 'CREATE TEMPORARY TABLE __temp__users AS SELECT name FROM users', - 'DROP TABLE users', - 'CREATE TABLE users (name VARCHAR(255) DEFAULT NULL)', - 'INSERT INTO users (name) SELECT name FROM __temp__users', - 'DROP TABLE __temp__users', + 'alter table `users` modify `name` varchar(255) null', 'alter table `users` add unique `index1`(`name`)', ], ]; @@ -540,12 +511,9 @@ public function testAddUniqueIndexWithNameWorks() $queries = $blueprintPostgres->toSql(DB::connection(), new PostgresGrammar); $expected = [ - 'CREATE TEMPORARY TABLE __temp__users AS SELECT name FROM users', - 'DROP TABLE users', - 'CREATE TABLE users (name INTEGER UNSIGNED DEFAULT NULL)', - 'INSERT INTO users (name) SELECT name FROM __temp__users', - 'DROP TABLE __temp__users', + 'alter table "users" alter column "name" type integer, alter column "name" drop not null, alter column "name" drop default, alter column "name" drop identity if exists', 'alter table "users" add constraint "index1" unique ("name")', + 'comment on column "users"."name" is NULL', ]; $this->assertEquals($expected, $queries); @@ -574,11 +542,8 @@ public function testAddUniqueIndexWithNameWorks() $queries = $blueprintSqlServer->toSql(DB::connection(), new SqlServerGrammar); $expected = [ - 'CREATE TEMPORARY TABLE __temp__users AS SELECT name FROM users', - 'DROP TABLE users', - 'CREATE TABLE users (name INTEGER UNSIGNED DEFAULT NULL)', - 'INSERT INTO users (name) SELECT name FROM __temp__users', - 'DROP TABLE __temp__users', + "DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE [dbo].[users] DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID('[dbo].[users]') AND [name] in ('name') AND [default_object_id] <> 0;EXEC(@sql)", + 'alter table "users" alter column "name" int null', 'create unique index "index1" on "users" ("name")', ]; diff --git a/tests/Integration/Database/SchemaBuilderTest.php b/tests/Integration/Database/SchemaBuilderTest.php index 0b21e7df84e9..06b3bb42074f 100644 --- a/tests/Integration/Database/SchemaBuilderTest.php +++ b/tests/Integration/Database/SchemaBuilderTest.php @@ -105,9 +105,9 @@ public function testChangeToTextColumn() $queries = $blueprint->toSql($this->getConnection(), $this->getConnection()->getSchemaGrammar()); - $uppercase = strtoupper($type); + $uppercase = strtolower($type); - $expected = ["ALTER TABLE test CHANGE test_column test_column $uppercase NOT NULL"]; + $expected = ["alter table `test` modify `test_column` $uppercase not null"]; $this->assertEquals($expected, $queries); } @@ -130,9 +130,9 @@ public function testChangeTextColumnToTextColumn() $queries = $blueprint->toSql($this->getConnection(), $this->getConnection()->getSchemaGrammar()); - $uppercase = strtoupper($type); + $uppercase = strtolower($type); - $expected = ["ALTER TABLE test CHANGE test_column test_column $uppercase NOT NULL"]; + $expected = ["alter table `test` modify `test_column` $uppercase not null"]; $this->assertEquals($expected, $queries); } From 90d1f8ac5c1af71d6520c344aa61aefc0d92d861 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 31 Oct 2023 21:48:47 +0330 Subject: [PATCH 08/52] fix tests --- tests/Integration/Database/DBAL/TimestampTypeTest.php | 1 - .../MySql/DatabaseMySqlSchemaBuilderAlterTableWithEnumTest.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/Integration/Database/DBAL/TimestampTypeTest.php b/tests/Integration/Database/DBAL/TimestampTypeTest.php index dd4aab23f183..f73d79b72f41 100644 --- a/tests/Integration/Database/DBAL/TimestampTypeTest.php +++ b/tests/Integration/Database/DBAL/TimestampTypeTest.php @@ -62,7 +62,6 @@ public function testChangeTimestampColumnToDatetimeColumn() $this->assertSame( match ($this->driver) { 'pgsql' => 'timestamp', - 'sqlsrv' => 'datetime2', default => 'datetime', }, Schema::getColumnType('test', 'timestamp_to_datetime') diff --git a/tests/Integration/Database/MySql/DatabaseMySqlSchemaBuilderAlterTableWithEnumTest.php b/tests/Integration/Database/MySql/DatabaseMySqlSchemaBuilderAlterTableWithEnumTest.php index 907ba4998df4..39c79369eb2d 100644 --- a/tests/Integration/Database/MySql/DatabaseMySqlSchemaBuilderAlterTableWithEnumTest.php +++ b/tests/Integration/Database/MySql/DatabaseMySqlSchemaBuilderAlterTableWithEnumTest.php @@ -39,7 +39,7 @@ public function testRenameColumnOnTableWithEnum() public function testChangeColumnOnTableWithEnum() { Schema::table('users', function (Blueprint $table) { - $table->unsignedInteger('age')->charset('')->change(); + $table->unsignedInteger('age')->change(); }); $this->assertSame('int', Schema::getColumnType('users', 'age')); From 8c39f63e3f318f3729614480af4e81318d7e506b Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 9 Nov 2023 02:20:07 +0330 Subject: [PATCH 09/52] fix tests --- tests/Integration/Database/EloquentWhereHasMorphTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/Database/EloquentWhereHasMorphTest.php b/tests/Integration/Database/EloquentWhereHasMorphTest.php index 73dffc85cb5f..1f67d1f89ef9 100644 --- a/tests/Integration/Database/EloquentWhereHasMorphTest.php +++ b/tests/Integration/Database/EloquentWhereHasMorphTest.php @@ -153,7 +153,7 @@ public function testWhereHasMorphWithOwnerKey() }); Schema::table('comments', function (Blueprint $table) { - $table->string('commentable_id')->change(); + $table->string('commentable_id')->nullable()->change(); }); Post::where('id', 1)->update(['slug' => 'foo']); From 45310fdd40c3ea7cc0a92b8e5d364158e98e2cd8 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 9 Nov 2023 03:05:20 +0330 Subject: [PATCH 10/52] wip --- src/Illuminate/Database/Connection.php | 2 +- src/Illuminate/Database/Schema/Builder.php | 2 +- src/Illuminate/Database/composer.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Database/Connection.php b/src/Illuminate/Database/Connection.php index ff84562f728c..70c4d5bd8e2b 100755 --- a/src/Illuminate/Database/Connection.php +++ b/src/Illuminate/Database/Connection.php @@ -1219,7 +1219,7 @@ public function isDoctrineAvailable() } /** - * Indicates whether native alter operations will be used when dropping, renaming, or modifying columns, even if Doctrine DBAL is installed. + * Indicates whether native alter operations will be used when modifying columns, even if Doctrine DBAL is installed. * * @return bool */ diff --git a/src/Illuminate/Database/Schema/Builder.php b/src/Illuminate/Database/Schema/Builder.php index b8c5ee8c9875..6fc2c7a23caf 100755 --- a/src/Illuminate/Database/Schema/Builder.php +++ b/src/Illuminate/Database/Schema/Builder.php @@ -113,7 +113,7 @@ public static function morphUsingUlids() } /** - * Attempt to use native schema operations for dropping, renaming, and modifying columns, even if Doctrine DBAL is installed. + * Attempt to use native schema operations for modifying columns, even if Doctrine DBAL is installed. * * @param bool $value * @return void diff --git a/src/Illuminate/Database/composer.json b/src/Illuminate/Database/composer.json index c840493bd621..2e2093c14e77 100644 --- a/src/Illuminate/Database/composer.json +++ b/src/Illuminate/Database/composer.json @@ -36,7 +36,7 @@ }, "suggest": { "ext-filter": "Required to use the Postgres database driver.", - "doctrine/dbal": "Required to rename columns and drop SQLite columns (^4.0).", + "doctrine/dbal": "Required to modify SQLite columns (^4.0).", "fakerphp/faker": "Required to use the eloquent factory builder (^1.21).", "illuminate/console": "Required to use the database commands (^11.0).", "illuminate/events": "Required to use the observers with Eloquent (^11.0).", From 19d5f9249af9bf288e378418b133f1d33025637f Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 21 Nov 2023 13:37:38 +0330 Subject: [PATCH 11/52] Revert "remove mysql 5.7 tests" This reverts commit ba31106361cb85493f02f9bb6043ec9aeda5e507. --- .github/workflows/databases.yml | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/.github/workflows/databases.yml b/.github/workflows/databases.yml index 5dd8e4046571..37ffbe3c2cae 100644 --- a/.github/workflows/databases.yml +++ b/.github/workflows/databases.yml @@ -8,6 +8,51 @@ on: pull_request: jobs: + mysql_57: + runs-on: ubuntu-22.04 + + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: forge + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + + strategy: + fail-fast: true + + name: MySQL 5.7 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.2 + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_mysql + tools: composer:v2 + coverage: none + + - name: Install dependencies + uses: nick-fields/retry@v2 + with: + timeout_minutes: 5 + max_attempts: 5 + command: composer update --prefer-stable --prefer-dist --no-interaction --no-progress + + - name: Execute tests + run: vendor/bin/phpunit tests/Integration/Database + env: + DB_CONNECTION: mysql + DB_USERNAME: root + mysql_8: runs-on: ubuntu-22.04 From 61208ea6a24d1c07d470c835283d57be32098c1e Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 21 Nov 2023 13:39:34 +0330 Subject: [PATCH 12/52] support native column renaming on MySQL 5.7 --- src/Illuminate/Database/Connection.php | 10 +++++ .../Database/Schema/Grammars/MySqlGrammar.php | 43 +++++++++++++++++++ .../Database/DatabaseSchemaBlueprintTest.php | 24 ++++++++++- .../Database/DatabaseSchemaBlueprintTest.php | 9 +++- 4 files changed, 83 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Database/Connection.php b/src/Illuminate/Database/Connection.php index 70c4d5bd8e2b..dc5b8f4f7de2 100755 --- a/src/Illuminate/Database/Connection.php +++ b/src/Illuminate/Database/Connection.php @@ -1708,6 +1708,16 @@ public function withTablePrefix(Grammar $grammar) return $grammar; } + /** + * Get the server version for the connection. + * + * @return string + */ + public function getServerVersion(): string + { + return $this->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION); + } + /** * Register a connection resolver. * diff --git a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php index 2017f5dc0da4..2aea6ad9bb4e 100755 --- a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php @@ -5,6 +5,7 @@ use Illuminate\Database\Connection; use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Schema\ColumnDefinition; use Illuminate\Support\Fluent; use RuntimeException; @@ -230,6 +231,48 @@ public function compileAutoIncrementStartingValues(Blueprint $blueprint, Fluent } } + /** + * 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(), '8.0.3', '<')) { + $column = collect($connection->getSchemaBuilder()->getColumns($blueprint->getTable())) + ->firstWhere('name', $command->from); + + $modifiers = $this->addModifiers($column['type'], $blueprint, new ColumnDefinition([ + 'change' => true, + 'type' => match($column['type_name']) { + 'bigint' => 'bigInteger', + 'int' => 'integer', + 'mediumint' => 'mediumInteger', + 'smallint' => 'smallInteger', + 'tinyint' => 'tinyInteger', + default => $column['type_name'], + }, + 'nullable' => $column['nullable'], + 'default' => $column['default'], + 'autoIncrement' => $column['auto_increment'], + 'collation' => $column['collation'], + 'comment' => $column['comment'], + ])); + + return sprintf('alter table %s change %s %s %s', + $this->wrapTable($blueprint), + $this->wrap($command->from), + $this->wrap($command->to), + $modifiers + ); + } + + return parent::compileRenameColumn($blueprint, $command, $connection); + } + /** * Compile a change column command into a series of SQL statements. * diff --git a/tests/Database/DatabaseSchemaBlueprintTest.php b/tests/Database/DatabaseSchemaBlueprintTest.php index 884434ddc642..7ff5586e3cff 100755 --- a/tests/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Database/DatabaseSchemaBlueprintTest.php @@ -180,7 +180,7 @@ public function testRenameColumnWithoutDoctrine() }); $connection = m::mock(Connection::class); - $connection->shouldReceive('usingNativeSchemaOperations')->andReturn(true); + $connection->shouldReceive('getServerVersion')->andReturn('8.0.4'); $blueprint = clone $base; $this->assertEquals(['alter table `users` rename column `foo` to `bar`'], $blueprint->toSql($connection, new MySqlGrammar)); @@ -195,6 +195,27 @@ public function testRenameColumnWithoutDoctrine() $this->assertEquals(['sp_rename \'"users"."foo"\', "bar", \'COLUMN\''], $blueprint->toSql($connection, new SqlServerGrammar)); } + public function testNativeRenameColumnOnMysql57() + { + $blueprint = new Blueprint('users', function ($table) { + $table->renameColumn('name', 'title'); + $table->renameColumn('id', 'key'); + }); + + $connection = m::mock(Connection::class); + $connection->shouldReceive('getServerVersion')->andReturn('5.7'); + $connection->shouldReceive('getSchemaBuilder->getColumns')->andReturn([ + ['name' => 'name', 'type' => 'varchar(255)', 'type_name' => 'varchar', 'nullable' => true, 'collation' => 'utf8mb4_unicode_ci', 'default' => 'foo', 'comment' => null, 'auto_increment' => false], + ['name' => 'id', 'type' => 'bigint unsigned', 'type_name' => 'bigint', 'nullable' => false, 'collation' => null, 'default' => null, 'comment' => 'lorem ipsum', 'auto_increment' => true] + ]); + + $this->assertEquals([ + "alter table `users` change `name` `title` varchar(255) collate 'utf8mb4_unicode_ci' null default 'foo'", + "alter table `users` change `id` `key` bigint unsigned not null auto_increment primary key comment 'lorem ipsum'", + ], $blueprint->toSql($connection, new MySqlGrammar)); + + } + public function testDropColumnWithoutDoctrine() { $base = new Blueprint('users', function ($table) { @@ -202,7 +223,6 @@ public function testDropColumnWithoutDoctrine() }); $connection = m::mock(Connection::class); - $connection->shouldReceive('usingNativeSchemaOperations')->andReturn(true); $blueprint = clone $base; $this->assertEquals(['alter table `users` drop `foo`'], $blueprint->toSql($connection, new MySqlGrammar)); diff --git a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php index e5375b7b7d16..2b73ce0d3520 100644 --- a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php @@ -60,12 +60,19 @@ public function testRenamingColumnsWithoutDoctrineWorks() $connection = DB::connection(); $schema = $connection->getSchemaBuilder(); + $schema->create('users', function ($table) { + $table->string('name'); + }); + $base = new Blueprint('users', function ($table) { $table->renameColumn('name', 'new_name'); }); $blueprint = clone $base; - $this->assertEquals(['alter table `users` rename column `name` to `new_name`'], $blueprint->toSql($connection, new MySqlGrammar)); + $this->assertContains($blueprint->toSql($connection, new MySqlGrammar), [ + ['alter table `users` rename column `name` to `new_name`'], // MySQL 8.0 + ['alter table `users` change `name` `new_name` varchar null'], // MySQL 5.7 + ]); $blueprint = clone $base; $this->assertEquals(['alter table "users" rename column "name" to "new_name"'], $blueprint->toSql($connection, new PostgresGrammar)); From 41837caecbf2091d900579bdf76e2b81ced2b765 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 21 Nov 2023 13:45:18 +0330 Subject: [PATCH 13/52] fix style --- src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php | 2 +- tests/Database/DatabaseSchemaBlueprintTest.php | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php index 2aea6ad9bb4e..dcc1d2464af3 100755 --- a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php @@ -247,7 +247,7 @@ public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Conne $modifiers = $this->addModifiers($column['type'], $blueprint, new ColumnDefinition([ 'change' => true, - 'type' => match($column['type_name']) { + 'type' => match ($column['type_name']) { 'bigint' => 'bigInteger', 'int' => 'integer', 'mediumint' => 'mediumInteger', diff --git a/tests/Database/DatabaseSchemaBlueprintTest.php b/tests/Database/DatabaseSchemaBlueprintTest.php index 7ff5586e3cff..4882fbb0fc69 100755 --- a/tests/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Database/DatabaseSchemaBlueprintTest.php @@ -206,14 +206,13 @@ public function testNativeRenameColumnOnMysql57() $connection->shouldReceive('getServerVersion')->andReturn('5.7'); $connection->shouldReceive('getSchemaBuilder->getColumns')->andReturn([ ['name' => 'name', 'type' => 'varchar(255)', 'type_name' => 'varchar', 'nullable' => true, 'collation' => 'utf8mb4_unicode_ci', 'default' => 'foo', 'comment' => null, 'auto_increment' => false], - ['name' => 'id', 'type' => 'bigint unsigned', 'type_name' => 'bigint', 'nullable' => false, 'collation' => null, 'default' => null, 'comment' => 'lorem ipsum', 'auto_increment' => true] + ['name' => 'id', 'type' => 'bigint unsigned', 'type_name' => 'bigint', 'nullable' => false, 'collation' => null, 'default' => null, 'comment' => 'lorem ipsum', 'auto_increment' => true], ]); $this->assertEquals([ "alter table `users` change `name` `title` varchar(255) collate 'utf8mb4_unicode_ci' null default 'foo'", "alter table `users` change `id` `key` bigint unsigned not null auto_increment primary key comment 'lorem ipsum'", ], $blueprint->toSql($connection, new MySqlGrammar)); - } public function testDropColumnWithoutDoctrine() From f612a13b386170b8fcf71fb930cd96daaa1900f1 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 21 Nov 2023 21:06:11 +0330 Subject: [PATCH 14/52] wip --- tests/Integration/Database/DatabaseSchemaBlueprintTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php index 2b73ce0d3520..10b17672b5a1 100644 --- a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php @@ -61,7 +61,7 @@ public function testRenamingColumnsWithoutDoctrineWorks() $schema = $connection->getSchemaBuilder(); $schema->create('users', function ($table) { - $table->string('name'); + $table->string('name')->nullable(); }); $base = new Blueprint('users', function ($table) { From fc2f9afcd6fa36ca36be81c0ced3bfa37815375c Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 28 Nov 2023 16:24:08 +0330 Subject: [PATCH 15/52] wip --- src/Illuminate/Database/Schema/Grammars/ChangeColumn.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Database/Schema/Grammars/ChangeColumn.php b/src/Illuminate/Database/Schema/Grammars/ChangeColumn.php index 7f429c0eccc5..2a745baae0ed 100644 --- a/src/Illuminate/Database/Schema/Grammars/ChangeColumn.php +++ b/src/Illuminate/Database/Schema/Grammars/ChangeColumn.php @@ -32,8 +32,9 @@ public static function compile($grammar, Blueprint $blueprint, Fluent $command, )); } - $schema = $connection->getDoctrineSchemaManager(); - $databasePlatform = $connection->getDoctrineConnection()->getDatabasePlatform(); + $doctrineConnection = $connection->getDoctrineConnection(); + $schema = $doctrineConnection->createSchemaManager(); + $databasePlatform = $doctrineConnection->getDatabasePlatform(); $databasePlatform->registerDoctrineTypeMapping('enum', 'string'); $tableDiff = static::getChangedDiff( From 274fe12fbd2ed4cccc65086cf8759ea710fd22fc Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 28 Nov 2023 16:33:54 +0330 Subject: [PATCH 16/52] remove doctrine usage on DatabaseTruncation --- src/Illuminate/Foundation/Testing/DatabaseTruncation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Testing/DatabaseTruncation.php b/src/Illuminate/Foundation/Testing/DatabaseTruncation.php index cea2d8d09250..4aaa9a96790c 100644 --- a/src/Illuminate/Foundation/Testing/DatabaseTruncation.php +++ b/src/Illuminate/Foundation/Testing/DatabaseTruncation.php @@ -83,7 +83,7 @@ protected function truncateTablesForConnection(ConnectionInterface $connection, $connection->unsetEventDispatcher(); - collect(static::$allTables[$name] ??= $connection->getDoctrineSchemaManager()->listTableNames()) + collect(static::$allTables[$name] ??= array_column($connection->getSchemaBuilder()->getTables(), 'name')) ->when( property_exists($this, 'tablesToTruncate'), fn ($tables) => $tables->intersect($this->tablesToTruncate), From 73754a431f4abe9bdef5134774038e3c570c1413 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 10 Dec 2023 15:38:10 +0330 Subject: [PATCH 17/52] wip --- tests/Database/DatabaseSchemaBlueprintTest.php | 4 ++-- tests/Integration/Database/DatabaseSchemaBlueprintTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Database/DatabaseSchemaBlueprintTest.php b/tests/Database/DatabaseSchemaBlueprintTest.php index 4882fbb0fc69..977f9a8f2b60 100755 --- a/tests/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Database/DatabaseSchemaBlueprintTest.php @@ -173,7 +173,7 @@ public function testRemoveColumn() $this->assertEquals(['alter table `users` add `foo` varchar(255) not null'], $blueprint->toSql($connection, new MySqlGrammar)); } - public function testRenameColumnWithoutDoctrine() + public function testRenameColumn() { $base = new Blueprint('users', function ($table) { $table->renameColumn('foo', 'bar'); @@ -215,7 +215,7 @@ public function testNativeRenameColumnOnMysql57() ], $blueprint->toSql($connection, new MySqlGrammar)); } - public function testDropColumnWithoutDoctrine() + public function testDropColumn() { $base = new Blueprint('users', function ($table) { $table->dropColumn('foo'); diff --git a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php index 10b17672b5a1..91a754c1f369 100644 --- a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php @@ -55,7 +55,7 @@ public function testRenamingAndChangingColumnsWork() $this->assertContains($queries, $expected); } - public function testRenamingColumnsWithoutDoctrineWorks() + public function testRenamingColumnsWorks() { $connection = DB::connection(); $schema = $connection->getSchemaBuilder(); @@ -98,7 +98,7 @@ public function testRenamingColumnsWithoutDoctrineWorks() $this->assertTrue($schema->hasColumns('test', ['bar', 'qux'])); } - public function testDroppingColumnsWithoutDoctrineWorks() + public function testDroppingColumnsWorks() { $connection = DB::connection(); $schema = $connection->getSchemaBuilder(); From f0822144036b2044096f680fdea9ed1ce26bfdc8 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 10 Dec 2023 16:34:25 +0330 Subject: [PATCH 18/52] rename index on sqlite --- .../Schema/Grammars/SQLiteGrammar.php | 30 +++++++++++-------- .../Database/DatabaseSchemaBlueprintTest.php | 4 +-- .../Database/DatabaseSchemaBuilderTest.php | 8 +++-- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php index b47771d50f80..0a0e2bf4c057 100755 --- a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php @@ -2,10 +2,10 @@ namespace Illuminate\Database\Schema\Grammars; -use Doctrine\DBAL\Schema\Index; use Illuminate\Database\Connection; use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Schema\IndexDefinition; use Illuminate\Support\Arr; use Illuminate\Support\Fluent; use RuntimeException; @@ -441,26 +441,32 @@ public function compileRename(Blueprint $blueprint, Fluent $command) */ public function compileRenameIndex(Blueprint $blueprint, Fluent $command, Connection $connection) { - $schemaManager = $connection->getDoctrineSchemaManager(); + $indexes = $connection->getSchemaBuilder()->getIndexes($blueprint->getTable()); - $indexes = $schemaManager->listTableIndexes($this->getTablePrefix().$blueprint->getTable()); - - $index = Arr::get($indexes, $command->from); + $index = Arr::first($indexes, fn ($index) => $index['name'] === $command->from); if (! $index) { throw new RuntimeException("Index [{$command->from}] does not exist."); } - $newIndex = new Index( - $command->to, $index->getColumns(), $index->isUnique(), - $index->isPrimary(), $index->getFlags(), $index->getOptions() - ); + if ($index['primary']) { + throw new RuntimeException('Sqlite does not support alter primary key.'); + } - $platform = $connection->getDoctrineConnection()->getDatabasePlatform(); + if ($index['unique']) { + return [ + $this->compileDropUnique($blueprint, new IndexDefinition(['index' => $index['name']])), + $this->compileUnique($blueprint, + new IndexDefinition(['index' => $command->to, 'columns' => $index['columns']]) + ), + ]; + } return [ - $platform->getDropIndexSQL($command->from, $this->getTablePrefix().$blueprint->getTable()), - $platform->getCreateIndexSQL($newIndex, $this->getTablePrefix().$blueprint->getTable()), + $this->compileDropIndex($blueprint, new IndexDefinition(['index' => $index['name']])), + $this->compileIndex($blueprint, + new IndexDefinition(['index' => $command->to, 'columns' => $index['columns']]) + ), ]; } diff --git a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php index 91a754c1f369..63f696fc6f80 100644 --- a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php @@ -386,8 +386,8 @@ public function testRenameIndexWorks() $queries = $blueprint->toSql(DB::connection(), new SQLiteGrammar); $expected = [ - 'DROP INDEX index1', - 'CREATE INDEX index2 ON users (name)', + 'drop index "index1"', + 'create index "index2" on "users" ("name")', ]; $this->assertEquals($expected, $queries); diff --git a/tests/Integration/Database/DatabaseSchemaBuilderTest.php b/tests/Integration/Database/DatabaseSchemaBuilderTest.php index b016a0124eee..8a377d3616d7 100644 --- a/tests/Integration/Database/DatabaseSchemaBuilderTest.php +++ b/tests/Integration/Database/DatabaseSchemaBuilderTest.php @@ -59,7 +59,9 @@ public function testHasColumnAndIndexWithPrefixIndexDisabled() $table->string('name')->index(); }); - $this->assertArrayHasKey('table1_name_index', $connection->getDoctrineSchemaManager()->listTableIndexes('example_table1')); + $indexes = array_column($connection->getSchemaBuilder()->getIndexes('table1'), 'name'); + + $this->assertContains('table1_name_index', $indexes, 'name'); } public function testHasColumnAndIndexWithPrefixIndexEnabled() @@ -71,6 +73,8 @@ public function testHasColumnAndIndexWithPrefixIndexEnabled() $table->string('name')->index(); }); - $this->assertArrayHasKey('example_table1_name_index', $connection->getDoctrineSchemaManager()->listTableIndexes('example_table1')); + $indexes = array_column($connection->getSchemaBuilder()->getIndexes('table1'), 'name'); + + $this->assertContains('example_table1_name_index', $indexes); } } From 254e099a55bef924315b4bbc96f4f80b731f7711 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 10 Dec 2023 23:16:27 +0330 Subject: [PATCH 19/52] remove doctrine/dbal from db commands --- .../Console/DatabaseInspectionCommand.php | 201 +----------------- .../Database/Console/MonitorCommand.php | 6 +- .../Database/Console/ShowCommand.php | 78 ++++--- .../Database/Console/ShowModelCommand.php | 83 +++----- .../Database/Console/TableCommand.php | 106 ++++----- 5 files changed, 138 insertions(+), 336 deletions(-) diff --git a/src/Illuminate/Database/Console/DatabaseInspectionCommand.php b/src/Illuminate/Database/Console/DatabaseInspectionCommand.php index 42568fc2c02d..85a3ab3bdcc7 100644 --- a/src/Illuminate/Database/Console/DatabaseInspectionCommand.php +++ b/src/Illuminate/Database/Console/DatabaseInspectionCommand.php @@ -2,169 +2,35 @@ namespace Illuminate\Database\Console; -use Doctrine\DBAL\Platforms\AbstractPlatform; use Illuminate\Console\Command; use Illuminate\Database\ConnectionInterface; use Illuminate\Database\MySqlConnection; use Illuminate\Database\PostgresConnection; -use Illuminate\Database\QueryException; use Illuminate\Database\SQLiteConnection; use Illuminate\Database\SqlServerConnection; use Illuminate\Support\Arr; -use Illuminate\Support\Composer; -use Symfony\Component\Process\Exception\ProcessSignaledException; -use Symfony\Component\Process\Exception\RuntimeException; -use Symfony\Component\Process\Process; - -use function Laravel\Prompts\confirm; abstract class DatabaseInspectionCommand extends Command { /** - * A map of database column types. - * - * @var array - */ - protected $typeMappings = [ - 'bit' => 'string', - 'citext' => 'string', - 'enum' => 'string', - 'geometry' => 'string', - 'geomcollection' => 'string', - 'linestring' => 'string', - 'ltree' => 'string', - 'multilinestring' => 'string', - 'multipoint' => 'string', - 'multipolygon' => 'string', - 'point' => 'string', - 'polygon' => 'string', - 'sysname' => 'string', - ]; - - /** - * The Composer instance. - * - * @var \Illuminate\Support\Composer - */ - protected $composer; - - /** - * Create a new command instance. - * - * @param \Illuminate\Support\Composer|null $composer - * @return void - */ - public function __construct(Composer $composer = null) - { - parent::__construct(); - - $this->composer = $composer ?? $this->laravel->make(Composer::class); - } - - /** - * Register the custom Doctrine type mappings for inspection commands. - * - * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform - * @return void - */ - protected function registerTypeMappings(AbstractPlatform $platform) - { - foreach ($this->typeMappings as $type => $value) { - $platform->registerDoctrineTypeMapping($type, $value); - } - } - - /** - * Get a human-readable platform name for the given platform. + * Get a human-readable name for the given connection. * - * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * @param \Illuminate\Database\ConnectionInterface $connection * @param string $database * @return string */ - protected function getPlatformName(AbstractPlatform $platform, $database) - { - return match (class_basename($platform)) { - 'MySQLPlatform' => 'MySQL <= 5', - 'MySQL57Platform' => 'MySQL 5.7', - 'MySQL80Platform' => 'MySQL 8', - 'PostgreSQL100Platform', 'PostgreSQLPlatform' => 'Postgres', - 'SqlitePlatform' => 'SQLite', - 'SQLServerPlatform' => 'SQL Server', - 'SQLServer2012Platform' => 'SQL Server 2012', - default => $database, - }; - } - - /** - * Get the size of a table in bytes. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param string $table - * @return int|null - */ - protected function getTableSize(ConnectionInterface $connection, string $table) + protected function getConnectionName(ConnectionInterface $connection, $database) { return match (true) { - $connection instanceof MySqlConnection => $this->getMySQLTableSize($connection, $table), - $connection instanceof PostgresConnection => $this->getPostgresTableSize($connection, $table), - $connection instanceof SQLiteConnection => $this->getSqliteTableSize($connection, $table), - default => null, + $connection instanceof MySqlConnection && $connection->isMaria() => 'MariaDB', + $connection instanceof MySqlConnection => 'MySQL', + $connection instanceof PostgresConnection => 'PostgreSQL', + $connection instanceof SQLiteConnection => 'SQLite', + $connection instanceof SqlServerConnection => 'SQL Server', + default => $database, }; } - /** - * Get the size of a MySQL table in bytes. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param string $table - * @return mixed - */ - protected function getMySQLTableSize(ConnectionInterface $connection, string $table) - { - $result = $connection->selectOne('SELECT (data_length + index_length) AS size FROM information_schema.TABLES WHERE table_schema = ? AND table_name = ?', [ - $connection->getDatabaseName(), - $table, - ]); - - return Arr::wrap((array) $result)['size']; - } - - /** - * Get the size of a Postgres table in bytes. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param string $table - * @return mixed - */ - protected function getPostgresTableSize(ConnectionInterface $connection, string $table) - { - $result = $connection->selectOne('SELECT pg_total_relation_size(?) AS size;', [ - $table, - ]); - - return Arr::wrap((array) $result)['size']; - } - - /** - * Get the size of a SQLite table in bytes. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param string $table - * @return mixed - */ - protected function getSqliteTableSize(ConnectionInterface $connection, string $table) - { - try { - $result = $connection->selectOne('SELECT SUM(pgsize) AS size FROM dbstat WHERE name=?', [ - $table, - ]); - - return Arr::wrap((array) $result)['size']; - } catch (QueryException) { - return null; - } - } - /** * Get the number of open connections for a database. * @@ -175,8 +41,8 @@ protected function getConnectionCount(ConnectionInterface $connection) { $result = match (true) { $connection instanceof MySqlConnection => $connection->selectOne('show status where variable_name = "threads_connected"'), - $connection instanceof PostgresConnection => $connection->selectOne('select count(*) AS "Value" from pg_stat_activity'), - $connection instanceof SqlServerConnection => $connection->selectOne('SELECT COUNT(*) Value FROM sys.dm_exec_sessions WHERE status = ?', ['running']), + $connection instanceof PostgresConnection => $connection->selectOne('select count(*) as "Value" from pg_stat_activity'), + $connection instanceof SqlServerConnection => $connection->selectOne('select count(*) Value from sys.dm_exec_sessions where status = ?', ['running']), default => null, }; @@ -200,49 +66,4 @@ protected function getConfigFromDatabase($database) return Arr::except(config('database.connections.'.$database), ['password']); } - /** - * Ensure the dependencies for the database commands are available. - * - * @return bool - */ - protected function ensureDependenciesExist() - { - return tap(interface_exists('Doctrine\DBAL\Driver'), function ($dependenciesExist) { - if (! $dependenciesExist && confirm('Inspecting database information requires the Doctrine DBAL (doctrine/dbal) package. Would you like to install it?', default: false)) { - $this->installDependencies(); - } - }); - } - - /** - * Install the command's dependencies. - * - * @return void - * - * @throws \Symfony\Component\Process\Exception\ProcessSignaledException - */ - protected function installDependencies() - { - $command = collect($this->composer->findComposer()) - ->push('require doctrine/dbal:^3.5.1') - ->implode(' '); - - $process = Process::fromShellCommandline($command, null, null, null, null); - - if ('\\' !== DIRECTORY_SEPARATOR && file_exists('/dev/tty') && is_readable('/dev/tty')) { - try { - $process->setTty(true); - } catch (RuntimeException $e) { - $this->components->warn($e->getMessage()); - } - } - - try { - $process->run(fn ($type, $line) => $this->output->write($line)); - } catch (ProcessSignaledException $e) { - if (extension_loaded('pcntl') && $e->getSignal() !== SIGINT) { - throw $e; - } - } - } } diff --git a/src/Illuminate/Database/Console/MonitorCommand.php b/src/Illuminate/Database/Console/MonitorCommand.php index 3dff3158268c..d87a441c015c 100644 --- a/src/Illuminate/Database/Console/MonitorCommand.php +++ b/src/Illuminate/Database/Console/MonitorCommand.php @@ -5,7 +5,6 @@ use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Database\ConnectionResolverInterface; use Illuminate\Database\Events\DatabaseBusy; -use Illuminate\Support\Composer; use Symfony\Component\Console\Attribute\AsCommand; #[AsCommand(name: 'db:monitor')] @@ -46,11 +45,10 @@ class MonitorCommand extends DatabaseInspectionCommand * * @param \Illuminate\Database\ConnectionResolverInterface $connection * @param \Illuminate\Contracts\Events\Dispatcher $events - * @param \Illuminate\Support\Composer $composer */ - public function __construct(ConnectionResolverInterface $connection, Dispatcher $events, Composer $composer) + public function __construct(ConnectionResolverInterface $connection, Dispatcher $events) { - parent::__construct($composer); + parent::__construct(); $this->connection = $connection; $this->events = $events; diff --git a/src/Illuminate/Database/Console/ShowCommand.php b/src/Illuminate/Database/Console/ShowCommand.php index c125c38820e4..136cf487676c 100644 --- a/src/Illuminate/Database/Console/ShowCommand.php +++ b/src/Illuminate/Database/Console/ShowCommand.php @@ -2,12 +2,11 @@ namespace Illuminate\Database\Console; -use Doctrine\DBAL\Schema\AbstractSchemaManager; -use Doctrine\DBAL\Schema\Table; -use Doctrine\DBAL\Schema\View; use Illuminate\Database\ConnectionInterface; use Illuminate\Database\ConnectionResolverInterface; +use Illuminate\Database\Schema\Builder; use Illuminate\Support\Arr; +use Illuminate\Support\Number; use Symfony\Component\Console\Attribute\AsCommand; #[AsCommand(name: 'db:show')] @@ -38,28 +37,22 @@ class ShowCommand extends DatabaseInspectionCommand */ public function handle(ConnectionResolverInterface $connections) { - if (! $this->ensureDependenciesExist()) { - return 1; - } - $connection = $connections->connection($database = $this->input->getOption('database')); - $doctrineConnection = $connection->getDoctrineConnection(); - $schema = $connection->getDoctrineSchemaManager(); - - $this->registerTypeMappings($doctrineConnection->getDatabasePlatform()); + $schema = $connection->getSchemaBuilder(); $data = [ 'platform' => [ 'config' => $this->getConfigFromDatabase($database), - 'name' => $this->getPlatformName($doctrineConnection->getDatabasePlatform(), $database), + 'name' => $this->getConnectionName($connection, $database), + 'version' => $connection->getServerVersion(), 'open_connections' => $this->getConnectionCount($connection), ], 'tables' => $this->tables($connection, $schema), ]; if ($this->option('views')) { - $data['views'] = $this->collectViews($connection, $schema); + $data['views'] = $this->views($connection, $schema); } $this->display($data); @@ -71,17 +64,19 @@ public function handle(ConnectionResolverInterface $connections) * Get information regarding the tables within the database. * * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Doctrine\DBAL\Schema\AbstractSchemaManager $schema + * @param \Illuminate\Database\Schema\Builder $schema * @return \Illuminate\Support\Collection */ - protected function tables(ConnectionInterface $connection, AbstractSchemaManager $schema) + protected function tables(ConnectionInterface $connection, Builder $schema) { - return collect($schema->listTables())->map(fn (Table $table, $index) => [ - 'table' => $table->getName(), - 'size' => $this->getTableSize($connection, $table->getName()), + return collect($schema->getTables())->map(fn ($table) => [ + 'table' => $table['name'], + 'schema' => $table['schema'], + 'size' => $table['size'], 'rows' => $this->option('counts') ? $connection->table($table->getName())->count() : null, - 'engine' => rescue(fn () => $table->getOption('engine'), null, false), - 'comment' => $table->getComment(), + 'engine' => $table['engine'], + 'collation' => $table['collation'], + 'comment' => $table['comment'], ]); } @@ -89,16 +84,16 @@ protected function tables(ConnectionInterface $connection, AbstractSchemaManager * Get information regarding the views within the database. * * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Doctrine\DBAL\Schema\AbstractSchemaManager $schema + * @param \Illuminate\Database\Schema\Builder $schema * @return \Illuminate\Support\Collection */ - protected function collectViews(ConnectionInterface $connection, AbstractSchemaManager $schema) + protected function views(ConnectionInterface $connection, Builder $schema) { - return collect($schema->listViews()) - ->reject(fn (View $view) => str($view->getName()) - ->startsWith(['pg_catalog', 'information_schema', 'spt_'])) - ->map(fn (View $view) => [ - 'view' => $view->getName(), + return collect($schema->getViews()) + ->reject(fn ($view) => str($view['name'])->startsWith(['pg_catalog', 'information_schema', 'spt_'])) + ->map(fn ($view) => [ + 'view' => $view['name'], + 'schema' => $view['schema'], 'rows' => $connection->table($view->getName())->count(), ]); } @@ -139,7 +134,7 @@ protected function displayForCli(array $data) $this->newLine(); - $this->components->twoColumnDetail(''.$platform['name'].''); + $this->components->twoColumnDetail(''.$platform['name'].'', $platform['version']); $this->components->twoColumnDetail('Database', Arr::get($platform['config'], 'database')); $this->components->twoColumnDetail('Host', Arr::get($platform['config'], 'host')); $this->components->twoColumnDetail('Port', Arr::get($platform['config'], 'port')); @@ -149,22 +144,27 @@ protected function displayForCli(array $data) $this->components->twoColumnDetail('Tables', $tables->count()); if ($tableSizeSum = $tables->sum('size')) { - $this->components->twoColumnDetail('Total Size', number_format($tableSizeSum / 1024 / 1024, 2).'MiB'); + $this->components->twoColumnDetail('Total Size', Number::fileSize($tableSizeSum, 2)); } $this->newLine(); if ($tables->isNotEmpty()) { - $this->components->twoColumnDetail('Table', 'Size (MiB)'.($this->option('counts') ? ' / Rows' : '')); + $hasSchema = ! is_null($tables->first()['schema']); + + $this->components->twoColumnDetail( + ($hasSchema ? 'Schema / ' : '').'Table', + 'Size'.($this->option('counts') ? ' / Rows' : '') + ); $tables->each(function ($table) { if ($tableSize = $table['size']) { - $tableSize = number_format($tableSize / 1024 / 1024, 2); + $tableSize = Number::fileSize($tableSize, 2); } $this->components->twoColumnDetail( - $table['table'].($this->output->isVerbose() ? ' '.$table['engine'].'' : null), - ($tableSize ? $tableSize : '—').($this->option('counts') ? ' / '.number_format($table['rows']).'' : '') + ($table['schema'] ? $table['schema'].' / ' : '').$table['table'].($this->output->isVerbose() ? ' '.$table['engine'].'' : null), + ($tableSize ?: '—').($this->option('counts') ? ' / '.Number::format($table['rows']).'' : '') ); if ($this->output->isVerbose()) { @@ -180,9 +180,17 @@ protected function displayForCli(array $data) } if ($views && $views->isNotEmpty()) { - $this->components->twoColumnDetail('View', 'Rows'); + $hasSchema = ! is_null($views->first()['schema']); + + $this->components->twoColumnDetail( + ($hasSchema ? 'Schema / ' : '').'View', + 'Rows' + ); - $views->each(fn ($view) => $this->components->twoColumnDetail($view['view'], number_format($view['rows']))); + $views->each(fn ($view) => $this->components->twoColumnDetail( + ($view['schema'] ? $view['schema'].' / ' : '').$view['view'], + Number::format($view['rows']) + )); $this->newLine(); } diff --git a/src/Illuminate/Database/Console/ShowModelCommand.php b/src/Illuminate/Database/Console/ShowModelCommand.php index 3ef912004e8c..4ab262546c15 100644 --- a/src/Illuminate/Database/Console/ShowModelCommand.php +++ b/src/Illuminate/Database/Console/ShowModelCommand.php @@ -3,9 +3,6 @@ namespace Illuminate\Database\Console; use BackedEnum; -use Doctrine\DBAL\Schema\Column; -use Doctrine\DBAL\Schema\Index; -use Doctrine\DBAL\Types\DecimalType; use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\Relation; @@ -70,10 +67,6 @@ class ShowModelCommand extends DatabaseInspectionCommand */ public function handle() { - if (! $this->ensureDependenciesExist()) { - return 1; - } - $class = $this->qualifyModel($this->argument('model')); try { @@ -81,7 +74,9 @@ public function handle() $class = get_class($model); } catch (BindingResolutionException $e) { - return $this->components->error($e->getMessage()); + $this->components->error($e->getMessage()); + + return 1; } if ($this->option('database')) { @@ -97,6 +92,8 @@ public function handle() $this->getRelations($model), $this->getObservers($model), ); + + return 0; } /** @@ -121,25 +118,23 @@ protected function getPolicy($model) protected function getAttributes($model) { $connection = $model->getConnection(); - $schema = $connection->getDoctrineSchemaManager(); - $this->registerTypeMappings($connection->getDoctrineConnection()->getDatabasePlatform()); - $table = $model->getConnection()->getTablePrefix().$model->getTable(); - $columns = $schema->listTableColumns($table); - $indexes = $schema->listTableIndexes($table); + $schema = $connection->getSchemaBuilder(); + $table = $model->getTable(); + $columns = $schema->getColumns($table); + $indexes = $schema->getIndexes($table); return collect($columns) - ->values() - ->map(fn (Column $column) => [ - 'name' => $column->getName(), - 'type' => $this->getColumnType($column), - 'increments' => $column->getAutoincrement(), - 'nullable' => ! $column->getNotnull(), + ->map(fn ($column) => [ + 'name' => $column['name'], + 'type' => $column['type'], + 'increments' => $column['auto_increment'], + 'nullable' => $column['nullable'], 'default' => $this->getColumnDefault($column, $model), - 'unique' => $this->columnIsUnique($column->getName(), $indexes), - 'fillable' => $model->isFillable($column->getName()), - 'hidden' => $this->attributeIsHidden($column->getName(), $model), + 'unique' => $this->columnIsUnique($column['name'], $indexes), + 'fillable' => $model->isFillable($column['name']), + 'hidden' => $this->attributeIsHidden($column['name'], $model), 'appended' => null, - 'cast' => $this->getCastType($column->getName(), $model), + 'cast' => $this->getCastType($column['name'], $model), ]) ->merge($this->getVirtualAttributes($model, $columns)); } @@ -148,7 +143,7 @@ protected function getAttributes($model) * Get the virtual (non-column) attributes for the given model. * * @param \Illuminate\Database\Eloquent\Model $model - * @param \Doctrine\DBAL\Schema\Column[] $columns + * @param array $columns * @return \Illuminate\Support\Collection */ protected function getVirtualAttributes($model, $columns) @@ -170,7 +165,7 @@ protected function getVirtualAttributes($model, $columns) return []; } }) - ->reject(fn ($cast, $name) => collect($columns)->has($name)) + ->reject(fn ($cast, $name) => collect($columns)->contains('name', $name)) ->map(fn ($cast, $name) => [ 'name' => $name, 'type' => null, @@ -428,45 +423,21 @@ protected function getCastsWithDates($model) ->merge($model->getCasts()); } - /** - * Get the type of the given column. - * - * @param \Doctrine\DBAL\Schema\Column $column - * @return string - */ - protected function getColumnType($column) - { - $name = $column->getType()->getName(); - - $unsigned = $column->getUnsigned() ? ' unsigned' : ''; - - $details = match (get_class($column->getType())) { - DecimalType::class => $column->getPrecision().','.$column->getScale(), - default => $column->getLength(), - }; - - if ($details) { - return sprintf('%s(%s)%s', $name, $details, $unsigned); - } - - return sprintf('%s%s', $name, $unsigned); - } - /** * Get the default value for the given column. * - * @param \Doctrine\DBAL\Schema\Column $column + * @param array $column * @param \Illuminate\Database\Eloquent\Model $model * @return mixed|null */ protected function getColumnDefault($column, $model) { - $attributeDefault = $model->getAttributes()[$column->getName()] ?? null; + $attributeDefault = $model->getAttributes()[$column['name']] ?? null; return match (true) { $attributeDefault instanceof BackedEnum => $attributeDefault->value, $attributeDefault instanceof UnitEnum => $attributeDefault->name, - default => $attributeDefault ?? $column->getDefault(), + default => $attributeDefault ?? $column['default'], }; } @@ -494,14 +465,14 @@ protected function attributeIsHidden($attribute, $model) * Determine if the given attribute is unique. * * @param string $column - * @param \Doctrine\DBAL\Schema\Index[] $indexes + * @param array $indexes * @return bool */ protected function columnIsUnique($column, $indexes) { - return collect($indexes) - ->filter(fn (Index $index) => count($index->getColumns()) === 1 && $index->getColumns()[0] === $column) - ->contains(fn (Index $index) => $index->isUnique()); + return collect($indexes)->contains( + fn ($index) => count($index['columns']) === 1 && $index['columns'][0] === $column && $index['unique'] + ); } /** diff --git a/src/Illuminate/Database/Console/TableCommand.php b/src/Illuminate/Database/Console/TableCommand.php index 1a7407d5791b..6c0075b2ab04 100644 --- a/src/Illuminate/Database/Console/TableCommand.php +++ b/src/Illuminate/Database/Console/TableCommand.php @@ -7,6 +7,8 @@ use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Table; use Illuminate\Database\ConnectionResolverInterface; +use Illuminate\Database\Schema\Builder; +use Illuminate\Support\Arr; use Illuminate\Support\Str; use Symfony\Component\Console\Attribute\AsCommand; @@ -39,36 +41,34 @@ class TableCommand extends DatabaseInspectionCommand */ public function handle(ConnectionResolverInterface $connections) { - if (! $this->ensureDependenciesExist()) { - return 1; - } - $connection = $connections->connection($this->input->getOption('database')); - $schema = $connection->getDoctrineSchemaManager(); + $schema = $connection->getSchemaBuilder(); - $this->registerTypeMappings($connection->getDoctrineConnection()->getDatabasePlatform()); + $tables = $schema->getTables(); - $table = $this->argument('table') ?: select( + $tableName = $this->argument('table') ?: select( 'Which table would you like to inspect?', - collect($schema->listTables())->flatMap(fn (Table $table) => [$table->getName()])->toArray() + array_column($tables, 'name') ); - if (! $schema->tablesExist([$table])) { - return $this->components->warn("Table [{$table}] doesn't exist."); - } + $table = Arr::first($tables, fn ($table) => $table['name'] === $tableName); - $table = $schema->introspectTable($table); + if (! $table) { + $this->components->warn("Table [{$table}] doesn't exist."); - $columns = $this->columns($table); - $indexes = $this->indexes($table); - $foreignKeys = $this->foreignKeys($table); + return 1; + } + + $columns = $this->columns($schema, $table['name']); + $indexes = $this->indexes($schema, $table['name']); + $foreignKeys = $this->foreignKeys($schema, $table['name']); $data = [ 'table' => [ - 'name' => $table->getName(), - 'columns' => $columns->count(), - 'size' => $this->getTableSize($connection, $table->getName()), + 'name' => $table['name'], + 'columns' => count($columns), + 'size' => $table['size'], ], 'columns' => $columns, 'indexes' => $indexes, @@ -83,46 +83,48 @@ public function handle(ConnectionResolverInterface $connections) /** * Get the information regarding the table's columns. * - * @param \Doctrine\DBAL\Schema\Table $table + * @param \Illuminate\Database\Schema\Builder $schema + * @param string $table * @return \Illuminate\Support\Collection */ - protected function columns(Table $table) + protected function columns(Builder $schema, string $table) { - return collect($table->getColumns())->map(fn (Column $column) => [ - 'column' => $column->getName(), + return collect($schema->getColumns($table))->map(fn ($column) => [ + 'column' => $column['name'], 'attributes' => $this->getAttributesForColumn($column), - 'default' => $column->getDefault(), - 'type' => $column->getType()->getName(), + 'default' => $column['default'], + 'type' => $column['type'], ]); } /** * Get the attributes for a table column. * - * @param \Doctrine\DBAL\Schema\Column $column + * @param array $column * @return \Illuminate\Support\Collection */ - protected function getAttributesForColumn(Column $column) + protected function getAttributesForColumn($column) { return collect([ - $column->getAutoincrement() ? 'autoincrement' : null, - 'type' => $column->getType()->getName(), - $column->getUnsigned() ? 'unsigned' : null, - ! $column->getNotNull() ? 'nullable' : null, + $column['type_name'], + $column['auto_increment'] ? 'autoincrement' : null, + $column['nullable'] ? 'nullable' : null, + $column['collation'], ])->filter(); } /** * Get the information regarding the table's indexes. * - * @param \Doctrine\DBAL\Schema\Table $table + * @param \Illuminate\Database\Schema\Builder $schema + * @param string $table * @return \Illuminate\Support\Collection */ - protected function indexes(Table $table) + protected function indexes(Builder $schema, string $table) { - return collect($table->getIndexes())->map(fn (Index $index) => [ - 'name' => $index->getName(), - 'columns' => collect($index->getColumns()), + return collect($schema->getIndexes($table))->map(fn ($index) => [ + 'name' => $index['name'], + 'columns' => collect($index['columns']), 'attributes' => $this->getAttributesForIndex($index), ]); } @@ -130,34 +132,36 @@ protected function indexes(Table $table) /** * Get the attributes for a table index. * - * @param \Doctrine\DBAL\Schema\Index $index + * @param array $index * @return \Illuminate\Support\Collection */ - protected function getAttributesForIndex(Index $index) + protected function getAttributesForIndex($index) { return collect([ - 'compound' => count($index->getColumns()) > 1, - 'unique' => $index->isUnique(), - 'primary' => $index->isPrimary(), - ])->filter()->keys()->map(fn ($attribute) => Str::lower($attribute)); + $index['type'], + count($index['columns']) > 1 ? 'compound' : null , + $index['unique'] && ! $index['primary'] ? 'unique' : null, + $index['primary'] ? 'primary' : null, + ])->filter(); } /** * Get the information regarding the table's foreign keys. * - * @param \Doctrine\DBAL\Schema\Table $table + * @param \Illuminate\Database\Schema\Builder $schema + * @param string $table * @return \Illuminate\Support\Collection */ - protected function foreignKeys(Table $table) + protected function foreignKeys(Builder $schema, string $table) { - return collect($table->getForeignKeys())->map(fn (ForeignKeyConstraint $foreignKey) => [ - 'name' => $foreignKey->getName(), - 'local_table' => $table->getName(), - 'local_columns' => collect($foreignKey->getLocalColumns()), - 'foreign_table' => $foreignKey->getForeignTableName(), - 'foreign_columns' => collect($foreignKey->getForeignColumns()), - 'on_update' => Str::lower(rescue(fn () => $foreignKey->getOption('onUpdate'), 'N/A')), - 'on_delete' => Str::lower(rescue(fn () => $foreignKey->getOption('onDelete'), 'N/A')), + return collect($schema->getForeignKeys($table))->map(fn ($foreignKey) => [ + 'name' => $foreignKey['name'], + 'local_columns' => collect($foreignKey['columns']), + 'foreign_schema' => $foreignKey['foreign_schema'], + 'foreign_table' => $foreignKey['foreign_table'], + 'foreign_columns' => collect($foreignKey['foreign_columns']), + 'on_update' => $foreignKey['on_update'], + 'on_delete' => $foreignKey['on_delete'], ]); } From 49a0c3feac93125f99c9247385f16c637a29962e Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 10 Dec 2023 23:17:53 +0330 Subject: [PATCH 20/52] fix styles --- .../Database/Console/DatabaseInspectionCommand.php | 1 - src/Illuminate/Database/Console/TableCommand.php | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Illuminate/Database/Console/DatabaseInspectionCommand.php b/src/Illuminate/Database/Console/DatabaseInspectionCommand.php index 85a3ab3bdcc7..e6befa5ab635 100644 --- a/src/Illuminate/Database/Console/DatabaseInspectionCommand.php +++ b/src/Illuminate/Database/Console/DatabaseInspectionCommand.php @@ -65,5 +65,4 @@ protected function getConfigFromDatabase($database) return Arr::except(config('database.connections.'.$database), ['password']); } - } diff --git a/src/Illuminate/Database/Console/TableCommand.php b/src/Illuminate/Database/Console/TableCommand.php index 6c0075b2ab04..b60ad892050a 100644 --- a/src/Illuminate/Database/Console/TableCommand.php +++ b/src/Illuminate/Database/Console/TableCommand.php @@ -2,14 +2,9 @@ namespace Illuminate\Database\Console; -use Doctrine\DBAL\Schema\Column; -use Doctrine\DBAL\Schema\ForeignKeyConstraint; -use Doctrine\DBAL\Schema\Index; -use Doctrine\DBAL\Schema\Table; use Illuminate\Database\ConnectionResolverInterface; use Illuminate\Database\Schema\Builder; use Illuminate\Support\Arr; -use Illuminate\Support\Str; use Symfony\Component\Console\Attribute\AsCommand; use function Laravel\Prompts\select; @@ -139,7 +134,7 @@ protected function getAttributesForIndex($index) { return collect([ $index['type'], - count($index['columns']) > 1 ? 'compound' : null , + count($index['columns']) > 1 ? 'compound' : null, $index['unique'] && ! $index['primary'] ? 'unique' : null, $index['primary'] ? 'primary' : null, ])->filter(); From ba0d8eaa0c974de571492426db1eb422139a906e Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 10 Dec 2023 23:36:32 +0330 Subject: [PATCH 21/52] wip --- .../Console/DatabaseInspectionCommand.php | 16 ++++++++++++++++ .../Database/Console/TableCommand.php | 17 ++++++++++------- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/Illuminate/Database/Console/DatabaseInspectionCommand.php b/src/Illuminate/Database/Console/DatabaseInspectionCommand.php index e6befa5ab635..6c1908cad405 100644 --- a/src/Illuminate/Database/Console/DatabaseInspectionCommand.php +++ b/src/Illuminate/Database/Console/DatabaseInspectionCommand.php @@ -65,4 +65,20 @@ protected function getConfigFromDatabase($database) return Arr::except(config('database.connections.'.$database), ['password']); } + + /** + * Remove the table prefix from a table name, if it exists. + * + * @param \Illuminate\Database\ConnectionInterface $connection + * @param string $table + * @return string + */ + protected function withoutTablePrefix(ConnectionInterface $connection, string $table) + { + $prefix = $connection->getTablePrefix(); + + return str_starts_with($table, $prefix) + ? substr($table, strlen($prefix)) + : $table; + } } diff --git a/src/Illuminate/Database/Console/TableCommand.php b/src/Illuminate/Database/Console/TableCommand.php index b60ad892050a..ffce811e9572 100644 --- a/src/Illuminate/Database/Console/TableCommand.php +++ b/src/Illuminate/Database/Console/TableCommand.php @@ -5,6 +5,7 @@ use Illuminate\Database\ConnectionResolverInterface; use Illuminate\Database\Schema\Builder; use Illuminate\Support\Arr; +use Illuminate\Support\Number; use Symfony\Component\Console\Attribute\AsCommand; use function Laravel\Prompts\select; @@ -55,9 +56,11 @@ public function handle(ConnectionResolverInterface $connections) return 1; } - $columns = $this->columns($schema, $table['name']); - $indexes = $this->indexes($schema, $table['name']); - $foreignKeys = $this->foreignKeys($schema, $table['name']); + $tableName = $this->withoutTablePrefix($connection, $table['name']); + + $columns = $this->columns($schema, $tableName); + $indexes = $this->indexes($schema, $tableName); + $foreignKeys = $this->foreignKeys($schema, $tableName); $data = [ 'table' => [ @@ -151,7 +154,7 @@ protected function foreignKeys(Builder $schema, string $table) { return collect($schema->getForeignKeys($table))->map(fn ($foreignKey) => [ 'name' => $foreignKey['name'], - 'local_columns' => collect($foreignKey['columns']), + 'columns' => collect($foreignKey['columns']), 'foreign_schema' => $foreignKey['foreign_schema'], 'foreign_table' => $foreignKey['foreign_table'], 'foreign_columns' => collect($foreignKey['foreign_columns']), @@ -200,7 +203,7 @@ protected function displayForCli(array $data) $this->components->twoColumnDetail('Columns', $table['columns']); if ($size = $table['size']) { - $this->components->twoColumnDetail('Size', number_format($size / 1024 / 1024, 2).'MiB'); + $this->components->twoColumnDetail('Size', Number::fileSize($size, 2)); } $this->newLine(); @@ -211,7 +214,7 @@ protected function displayForCli(array $data) $columns->each(function ($column) { $this->components->twoColumnDetail( $column['column'].' '.$column['attributes']->implode(', ').'', - ($column['default'] ? ''.$column['default'].' ' : '').''.$column['type'].'' + ($column['default'] ? ''.$column['default'].' ' : '').$column['type'] ); }); @@ -236,7 +239,7 @@ protected function displayForCli(array $data) $foreignKeys->each(function ($foreignKey) { $this->components->twoColumnDetail( - $foreignKey['name'].' '.$foreignKey['local_columns']->implode(', ').' references '.$foreignKey['foreign_columns']->implode(', ').' on '.$foreignKey['foreign_table'].'', + $foreignKey['name'].' '.$foreignKey['columns']->implode(', ').' references '.$foreignKey['foreign_columns']->implode(', ').' on '.$foreignKey['foreign_table'].'', $foreignKey['on_update'].' / '.$foreignKey['on_delete'], ); }); From bd1bceb8d78851a70a43a48102b259288096059e Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 11 Dec 2023 15:44:50 +0330 Subject: [PATCH 22/52] wip --- src/Illuminate/Database/Connection.php | 12 ------------ src/Illuminate/Support/Facades/DB.php | 1 - 2 files changed, 13 deletions(-) diff --git a/src/Illuminate/Database/Connection.php b/src/Illuminate/Database/Connection.php index dc5b8f4f7de2..c6f26bd1fe88 100755 --- a/src/Illuminate/Database/Connection.php +++ b/src/Illuminate/Database/Connection.php @@ -1228,18 +1228,6 @@ public function usingNativeSchemaOperations() return ! $this->isDoctrineAvailable() || SchemaBuilder::$alwaysUsesNativeSchemaOperationsIfPossible; } - /** - * Get the Doctrine DBAL schema manager for the connection. - * - * @return \Doctrine\DBAL\Schema\AbstractSchemaManager - */ - public function getDoctrineSchemaManager() - { - $connection = $this->getDoctrineConnection(); - - return $connection->createSchemaManager(); - } - /** * Get the Doctrine DBAL database connection instance. * diff --git a/src/Illuminate/Support/Facades/DB.php b/src/Illuminate/Support/Facades/DB.php index 3ed7fa4d720a..099dbc13b989 100755 --- a/src/Illuminate/Support/Facades/DB.php +++ b/src/Illuminate/Support/Facades/DB.php @@ -62,7 +62,6 @@ * @method static \Illuminate\Database\Connection useWriteConnectionWhenReading(bool $value = true) * @method static bool isDoctrineAvailable() * @method static bool usingNativeSchemaOperations() - * @method static \Doctrine\DBAL\Schema\AbstractSchemaManager getDoctrineSchemaManager() * @method static \Doctrine\DBAL\Connection getDoctrineConnection() * @method static \PDO getPdo() * @method static \PDO|\Closure|null getRawPdo() From ba4978e22bd4066b348df8019d1972ee692073d4 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 11 Dec 2023 23:47:25 +0330 Subject: [PATCH 23/52] support renaming columns on legacy MariaDB --- src/Illuminate/Database/MySqlConnection.php | 13 +++++++++++ .../Database/Schema/Grammars/MySqlGrammar.php | 6 ++++- .../Database/DatabaseSchemaBlueprintTest.php | 23 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Database/MySqlConnection.php b/src/Illuminate/Database/MySqlConnection.php index 460a4fd375c1..cb15e869cbba 100755 --- a/src/Illuminate/Database/MySqlConnection.php +++ b/src/Illuminate/Database/MySqlConnection.php @@ -10,6 +10,7 @@ use Illuminate\Database\Schema\MySqlBuilder; use Illuminate\Database\Schema\MySqlSchemaState; use Illuminate\Filesystem\Filesystem; +use Illuminate\Support\Str; use PDO; class MySqlConnection extends Connection @@ -48,6 +49,18 @@ public function isMaria() return str_contains($this->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION), 'MariaDB'); } + /** + * Get the server version for the connection. + * + * @return string + */ + public function getServerVersion(): string + { + return str_contains($version = parent::getServerVersion(), 'MariaDB') + ? Str::between($version, '5.5.5-', '-MariaDB') + : $version; + } + /** * Get the default query grammar instance. * diff --git a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php index b892cb40aed2..f7f46442ed28 100755 --- a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php @@ -319,7 +319,11 @@ public function compileAutoIncrementStartingValues(Blueprint $blueprint, Fluent */ public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Connection $connection) { - if (version_compare($connection->getServerVersion(), '8.0.3', '<')) { + $version = $connection->getServerVersion(); + + if (($connection->isMaria() && version_compare($version, '10.5.2', '<')) + || (! $connection->isMaria() && version_compare($version, '8.0.3', '<')) + ) { $column = collect($connection->getSchemaBuilder()->getColumns($blueprint->getTable())) ->firstWhere('name', $command->from); diff --git a/tests/Database/DatabaseSchemaBlueprintTest.php b/tests/Database/DatabaseSchemaBlueprintTest.php index 977f9a8f2b60..54d8b588ca67 100755 --- a/tests/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Database/DatabaseSchemaBlueprintTest.php @@ -181,6 +181,7 @@ public function testRenameColumn() $connection = m::mock(Connection::class); $connection->shouldReceive('getServerVersion')->andReturn('8.0.4'); + $connection->shouldReceive('isMaria')->andReturn(false); $blueprint = clone $base; $this->assertEquals(['alter table `users` rename column `foo` to `bar`'], $blueprint->toSql($connection, new MySqlGrammar)); @@ -203,6 +204,7 @@ public function testNativeRenameColumnOnMysql57() }); $connection = m::mock(Connection::class); + $connection->shouldReceive('isMaria')->andReturn(false); $connection->shouldReceive('getServerVersion')->andReturn('5.7'); $connection->shouldReceive('getSchemaBuilder->getColumns')->andReturn([ ['name' => 'name', 'type' => 'varchar(255)', 'type_name' => 'varchar', 'nullable' => true, 'collation' => 'utf8mb4_unicode_ci', 'default' => 'foo', 'comment' => null, 'auto_increment' => false], @@ -215,6 +217,27 @@ public function testNativeRenameColumnOnMysql57() ], $blueprint->toSql($connection, new MySqlGrammar)); } + public function testNativeRenameColumnOnLegacyMariaDB() + { + $blueprint = new Blueprint('users', function ($table) { + $table->renameColumn('name', 'title'); + $table->renameColumn('id', 'key'); + }); + + $connection = m::mock(Connection::class); + $connection->shouldReceive('isMaria')->andReturn(true); + $connection->shouldReceive('getServerVersion')->andReturn('10.1.35'); + $connection->shouldReceive('getSchemaBuilder->getColumns')->andReturn([ + ['name' => 'name', 'type' => 'varchar(255)', 'type_name' => 'varchar', 'nullable' => true, 'collation' => 'utf8mb4_unicode_ci', 'default' => 'foo', 'comment' => null, 'auto_increment' => false], + ['name' => 'id', 'type' => 'bigint unsigned', 'type_name' => 'bigint', 'nullable' => false, 'collation' => null, 'default' => null, 'comment' => 'lorem ipsum', 'auto_increment' => true], + ]); + + $this->assertEquals([ + "alter table `users` change `name` `title` varchar(255) collate 'utf8mb4_unicode_ci' null default 'foo'", + "alter table `users` change `id` `key` bigint unsigned not null auto_increment primary key comment 'lorem ipsum'", + ], $blueprint->toSql($connection, new MySqlGrammar)); + } + public function testDropColumn() { $base = new Blueprint('users', function ($table) { From eae02d9bc8cc7edeabd7e5bb89cd892e9a841e6f Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 11 Dec 2023 23:54:27 +0330 Subject: [PATCH 24/52] remove redundant non-standard tests --- .../Database/DatabaseSchemaBlueprintTest.php | 26 +------------------ 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php index 63f696fc6f80..f340e793c280 100644 --- a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php @@ -57,31 +57,7 @@ public function testRenamingAndChangingColumnsWork() public function testRenamingColumnsWorks() { - $connection = DB::connection(); - $schema = $connection->getSchemaBuilder(); - - $schema->create('users', function ($table) { - $table->string('name')->nullable(); - }); - - $base = new Blueprint('users', function ($table) { - $table->renameColumn('name', 'new_name'); - }); - - $blueprint = clone $base; - $this->assertContains($blueprint->toSql($connection, new MySqlGrammar), [ - ['alter table `users` rename column `name` to `new_name`'], // MySQL 8.0 - ['alter table `users` change `name` `new_name` varchar null'], // MySQL 5.7 - ]); - - $blueprint = clone $base; - $this->assertEquals(['alter table "users" rename column "name" to "new_name"'], $blueprint->toSql($connection, new PostgresGrammar)); - - $blueprint = clone $base; - $this->assertEquals(['alter table "users" rename column "name" to "new_name"'], $blueprint->toSql($connection, new SQLiteGrammar)); - - $blueprint = clone $base; - $this->assertEquals(['sp_rename \'"users"."name"\', "new_name", \'COLUMN\''], $blueprint->toSql($connection, new SqlServerGrammar)); + $schema = DB::connection()->getSchemaBuilder(); $schema->create('test', function (Blueprint $table) { $table->string('foo'); From 92d0ec90fa0a4114a6b8ee81267c3cf3de39920c Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 17 Dec 2023 23:40:50 +0330 Subject: [PATCH 25/52] add collation modifier to sqlite --- .../Query/Processors/SQLiteProcessor.php | 11 +++++-- .../Schema/Grammars/SQLiteGrammar.php | 33 +++++++++++++++++-- .../Database/Schema/SQLiteBuilder.php | 16 +++++++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/Illuminate/Database/Query/Processors/SQLiteProcessor.php b/src/Illuminate/Database/Query/Processors/SQLiteProcessor.php index 8f5fb98206e0..1bd2d7d61c30 100644 --- a/src/Illuminate/Database/Query/Processors/SQLiteProcessor.php +++ b/src/Illuminate/Database/Query/Processors/SQLiteProcessor.php @@ -23,22 +23,27 @@ public function processColumnListing($results) * Process the results of a columns query. * * @param array $results + * @param string $sql * @return array */ - public function processColumns($results) + public function processColumns($results, $sql = '') { $hasPrimaryKey = array_sum(array_column($results, 'primary')) === 1; - return array_map(function ($result) use ($hasPrimaryKey) { + return array_map(function ($result) use ($hasPrimaryKey, $sql) { $result = (object) $result; $type = strtolower($result->type); + $collation = preg_match('/\b'.preg_quote($result->name) + .'\b[^,(]+(?:\([^()]+\)[^,]*)?(?:(?:default|check|as)\s*(?:\(.*?\))?[^,]*)*collate\s+["\']?(\w+)/i', + $sql, $matches) === 1 ? strtolower($matches[1]) : null; + return [ 'name' => $result->name, 'type_name' => strtok($type, '('), 'type' => $type, - 'collation' => null, + 'collation' => $collation, 'nullable' => (bool) $result->nullable, 'default' => $result->default, 'auto_increment' => $hasPrimaryKey && $result->primary && $type === 'integer', diff --git a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php index 10b53557a438..5e1072a5001f 100755 --- a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php @@ -17,7 +17,7 @@ class SQLiteGrammar extends Grammar * * @var string[] */ - protected $modifiers = ['Increment', 'Nullable', 'Default', 'VirtualAs', 'StoredAs']; + protected $modifiers = ['Increment', 'Nullable', 'Default', 'Collate', 'VirtualAs', 'StoredAs']; /** * The columns available as serials. @@ -35,7 +35,22 @@ class SQLiteGrammar extends Grammar */ public function compileTableExists() { - return "select * from sqlite_master where type = 'table' and name = ?"; + return "select sql from sqlite_master where type = 'table' and name = ?"; + } + + /** + * Compile the query to determine the SQL text that describes the given object. + * + * @param string $name + * @param string $type + * @return string + */ + public function compileSqlCreateStatement($name, $type = 'table') + { + return sprintf('select "sql" from sqlite_master where type = %s and name = %s', + $this->wrap($type), + $this->wrap(str_replace('.', '__', $name)) + ); } /** @@ -1063,6 +1078,20 @@ protected function modifyIncrement(Blueprint $blueprint, Fluent $column) } } + /** + * Get the SQL for a collation column modifier. + * + * @param \Illuminate\Database\Schema\Blueprint $blueprint + * @param \Illuminate\Support\Fluent $column + * @return string|null + */ + protected function modifyCollate(Blueprint $blueprint, Fluent $column) + { + if (! is_null($column->collation)) { + return " collate '{$column->collation}'"; + } + } + /** * Wrap the given JSON selector. * diff --git a/src/Illuminate/Database/Schema/SQLiteBuilder.php b/src/Illuminate/Database/Schema/SQLiteBuilder.php index 8ae272d767b6..f1a89a1e9478 100644 --- a/src/Illuminate/Database/Schema/SQLiteBuilder.php +++ b/src/Illuminate/Database/Schema/SQLiteBuilder.php @@ -51,6 +51,22 @@ public function getTables() ); } + /** + * 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)) + ); + } + /** * Get all of the table names for the database. * From 8b6c226049bcafcb297f1020640817e9cc9e0fa7 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 17 Dec 2023 23:46:21 +0330 Subject: [PATCH 26/52] support native column modifying on sqlite --- .../Schema/Grammars/SQLiteGrammar.php | 143 +++++++++++++---- .../Database/DatabaseSchemaBlueprintTest.php | 144 +++++++----------- .../Database/SchemaBuilderTest.php | 2 + 3 files changed, 173 insertions(+), 116 deletions(-) diff --git a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php index 5e1072a5001f..593ce4339b6b 100755 --- a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php @@ -5,6 +5,8 @@ use Illuminate\Database\Connection; use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Schema\ColumnDefinition; +use Illuminate\Database\Schema\ForeignKeyDefinition; use Illuminate\Database\Schema\IndexDefinition; use Illuminate\Support\Arr; use Illuminate\Support\Fluent; @@ -136,7 +138,7 @@ public function compileColumnListing($table) public function compileColumns($table) { return sprintf( - "select name, type, not 'notnull' as 'nullable', dflt_value as 'default', pk as 'primary' " + 'select name, type, not "notnull" as "nullable", dflt_value as "default", pk as "primary" ' .'from pragma_table_info(%s) order by cid asc', $this->wrap(str_replace('.', '__', $table)) ); @@ -191,39 +193,24 @@ public function compileCreate(Blueprint $blueprint, Fluent $command) $blueprint->temporary ? 'create temporary' : 'create', $this->wrapTable($blueprint), implode(', ', $this->getColumns($blueprint)), - (string) $this->addForeignKeys($blueprint), - (string) $this->addPrimaryKeys($blueprint) + $this->addForeignKeys($this->getCommandsByName($blueprint, 'foreign')), + $this->addPrimaryKeys($this->getCommandByName($blueprint, 'primary')) ); } /** * Get the foreign key syntax for a table creation statement. * - * @param \Illuminate\Database\Schema\Blueprint $blueprint + * @param \Illuminate\Database\Schema\ForeignKeyDefinition[] $foreignKeys * @return string|null */ - protected function addForeignKeys(Blueprint $blueprint) + protected function addForeignKeys($foreignKeys) { - $foreigns = $this->getCommandsByName($blueprint, 'foreign'); - - return collect($foreigns)->reduce(function ($sql, $foreign) { + return collect($foreignKeys)->reduce(function ($sql, $foreign) { // Once we have all the foreign key commands for the table creation statement // we'll loop through each of them and add them to the create table SQL we // are building, since SQLite needs foreign keys on the tables creation. - $sql .= $this->getForeignKey($foreign); - - if (! is_null($foreign->onDelete)) { - $sql .= " on delete {$foreign->onDelete}"; - } - - // If this foreign key specifies the action to be taken on update we will add - // that to the statement here. We'll append it to this SQL and then return - // the SQL so we can keep adding any other foreign constraints onto this. - if (! is_null($foreign->onUpdate)) { - $sql .= " on update {$foreign->onUpdate}"; - } - - return $sql; + return $sql.$this->getForeignKey($foreign); }, ''); } @@ -238,22 +225,35 @@ protected function getForeignKey($foreign) // We need to columnize the columns that the foreign key is being defined for // so that it is a properly formatted list. Once we have done this, we can // return the foreign key SQL declaration to the calling method for use. - return sprintf(', foreign key(%s) references %s(%s)', + $sql = sprintf(', foreign key(%s) references %s(%s)', $this->columnize($foreign->columns), $this->wrapTable($foreign->on), $this->columnize((array) $foreign->references) ); + + if (! is_null($foreign->onDelete)) { + $sql .= " on delete {$foreign->onDelete}"; + } + + // If this foreign key specifies the action to be taken on update we will add + // that to the statement here. We'll append it to this SQL and then return + // the SQL so we can keep adding any other foreign constraints onto this. + if (! is_null($foreign->onUpdate)) { + $sql .= " on update {$foreign->onUpdate}"; + } + + return $sql; } /** * Get the primary key syntax for a table creation statement. * - * @param \Illuminate\Database\Schema\Blueprint $blueprint + * @param \Illuminate\Support\Fluent|null $primary * @return string|null */ - protected function addPrimaryKeys(Blueprint $blueprint) + protected function addPrimaryKeys($primary) { - if (! is_null($primary = $this->getCommandByName($blueprint, 'primary'))) { + if (! is_null($primary)) { return ", primary key ({$this->columnize($primary->columns)})"; } } @@ -276,6 +276,97 @@ public function compileAdd(Blueprint $blueprint, Fluent $command) })->all(); } + /** + * 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) + { + if (! $connection->usingNativeSchemaOperations()) { + return parent::compileChange($blueprint, $command, $connection); + } + + $schema = $connection->getSchemaBuilder(); + $table = $blueprint->getTable(); + + $changedColumns = collect($blueprint->getChangedColumns()); + $columnNames = []; + $autoIncrementColumn = null; + + $columns = collect($schema->getColumns($table)) + ->map(function ($column) use ($blueprint, $changedColumns, &$columnNames, &$autoIncrementColumn) { + $column = $changedColumns->first(fn ($col) => $col->name === $column['name'], $column); + + if ($column instanceof Fluent) { + $name = $this->wrap($column); + $columnNames[] = $name; + $autoIncrementColumn = $column->autoIncrement ? $column->name : $autoIncrementColumn; + + return $this->addModifiers($name.' '.$this->getType($column), $blueprint, $column); + } else { + $name = $this->wrap($column['name']); + $columnNames[] = $name; + $autoIncrementColumn = $column['auto_increment'] ? $column['name'] : $autoIncrementColumn; + + return $this->addModifiers($name.' '.$column['type'], $blueprint, + new ColumnDefinition([ + 'change' => true, + 'type' => $column['type_name'], + 'nullable' => $column['nullable'], + 'default' => $column['default'], + 'autoIncrement' => $column['auto_increment'], + 'collation' => $column['collation'], + 'comment' => $column['comment'], + ]) + ); + } + })->all(); + + $foreignKeys = collect($schema->getForeignKeys($table))->map(fn ($foreignKey) => new ForeignKeyDefinition([ + 'columns' => $foreignKey['columns'], + 'on' => $foreignKey['foreign_table'], + 'references' => $foreignKey['foreign_columns'], + 'onUpdate' => $foreignKey['on_update'], + 'onDelete' => $foreignKey['on_delete'], + ]))->all(); + + [$primary, $indexes] = collect($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'); + + $indexes = collect($indexes)->reject(fn ($index) => str_starts_with('sqlite_', $index->index))->map( + fn ($index) => $this->{'compile'.ucfirst($index->name)}($blueprint, $index) + )->all(); + + $tempTable = $this->wrap('__temp__'.$this->getTablePrefix().$table); + $table = $this->wrap($this->getTablePrefix().$table); + $columnNames = implode(', ', $columnNames); + + return array_merge([ + sprintf('create table %s (%s%s%s)', + $tempTable, + implode(', ', $columns), + $this->addForeignKeys($foreignKeys), + $autoIncrementColumn ? '' :$this->addPrimaryKeys($primary->first()) + ), + sprintf('insert into %s (%s) select %s from %s', $tempTable, $columnNames, $columnNames, $table), + sprintf('drop table %s', $table), + sprintf('alter table %s rename to %s', $tempTable, $table), + ], $indexes); + } + /** * Compile a unique key command. * diff --git a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php index a76e68799f57..35317ec1f01e 100644 --- a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php @@ -40,19 +40,15 @@ public function testRenamingAndChangingColumnsWork() $queries = $blueprint->toSql(DB::connection(), new SQLiteGrammar); - // Expect one of the following two query sequences to be present... $expected = [ - [ - 'CREATE TEMPORARY TABLE __temp__users AS SELECT name, age FROM users', - 'DROP TABLE users', - 'CREATE TABLE users (name VARCHAR NOT NULL, age INTEGER NOT NULL)', - 'INSERT INTO users (name, age) SELECT name, age FROM __temp__users', - 'DROP TABLE __temp__users', - 'alter table "users" rename column "name" to "first_name"', - ], + 'create table "__temp__users" ("name" varchar not null, "age" integer not null)', + 'insert into "__temp__users" ("name", "age") select "name", "age" from "users"', + 'drop table "users"', + 'alter table "__temp__users" rename to "users"', + 'alter table "users" rename column "name" to "first_name"', ]; - $this->assertContains($queries, $expected); + $this->assertEquals($expected, $queries); } public function testRenamingColumnsWorks() @@ -238,30 +234,24 @@ public function testChangingColumnWithCollationWorks() $queries = $blueprint->toSql(DB::connection(), new SQLiteGrammar); $expected = [ - [ - 'CREATE TEMPORARY TABLE __temp__users AS SELECT age FROM users', - 'DROP TABLE users', - 'CREATE TABLE users (age INTEGER NOT NULL COLLATE "RTRIM")', - 'INSERT INTO users (age) SELECT age FROM __temp__users', - 'DROP TABLE __temp__users', - ], + 'create table "__temp__users" ("age" integer not null collate \'RTRIM\')', + 'insert into "__temp__users" ("age") select "age" from "users"', + 'drop table "users"', + 'alter table "__temp__users" rename to "users"', ]; - $this->assertContains($queries, $expected); + $this->assertEquals($expected, $queries); $queries = $blueprint2->toSql(DB::connection(), new SQLiteGrammar); $expected = [ - [ - 'CREATE TEMPORARY TABLE __temp__users AS SELECT age FROM users', - 'DROP TABLE users', - 'CREATE TABLE users (age INTEGER NOT NULL COLLATE "NOCASE")', - 'INSERT INTO users (age) SELECT age FROM __temp__users', - 'DROP TABLE __temp__users', - ], + 'create table "__temp__users" ("age" integer not null collate \'NOCASE\')', + 'insert into "__temp__users" ("age") select "age" from "users"', + 'drop table "users"', + 'alter table "__temp__users" rename to "users"', ]; - $this->assertContains($queries, $expected); + $this->assertEquals($expected, $queries); } public function testChangingCharColumnsWork() @@ -277,23 +267,13 @@ public function testChangingCharColumnsWork() $queries = $blueprint->toSql(DB::connection(), new SQLiteGrammar); $expected = [ - [ - 'CREATE TEMPORARY TABLE __temp__users AS SELECT name FROM users', - 'DROP TABLE users', - 'CREATE TABLE users (name CHAR(50) NOT NULL)', - 'INSERT INTO users (name) SELECT name FROM __temp__users', - 'DROP TABLE __temp__users', - ], - [ - 'CREATE TEMPORARY TABLE __temp__users AS SELECT name FROM users', - 'DROP TABLE users', - 'CREATE TABLE users (name CHAR(50) NOT NULL COLLATE "BINARY")', - 'INSERT INTO users (name) SELECT name FROM __temp__users', - 'DROP TABLE __temp__users', - ], + 'create table "__temp__users" ("name" varchar not null)', + 'insert into "__temp__users" ("name") select "name" from "users"', + 'drop table "users"', + 'alter table "__temp__users" rename to "users"', ]; - $this->assertContains($queries, $expected); + $this->assertEquals($expected, $queries); } public function testChangingPrimaryAutoincrementColumnsToNonAutoincrementColumnsWork() @@ -309,16 +289,13 @@ public function testChangingPrimaryAutoincrementColumnsToNonAutoincrementColumns $queries = $blueprint->toSql(DB::connection(), new SQLiteGrammar); $expected = [ - [ - 'CREATE TEMPORARY TABLE __temp__users AS SELECT id FROM users', - 'DROP TABLE users', - 'CREATE TABLE users (id BLOB NOT NULL, PRIMARY KEY(id))', - 'INSERT INTO users (id) SELECT id FROM __temp__users', - 'DROP TABLE __temp__users', - ], + 'create table "__temp__users" ("id" blob not null, primary key ("id"))', + 'insert into "__temp__users" ("id") select "id" from "users"', + 'drop table "users"', + 'alter table "__temp__users" rename to "users"', ]; - $this->assertContains($queries, $expected); + $this->assertEquals($expected, $queries); } public function testChangingDoubleColumnsWork() @@ -334,11 +311,10 @@ public function testChangingDoubleColumnsWork() $queries = $blueprint->toSql(DB::connection(), new SQLiteGrammar); $expected = [ - 'CREATE TEMPORARY TABLE __temp__products AS SELECT price FROM products', - 'DROP TABLE products', - 'CREATE TABLE products (price DOUBLE PRECISION NOT NULL)', - 'INSERT INTO products (price) SELECT price FROM __temp__products', - 'DROP TABLE __temp__products', + 'create table "__temp__products" ("price" double not null)', + 'insert into "__temp__products" ("price") select "price" from "products"', + 'drop table "products"', + 'alter table "__temp__products" rename to "products"', ]; $this->assertEquals($expected, $queries); @@ -406,13 +382,11 @@ public function testAddUniqueIndexWithoutNameWorks() $queries = $blueprintMySql->toSql(DB::connection(), new MySqlGrammar); $expected = [ - [ - 'alter table `users` modify `name` varchar(255) null', - 'alter table `users` add unique `users_name_unique`(`name`)', - ], + 'alter table `users` modify `name` varchar(255) null', + 'alter table `users` add unique `users_name_unique`(`name`)', ]; - $this->assertContains($queries, $expected); + $this->assertEquals($expected, $queries); $blueprintPostgres = new Blueprint('users', function ($table) { $table->string('name')->nullable()->unique()->change(); @@ -421,14 +395,12 @@ public function testAddUniqueIndexWithoutNameWorks() $queries = $blueprintPostgres->toSql(DB::connection(), new PostgresGrammar); $expected = [ - [ - 'alter table "users" alter column "name" type varchar(255), alter column "name" drop not null, alter column "name" drop default, alter column "name" drop identity if exists', - 'alter table "users" add constraint "users_name_unique" unique ("name")', - 'comment on column "users"."name" is NULL', - ], + 'alter table "users" alter column "name" type varchar(255), alter column "name" drop not null, alter column "name" drop default, alter column "name" drop identity if exists', + 'alter table "users" add constraint "users_name_unique" unique ("name")', + 'comment on column "users"."name" is NULL', ]; - $this->assertContains($queries, $expected); + $this->assertEquals($expected, $queries); $blueprintSQLite = new Blueprint('users', function ($table) { $table->string('name')->nullable()->unique()->change(); @@ -437,17 +409,14 @@ public function testAddUniqueIndexWithoutNameWorks() $queries = $blueprintSQLite->toSql(DB::connection(), new SQLiteGrammar); $expected = [ - [ - 'CREATE TEMPORARY TABLE __temp__users AS SELECT name FROM users', - 'DROP TABLE users', - 'CREATE TABLE users (name VARCHAR(255) DEFAULT NULL)', - 'INSERT INTO users (name) SELECT name FROM __temp__users', - 'DROP TABLE __temp__users', - 'create unique index "users_name_unique" on "users" ("name")', - ], + 'create table "__temp__users" ("name" varchar)', + 'insert into "__temp__users" ("name") select "name" from "users"', + 'drop table "users"', + 'alter table "__temp__users" rename to "users"', + 'create unique index "users_name_unique" on "users" ("name")', ]; - $this->assertContains($queries, $expected); + $this->assertEquals($expected, $queries); $blueprintSqlServer = new Blueprint('users', function ($table) { $table->string('name')->nullable()->unique()->change(); @@ -456,14 +425,12 @@ public function testAddUniqueIndexWithoutNameWorks() $queries = $blueprintSqlServer->toSql(DB::connection(), new SqlServerGrammar); $expected = [ - [ - "DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE [dbo].[users] DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID('[dbo].[users]') AND [name] in ('name') AND [default_object_id] <> 0;EXEC(@sql)", - 'alter table "users" alter column "name" nvarchar(255) null', - 'create unique index "users_name_unique" on "users" ("name")', - ], + "DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE [dbo].[users] DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID('[dbo].[users]') AND [name] in ('name') AND [default_object_id] <> 0;EXEC(@sql)", + 'alter table "users" alter column "name" nvarchar(255) null', + 'create unique index "users_name_unique" on "users" ("name")', ]; - $this->assertContains($queries, $expected); + $this->assertEquals($expected, $queries); } public function testAddUniqueIndexWithNameWorks() @@ -479,13 +446,11 @@ public function testAddUniqueIndexWithNameWorks() $queries = $blueprintMySql->toSql(DB::connection(), new MySqlGrammar); $expected = [ - [ - 'alter table `users` modify `name` varchar(255) null', - 'alter table `users` add unique `index1`(`name`)', - ], + 'alter table `users` modify `name` varchar(255) null', + 'alter table `users` add unique `index1`(`name`)', ]; - $this->assertContains($queries, $expected); + $this->assertEquals($expected, $queries); $blueprintPostgres = new Blueprint('users', function ($table) { $table->unsignedInteger('name')->nullable()->unique('index1')->change(); @@ -508,11 +473,10 @@ public function testAddUniqueIndexWithNameWorks() $queries = $blueprintSQLite->toSql(DB::connection(), new SQLiteGrammar); $expected = [ - 'CREATE TEMPORARY TABLE __temp__users AS SELECT name FROM users', - 'DROP TABLE users', - 'CREATE TABLE users (name INTEGER UNSIGNED DEFAULT NULL)', - 'INSERT INTO users (name) SELECT name FROM __temp__users', - 'DROP TABLE __temp__users', + 'create table "__temp__users" ("name" integer)', + 'insert into "__temp__users" ("name") select "name" from "users"', + 'drop table "users"', + 'alter table "__temp__users" rename to "users"', 'create unique index "index1" on "users" ("name")', ]; diff --git a/tests/Integration/Database/SchemaBuilderTest.php b/tests/Integration/Database/SchemaBuilderTest.php index 85b9504249cf..10eaeb4cc8d1 100644 --- a/tests/Integration/Database/SchemaBuilderTest.php +++ b/tests/Integration/Database/SchemaBuilderTest.php @@ -50,6 +50,7 @@ public function testRegisterCustomDoctrineType() $this->markTestSkipped('Test requires a SQLite connection.'); } + Schema::useNativeSchemaOperationsIfPossible(false); Schema::getConnection()->registerDoctrineType(TinyInteger::class, TinyInteger::NAME, 'TINYINT'); Schema::create('test', function (Blueprint $table) { @@ -72,6 +73,7 @@ public function testRegisterCustomDoctrineTypeASecondTime() $this->markTestSkipped('Test requires a SQLite connection.'); } + Schema::useNativeSchemaOperationsIfPossible(false); Schema::getConnection()->registerDoctrineType(TinyInteger::class, TinyInteger::NAME, 'TINYINT'); Schema::create('test', function (Blueprint $table) { From 402830dfd7b3cda8b82169ead7ce73ac88f829d0 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 17 Dec 2023 23:48:46 +0330 Subject: [PATCH 27/52] fix styles --- src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php index 593ce4339b6b..75e17cc6ac7f 100755 --- a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php @@ -337,7 +337,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection ]))->all(); [$primary, $indexes] = collect($schema->getIndexes($table))->map(fn ($index) => new IndexDefinition([ - 'name' => match(true) { + 'name' => match (true) { $index['primary'] => 'primary', $index['unique'] => 'unique', default => 'index', @@ -359,7 +359,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $tempTable, implode(', ', $columns), $this->addForeignKeys($foreignKeys), - $autoIncrementColumn ? '' :$this->addPrimaryKeys($primary->first()) + $autoIncrementColumn ? '' : $this->addPrimaryKeys($primary->first()) ), sprintf('insert into %s (%s) select %s from %s', $tempTable, $columnNames, $columnNames, $table), sprintf('drop table %s', $table), From 9a7be0ca040e83d69f65fd51a2afccdf53e0895b Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 18 Dec 2023 00:11:35 +0330 Subject: [PATCH 28/52] add user-defined types to db:show --- .../Database/Console/ShowCommand.php | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Console/ShowCommand.php b/src/Illuminate/Database/Console/ShowCommand.php index 136cf487676c..9a0712af25f9 100644 --- a/src/Illuminate/Database/Console/ShowCommand.php +++ b/src/Illuminate/Database/Console/ShowCommand.php @@ -20,7 +20,8 @@ class ShowCommand extends DatabaseInspectionCommand protected $signature = 'db:show {--database= : The database connection} {--json : Output the database information as JSON} {--counts : Show the table row count Note: This can be slow on large databases }; - {--views : Show the database views Note: This can be slow on large databases }'; + {--views : Show the database views Note: This can be slow on large databases } + {--types : Show the user-defined types}'; /** * The console command description. @@ -55,6 +56,10 @@ public function handle(ConnectionResolverInterface $connections) $data['views'] = $this->views($connection, $schema); } + if ($this->option('types')) { + $data['types'] = $this->types($connection, $schema); + } + $this->display($data); return 0; @@ -98,6 +103,24 @@ protected function views(ConnectionInterface $connection, Builder $schema) ]); } + /** + * Get information regarding the user-defined types within the database. + * + * @param \Illuminate\Database\ConnectionInterface $connection + * @param \Illuminate\Database\Schema\Builder $schema + * @return \Illuminate\Support\Collection + */ + protected function types(ConnectionInterface $connection, Builder $schema) + { + return collect($schema->getTypes()) + ->map(fn ($type) => [ + 'name' => $type['name'], + 'schema' => $type['schema'], + 'type' => $type['type'], + 'category' => $type['category'], + ]); + } + /** * Render the database information. * @@ -131,6 +154,7 @@ protected function displayForCli(array $data) $platform = $data['platform']; $tables = $data['tables']; $views = $data['views'] ?? null; + $types = $data['types'] ?? null; $this->newLine(); @@ -194,5 +218,21 @@ protected function displayForCli(array $data) $this->newLine(); } + + if ($types && $types->isNotEmpty()) { + $hasSchema = ! is_null($types->first()['schema']); + + $this->components->twoColumnDetail( + ($hasSchema ? 'Schema / ' : '').'Type', + 'Type / Category' + ); + + $types->each(fn ($type) => $this->components->twoColumnDetail( + ($type['schema'] ? $type['schema'].' / ' : '').$type['name'], + $type['type'].' / '.$type['category'] + )); + + $this->newLine(); + } } } From 686ec27961266b71d309d6db73b4cb08cf9a7997 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 18 Dec 2023 00:34:07 +0330 Subject: [PATCH 29/52] wip --- composer.json | 1 - src/Illuminate/Database/Connection.php | 2 +- src/Illuminate/Database/Schema/Grammars/ChangeColumn.php | 7 ------- src/Illuminate/Database/composer.json | 1 - src/Illuminate/Support/Facades/DB.php | 1 - 5 files changed, 1 insertion(+), 11 deletions(-) diff --git a/composer.json b/composer.json index a06c0e43b279..48aa46e87820 100644 --- a/composer.json +++ b/composer.json @@ -164,7 +164,6 @@ "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.235.5).", "brianium/paratest": "Required to run tests in parallel (^6.0).", - "doctrine/dbal": "Required to modify SQLite columns (^4.0).", "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", "filp/whoops": "Required for friendly error pages in development (^2.14.3).", "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.6).", diff --git a/src/Illuminate/Database/Connection.php b/src/Illuminate/Database/Connection.php index c6f26bd1fe88..805bf5ac179e 100755 --- a/src/Illuminate/Database/Connection.php +++ b/src/Illuminate/Database/Connection.php @@ -1213,7 +1213,7 @@ public function useWriteConnectionWhenReading($value = true) * * @return bool */ - public function isDoctrineAvailable() + protected function isDoctrineAvailable() { return class_exists('Doctrine\DBAL\Connection'); } diff --git a/src/Illuminate/Database/Schema/Grammars/ChangeColumn.php b/src/Illuminate/Database/Schema/Grammars/ChangeColumn.php index 2a745baae0ed..52387c1f6b68 100644 --- a/src/Illuminate/Database/Schema/Grammars/ChangeColumn.php +++ b/src/Illuminate/Database/Schema/Grammars/ChangeColumn.php @@ -25,13 +25,6 @@ class ChangeColumn */ 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() - )); - } - $doctrineConnection = $connection->getDoctrineConnection(); $schema = $doctrineConnection->createSchemaManager(); $databasePlatform = $doctrineConnection->getDatabasePlatform(); diff --git a/src/Illuminate/Database/composer.json b/src/Illuminate/Database/composer.json index 2e2093c14e77..3a79f754e44e 100644 --- a/src/Illuminate/Database/composer.json +++ b/src/Illuminate/Database/composer.json @@ -36,7 +36,6 @@ }, "suggest": { "ext-filter": "Required to use the Postgres database driver.", - "doctrine/dbal": "Required to modify SQLite columns (^4.0).", "fakerphp/faker": "Required to use the eloquent factory builder (^1.21).", "illuminate/console": "Required to use the database commands (^11.0).", "illuminate/events": "Required to use the observers with Eloquent (^11.0).", diff --git a/src/Illuminate/Support/Facades/DB.php b/src/Illuminate/Support/Facades/DB.php index 099dbc13b989..723240a700b8 100755 --- a/src/Illuminate/Support/Facades/DB.php +++ b/src/Illuminate/Support/Facades/DB.php @@ -60,7 +60,6 @@ * @method static \Illuminate\Database\Connection setRecordModificationState(bool $value) * @method static void forgetRecordModificationState() * @method static \Illuminate\Database\Connection useWriteConnectionWhenReading(bool $value = true) - * @method static bool isDoctrineAvailable() * @method static bool usingNativeSchemaOperations() * @method static \Doctrine\DBAL\Connection getDoctrineConnection() * @method static \PDO getPdo() From d44e2c624e97c6287fecbe1f38253b3dcdafefd5 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 18 Dec 2023 00:35:19 +0330 Subject: [PATCH 30/52] fix styles --- src/Illuminate/Database/Schema/Grammars/ChangeColumn.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Illuminate/Database/Schema/Grammars/ChangeColumn.php b/src/Illuminate/Database/Schema/Grammars/ChangeColumn.php index 52387c1f6b68..5910517b3967 100644 --- a/src/Illuminate/Database/Schema/Grammars/ChangeColumn.php +++ b/src/Illuminate/Database/Schema/Grammars/ChangeColumn.php @@ -8,7 +8,6 @@ use Illuminate\Database\Connection; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Fluent; -use RuntimeException; class ChangeColumn { From ebd9beedf62e782fdacad732408023d46fb8849d Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 18 Dec 2023 03:07:42 +0330 Subject: [PATCH 31/52] fix dropForeign exception on SQLite --- src/Illuminate/Database/Schema/Blueprint.php | 10 +--------- src/Illuminate/Database/Schema/Grammars/Grammar.php | 12 ++++++++++++ .../Database/DatabaseSchemaBlueprintTest.php | 6 +++--- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Illuminate/Database/Schema/Blueprint.php b/src/Illuminate/Database/Schema/Blueprint.php index beeb49bbe34c..15500b3d71b3 100755 --- a/src/Illuminate/Database/Schema/Blueprint.php +++ b/src/Illuminate/Database/Schema/Blueprint.php @@ -2,13 +2,11 @@ namespace Illuminate\Database\Schema; -use BadMethodCallException; use Closure; use Illuminate\Database\Connection; use Illuminate\Database\Eloquent\Concerns\HasUlids; use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Grammars\Grammar; -use Illuminate\Database\SQLiteConnection; use Illuminate\Support\Fluent; use Illuminate\Support\Traits\Macroable; @@ -152,13 +150,7 @@ public function toSql(Connection $connection, Grammar $grammar) */ protected function ensureCommandsAreValid(Connection $connection) { - if ($connection instanceof SQLiteConnection) { - if ($this->commandsNamed(['dropForeign'])->count() > 0) { - throw new BadMethodCallException( - "SQLite doesn't support dropping foreign keys (you would need to re-create the table)." - ); - } - } + // } /** diff --git a/src/Illuminate/Database/Schema/Grammars/Grammar.php b/src/Illuminate/Database/Schema/Grammars/Grammar.php index a9d1a64d7038..c7dd48af2e32 100755 --- a/src/Illuminate/Database/Schema/Grammars/Grammar.php +++ b/src/Illuminate/Database/Schema/Grammars/Grammar.php @@ -164,6 +164,18 @@ public function compileForeign(Blueprint $blueprint, Fluent $command) 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. * diff --git a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php index 35317ec1f01e..41ba32f8549d 100644 --- a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php @@ -2,7 +2,7 @@ namespace Illuminate\Tests\Integration\Database; -use BadMethodCallException; +use RuntimeException; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Grammars\MySqlGrammar; use Illuminate\Database\Schema\Grammars\PostgresGrammar; @@ -565,8 +565,8 @@ public function testItDoesNotSetPrecisionHigherThanSupportedWhenRenamingTimestam public function testItEnsuresDroppingForeignKeyIsAvailable() { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessage("SQLite doesn't support dropping foreign keys (you would need to re-create the table)."); + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('This database driver does not support dropping foreign keys.'); Schema::table('users', function (Blueprint $table) { $table->dropForeign('something'); From 46562fb5893b7fe53b1fd0eb450f8e46bfd8fde0 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 18 Dec 2023 03:08:57 +0330 Subject: [PATCH 32/52] fix styles --- tests/Integration/Database/DatabaseSchemaBlueprintTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php index 41ba32f8549d..cc5355063e31 100644 --- a/tests/Integration/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Integration/Database/DatabaseSchemaBlueprintTest.php @@ -2,7 +2,6 @@ namespace Illuminate\Tests\Integration\Database; -use RuntimeException; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Grammars\MySqlGrammar; use Illuminate\Database\Schema\Grammars\PostgresGrammar; @@ -11,6 +10,7 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; use Orchestra\Testbench\TestCase; +use RuntimeException; class DatabaseSchemaBlueprintTest extends TestCase { From 1cc64afc01376011141c59e3ff97dc55bb437b6b Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 18 Dec 2023 04:26:01 +0330 Subject: [PATCH 33/52] include generated and hidden columns on sqlite --- src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php index 75e17cc6ac7f..aaba8e1a20c8 100755 --- a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php @@ -139,7 +139,7 @@ public function compileColumns($table) { return sprintf( 'select name, type, not "notnull" as "nullable", dflt_value as "default", pk as "primary" ' - .'from pragma_table_info(%s) order by cid asc', + .'from pragma_table_xinfo(%s) order by cid asc', $this->wrap(str_replace('.', '__', $table)) ); } From 1d8510f047f50347541f5d8bd4dbc4f4ba01a30b Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 18 Dec 2023 17:46:53 +0330 Subject: [PATCH 34/52] remove custom doctrine types --- .../Database/DBAL/TimestampType.php | 94 --------------- src/Illuminate/Support/Facades/DB.php | 1 - .../ConfigureCustomDoctrineTypeTest.php | 114 ------------------ .../Database/Fixtures/TinyInteger.php | 38 ------ .../Database/SchemaBuilderTest.php | 33 +---- .../Database/{DBAL => }/TimestampTypeTest.php | 29 +---- 6 files changed, 6 insertions(+), 303 deletions(-) delete mode 100644 src/Illuminate/Database/DBAL/TimestampType.php delete mode 100644 tests/Integration/Database/ConfigureCustomDoctrineTypeTest.php delete mode 100644 tests/Integration/Database/Fixtures/TinyInteger.php rename tests/Integration/Database/{DBAL => }/TimestampTypeTest.php (74%) diff --git a/src/Illuminate/Database/DBAL/TimestampType.php b/src/Illuminate/Database/DBAL/TimestampType.php deleted file mode 100644 index aee4a2a0130b..000000000000 --- a/src/Illuminate/Database/DBAL/TimestampType.php +++ /dev/null @@ -1,94 +0,0 @@ - $this->getMySqlPlatformSQLDeclaration($column), - PostgreSQLPlatform::class => $this->getPostgresPlatformSQLDeclaration($column), - SQLServerPlatform::class => $this->getSqlServerPlatformSQLDeclaration($column), - SQLitePlatform::class => 'DATETIME', - default => throw NotSupported::new('TIMESTAMP'), - }; - } - - /** - * Get the SQL declaration for MySQL. - * - * @param array $column - * @return string - */ - protected function getMySqlPlatformSQLDeclaration(array $column): string - { - $columnType = 'TIMESTAMP'; - - if ($column['precision']) { - $columnType = 'TIMESTAMP('.min((int) $column['precision'], 6).')'; - } - - $notNull = $column['notnull'] ?? false; - - if (! $notNull) { - return $columnType.' NULL'; - } - - return $columnType; - } - - /** - * Get the SQL declaration for PostgreSQL. - * - * @param array $column - * @return string - */ - protected function getPostgresPlatformSQLDeclaration(array $column): string - { - return 'TIMESTAMP('.min((int) $column['precision'], 6).')'; - } - - /** - * Get the SQL declaration for SQL Server. - * - * @param array $column - * @return string - */ - protected function getSqlServerPlatformSQLDeclaration(array $column): string - { - return $column['precision'] ?? false - ? 'DATETIME2('.min((int) $column['precision'], 7).')' - : 'DATETIME'; - } - - /** - * {@inheritdoc} - * - * @return string - */ - public function getName() - { - return 'timestamp'; - } -} diff --git a/src/Illuminate/Support/Facades/DB.php b/src/Illuminate/Support/Facades/DB.php index 723240a700b8..596909319edf 100755 --- a/src/Illuminate/Support/Facades/DB.php +++ b/src/Illuminate/Support/Facades/DB.php @@ -4,7 +4,6 @@ /** * @method static \Illuminate\Database\Connection connection(string|null $name = null) - * @method static void registerDoctrineType(string $class, string $name, string $type) * @method static void purge(string|null $name = null) * @method static void disconnect(string|null $name = null) * @method static \Illuminate\Database\Connection reconnect(string|null $name = null) diff --git a/tests/Integration/Database/ConfigureCustomDoctrineTypeTest.php b/tests/Integration/Database/ConfigureCustomDoctrineTypeTest.php deleted file mode 100644 index fa177d48eb6b..000000000000 --- a/tests/Integration/Database/ConfigureCustomDoctrineTypeTest.php +++ /dev/null @@ -1,114 +0,0 @@ - MySQLBitType::class, - 'xml' => PostgresXmlType::class, - ]; - } - - public function testRegisterCustomDoctrineTypesWithNonDefaultDatabaseConnections() - { - $this->assertTrue( - DB::connection() - ->getDoctrineConnection() - ->getDatabasePlatform() - ->hasDoctrineTypeMappingFor('xml') - ); - - // Custom type mappings are registered for a connection when it's created, - // this is not the default connection but it has the custom type mappings - $this->assertTrue( - DB::connection('sqlite') - ->getDoctrineConnection() - ->getDatabasePlatform() - ->hasDoctrineTypeMappingFor('xml') - ); - } - - public function testRenameConfiguredCustomDoctrineColumnTypeWithPostgres() - { - if ($this->driver !== 'pgsql') { - $this->markTestSkipped('Test requires a Postgres connection.'); - } - - Grammar::macro('typeXml', function () { - return 'xml'; - }); - - Schema::create('test', function (Blueprint $table) { - $table->addColumn('xml', 'test_column'); - }); - - Schema::table('test', function (Blueprint $table) { - $table->renameColumn('test_column', 'renamed_column'); - }); - - $this->assertFalse(Schema::hasColumn('test', 'test_column')); - $this->assertTrue(Schema::hasColumn('test', 'renamed_column')); - } - - public function testRenameConfiguredCustomDoctrineColumnTypeWithMysql() - { - if ($this->driver !== 'mysql') { - $this->markTestSkipped('Test requires a MySQL connection.'); - } - - Grammar::macro('typeBit', function () { - return 'bit'; - }); - - Schema::create('test', function (Blueprint $table) { - $table->addColumn('bit', 'test_column'); - }); - - Schema::table('test', function (Blueprint $table) { - $table->renameColumn('test_column', 'renamed_column'); - }); - - $this->assertFalse(Schema::hasColumn('test', 'test_column')); - $this->assertTrue(Schema::hasColumn('test', 'renamed_column')); - } -} - -class PostgresXmlType extends Type -{ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string - { - return 'xml'; - } - - public function getName() - { - return 'xml'; - } -} - -class MySQLBitType extends Type -{ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string - { - return 'bit'; - } - - public function getName() - { - return 'bit'; - } -} diff --git a/tests/Integration/Database/Fixtures/TinyInteger.php b/tests/Integration/Database/Fixtures/TinyInteger.php deleted file mode 100644 index 6b94dff3e869..000000000000 --- a/tests/Integration/Database/Fixtures/TinyInteger.php +++ /dev/null @@ -1,38 +0,0 @@ -driver !== 'sqlite') { $this->markTestSkipped('Test requires a SQLite connection.'); } - Schema::useNativeSchemaOperationsIfPossible(false); - Schema::getConnection()->registerDoctrineType(TinyInteger::class, TinyInteger::NAME, 'TINYINT'); - - Schema::create('test', function (Blueprint $table) { - $table->string('test_column'); - }); - - $blueprint = new Blueprint('test', function (Blueprint $table) { - $table->tinyInteger('test_column')->change(); - }); - - $blueprint->build($this->getConnection(), new SQLiteGrammar); - - $this->assertArrayHasKey(TinyInteger::NAME, Type::getTypesMap()); - $this->assertSame('tinyint', Schema::getColumnType('test', 'test_column')); - } - - public function testRegisterCustomDoctrineTypeASecondTime() - { - if ($this->driver !== 'sqlite') { - $this->markTestSkipped('Test requires a SQLite connection.'); - } - - Schema::useNativeSchemaOperationsIfPossible(false); - Schema::getConnection()->registerDoctrineType(TinyInteger::class, TinyInteger::NAME, 'TINYINT'); - Schema::create('test', function (Blueprint $table) { $table->string('test_column'); }); @@ -86,8 +58,7 @@ public function testRegisterCustomDoctrineTypeASecondTime() $blueprint->build($this->getConnection(), new SQLiteGrammar); - $this->assertArrayHasKey(TinyInteger::NAME, Type::getTypesMap()); - $this->assertSame('tinyint', Schema::getColumnType('test', 'test_column')); + $this->assertSame('integer', Schema::getColumnType('test', 'test_column')); } public function testChangeToTextColumn() diff --git a/tests/Integration/Database/DBAL/TimestampTypeTest.php b/tests/Integration/Database/TimestampTypeTest.php similarity index 74% rename from tests/Integration/Database/DBAL/TimestampTypeTest.php rename to tests/Integration/Database/TimestampTypeTest.php index 1969e5c48099..05e57638c0e1 100644 --- a/tests/Integration/Database/DBAL/TimestampTypeTest.php +++ b/tests/Integration/Database/TimestampTypeTest.php @@ -1,33 +1,12 @@ TimestampType::class, - ]; - } - - public function testRegisterTimestampTypeOnConnection() - { - $this->assertTrue( - $this->app['db']->connection() - ->getDoctrineConnection() - ->getDatabasePlatform() - ->hasDoctrineTypeMappingFor('timestamp') - ); - } - public function testChangeDatetimeColumnToTimestampColumn() { Schema::create('test', function (Blueprint $table) { @@ -35,7 +14,7 @@ public function testChangeDatetimeColumnToTimestampColumn() }); Schema::table('test', function (Blueprint $table) { - $table->timestamp('datetime_to_timestamp')->nullable(true)->change(); + $table->timestamp('datetime_to_timestamp')->nullable()->change(); }); $this->assertTrue(Schema::hasColumn('test', 'datetime_to_timestamp')); @@ -56,7 +35,7 @@ public function testChangeTimestampColumnToDatetimeColumn() }); Schema::table('test', function (Blueprint $table) { - $table->dateTime('timestamp_to_datetime')->nullable(true)->change(); + $table->dateTime('timestamp_to_datetime')->nullable()->change(); }); $this->assertTrue(Schema::hasColumn('test', 'timestamp_to_datetime')); @@ -81,7 +60,7 @@ public function testChangeStringColumnToTimestampColumn() }); $blueprint = new Blueprint('test', function ($table) { - $table->timestamp('string_to_timestamp')->nullable(true)->change(); + $table->timestamp('string_to_timestamp')->nullable()->change(); }); $queries = $blueprint->toSql($this->getConnection(), $this->getConnection()->getSchemaGrammar()); From 86058a8b17fb66b4626228945cdb6c8c302449c5 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 18 Dec 2023 17:47:11 +0330 Subject: [PATCH 35/52] remove doctrine change column --- src/Illuminate/Database/Connection.php | 4 +- .../Database/Migrations/Migrator.php | 28 +-- src/Illuminate/Database/Schema/Blueprint.php | 4 - src/Illuminate/Database/Schema/Builder.php | 10 +- .../Database/Schema/Grammars/ChangeColumn.php | 231 ------------------ .../Database/Schema/Grammars/Grammar.php | 2 +- .../Database/Schema/Grammars/MySqlGrammar.php | 4 - .../Schema/Grammars/PostgresGrammar.php | 4 - .../Schema/Grammars/SQLiteGrammar.php | 4 - .../Schema/Grammars/SqlServerGrammar.php | 4 - 10 files changed, 20 insertions(+), 275 deletions(-) delete mode 100644 src/Illuminate/Database/Schema/Grammars/ChangeColumn.php diff --git a/src/Illuminate/Database/Connection.php b/src/Illuminate/Database/Connection.php index 805bf5ac179e..57dde48214e5 100755 --- a/src/Illuminate/Database/Connection.php +++ b/src/Illuminate/Database/Connection.php @@ -1219,7 +1219,9 @@ protected function isDoctrineAvailable() } /** - * Indicates whether native alter operations will be used when modifying columns, even if Doctrine DBAL is installed. + * Indicates whether native alter operations will be used when dropping, renaming, or modifying columns, even if Doctrine DBAL is installed. + * + * @deprecated Will be removed in a future Laravel version. * * @return bool */ diff --git a/src/Illuminate/Database/Migrations/Migrator.php b/src/Illuminate/Database/Migrations/Migrator.php index 4f6ec5247a04..092232a08024 100755 --- a/src/Illuminate/Database/Migrations/Migrator.php +++ b/src/Illuminate/Database/Migrations/Migrator.php @@ -2,7 +2,6 @@ namespace Illuminate\Database\Migrations; -use Doctrine\DBAL\Schema\SchemaException; use Illuminate\Console\View\Components\BulletList; use Illuminate\Console\View\Components\Error; use Illuminate\Console\View\Components\Info; @@ -428,28 +427,19 @@ protected function runMigration($migration, $method) */ protected function pretendToRun($migration, $method) { - try { - $name = get_class($migration); - - $reflectionClass = new ReflectionClass($migration); + $name = get_class($migration); - if ($reflectionClass->isAnonymous()) { - $name = $this->getMigrationName($reflectionClass->getFileName()); - } + $reflectionClass = new ReflectionClass($migration); - $this->write(TwoColumnDetail::class, $name); + if ($reflectionClass->isAnonymous()) { + $name = $this->getMigrationName($reflectionClass->getFileName()); + } - $this->write(BulletList::class, collect($this->getQueries($migration, $method))->map(function ($query) { - return $query['query']; - })); - } catch (SchemaException) { - $name = get_class($migration); + $this->write(TwoColumnDetail::class, $name); - $this->write(Error::class, sprintf( - '[%s] failed to dump queries. This may be due to changing database columns using Doctrine, which is not supported while pretending to run migrations.', - $name, - )); - } + $this->write(BulletList::class, collect($this->getQueries($migration, $method))->map(function ($query) { + return $query['query']; + })); } /** diff --git a/src/Illuminate/Database/Schema/Blueprint.php b/src/Illuminate/Database/Schema/Blueprint.php index 15500b3d71b3..2160519eb434 100755 --- a/src/Illuminate/Database/Schema/Blueprint.php +++ b/src/Illuminate/Database/Schema/Blueprint.php @@ -240,10 +240,6 @@ protected function addFluentIndexes() public function addFluentCommands(Connection $connection, Grammar $grammar) { foreach ($this->columns as $column) { - if ($column->change && ! $connection->usingNativeSchemaOperations()) { - continue; - } - foreach ($grammar->getFluentCommands() as $commandName) { $this->addCommand($commandName, compact('column')); } diff --git a/src/Illuminate/Database/Schema/Builder.php b/src/Illuminate/Database/Schema/Builder.php index ddee03dd6fda..24e2543a3aa4 100755 --- a/src/Illuminate/Database/Schema/Builder.php +++ b/src/Illuminate/Database/Schema/Builder.php @@ -46,11 +46,13 @@ class Builder public static $defaultMorphKeyType = 'int'; /** - * Indicates whether Doctrine DBAL usage will be prevented if possible when modifying columns. + * Indicates whether Doctrine DBAL usage will be prevented if possible when dropping, renaming, and modifying columns. + * + * @deprecated Will be removed in a future Laravel version. * * @var bool */ - public static $alwaysUsesNativeSchemaOperationsIfPossible = true; + public static $alwaysUsesNativeSchemaOperationsIfPossible = false; /** * Create a new database Schema manager. @@ -113,7 +115,9 @@ public static function morphUsingUlids() } /** - * Attempt to use native schema operations for modifying columns, even if Doctrine DBAL is installed. + * Attempt to use native schema operations for dropping, renaming, and modifying columns, even if Doctrine DBAL is installed. + * + * @deprecated Will be removed in a future Laravel version. * * @param bool $value * @return void diff --git a/src/Illuminate/Database/Schema/Grammars/ChangeColumn.php b/src/Illuminate/Database/Schema/Grammars/ChangeColumn.php deleted file mode 100644 index 5910517b3967..000000000000 --- a/src/Illuminate/Database/Schema/Grammars/ChangeColumn.php +++ /dev/null @@ -1,231 +0,0 @@ -getDoctrineConnection(); - $schema = $doctrineConnection->createSchemaManager(); - $databasePlatform = $doctrineConnection->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['PlatformOptions'] = [ - '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; - } -} diff --git a/src/Illuminate/Database/Schema/Grammars/Grammar.php b/src/Illuminate/Database/Schema/Grammars/Grammar.php index c7dd48af2e32..2d2de55bea69 100755 --- a/src/Illuminate/Database/Schema/Grammars/Grammar.php +++ b/src/Illuminate/Database/Schema/Grammars/Grammar.php @@ -93,7 +93,7 @@ public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Conne */ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection) { - return ChangeColumn::compile($this, $blueprint, $command, $connection); + throw new LogicException('This database driver does not support modifying columns.'); } /** diff --git a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php index 910c2929a95d..2c4c8bb92a85 100755 --- a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php @@ -393,10 +393,6 @@ public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Conne */ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection) { - if (! $connection->usingNativeSchemaOperations()) { - return parent::compileChange($blueprint, $command, $connection); - } - $columns = []; foreach ($blueprint->getChangedColumns() as $column) { diff --git a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php index 7fc54379f2b5..c623db47e79d 100755 --- a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php @@ -296,10 +296,6 @@ public function compileAutoIncrementStartingValues(Blueprint $blueprint, Fluent */ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection) { - if (! $connection->usingNativeSchemaOperations()) { - return parent::compileChange($blueprint, $command, $connection); - } - $columns = []; foreach ($blueprint->getChangedColumns() as $column) { diff --git a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php index aaba8e1a20c8..b332922e8464 100755 --- a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php @@ -288,10 +288,6 @@ public function compileAdd(Blueprint $blueprint, Fluent $command) */ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection) { - if (! $connection->usingNativeSchemaOperations()) { - return parent::compileChange($blueprint, $command, $connection); - } - $schema = $connection->getSchemaBuilder(); $table = $blueprint->getTable(); diff --git a/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php b/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php index 7b13584493b9..c67769c73d32 100755 --- a/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php @@ -275,10 +275,6 @@ public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Conne */ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection) { - if (! $connection->usingNativeSchemaOperations()) { - return parent::compileChange($blueprint, $command, $connection); - } - $changes = [$this->compileDropDefaultConstraint($blueprint, $command)]; foreach ($blueprint->getChangedColumns() as $column) { From 88c2752a2c91aa49c5ca7278c4118d0aea7e45c3 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 18 Dec 2023 17:49:44 +0330 Subject: [PATCH 36/52] styling --- src/Illuminate/Database/Migrations/Migrator.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Illuminate/Database/Migrations/Migrator.php b/src/Illuminate/Database/Migrations/Migrator.php index 092232a08024..e650959419f9 100755 --- a/src/Illuminate/Database/Migrations/Migrator.php +++ b/src/Illuminate/Database/Migrations/Migrator.php @@ -3,7 +3,6 @@ namespace Illuminate\Database\Migrations; use Illuminate\Console\View\Components\BulletList; -use Illuminate\Console\View\Components\Error; use Illuminate\Console\View\Components\Info; use Illuminate\Console\View\Components\Task; use Illuminate\Console\View\Components\TwoColumnDetail; From 298b43fe99951833f2a1f640cc5a7f8322880c64 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 18 Dec 2023 18:46:38 +0330 Subject: [PATCH 37/52] remove support for registering custom doctrine types --- src/Illuminate/Database/Connection.php | 53 +------------------- src/Illuminate/Database/DatabaseManager.php | 54 --------------------- 2 files changed, 1 insertion(+), 106 deletions(-) diff --git a/src/Illuminate/Database/Connection.php b/src/Illuminate/Database/Connection.php index 57dde48214e5..7fd2c1afa7d9 100755 --- a/src/Illuminate/Database/Connection.php +++ b/src/Illuminate/Database/Connection.php @@ -6,7 +6,6 @@ use Closure; use DateTimeInterface; use Doctrine\DBAL\Connection as DoctrineConnection; -use Doctrine\DBAL\Types\Type; use Exception; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Database\Events\QueryExecuted; @@ -196,13 +195,6 @@ class Connection implements ConnectionInterface */ protected $doctrineConnection; - /** - * Type mappings that should be registered with new Doctrine connections. - * - * @var array - */ - protected $doctrineTypeMappings = []; - /** * The connection resolvers. * @@ -1208,16 +1200,6 @@ public function useWriteConnectionWhenReading($value = true) return $this; } - /** - * Is Doctrine available? - * - * @return bool - */ - protected function isDoctrineAvailable() - { - return class_exists('Doctrine\DBAL\Connection'); - } - /** * Indicates whether native alter operations will be used when dropping, renaming, or modifying columns, even if Doctrine DBAL is installed. * @@ -1227,7 +1209,7 @@ protected function isDoctrineAvailable() */ public function usingNativeSchemaOperations() { - return ! $this->isDoctrineAvailable() || SchemaBuilder::$alwaysUsesNativeSchemaOperationsIfPossible; + return ! class_exists('Doctrine\DBAL\Connection') || SchemaBuilder::$alwaysUsesNativeSchemaOperationsIfPossible; } /** @@ -1246,44 +1228,11 @@ public function getDoctrineConnection() 'driver' => $driver->getName(), 'serverVersion' => $this->getConfig('server_version'), ]), $driver); - - foreach ($this->doctrineTypeMappings as $name => $type) { - $this->doctrineConnection - ->getDatabasePlatform() - ->registerDoctrineTypeMapping($type, $name); - } } return $this->doctrineConnection; } - /** - * Register a custom Doctrine mapping type. - * - * @param Type|class-string $class - * @param string $name - * @param string $type - * @return void - * - * @throws \Doctrine\DBAL\Exception - * @throws \RuntimeException - */ - public function registerDoctrineType(Type|string $class, string $name, string $type): void - { - if (! $this->isDoctrineAvailable()) { - throw new RuntimeException( - 'Registering a custom Doctrine type requires Doctrine DBAL (doctrine/dbal).' - ); - } - - if (! Type::hasType($name)) { - Type::getTypeRegistry() - ->register($name, is_string($class) ? new $class() : $class); - } - - $this->doctrineTypeMappings[$name] = $type; - } - /** * Get the current PDO connection. * diff --git a/src/Illuminate/Database/DatabaseManager.php b/src/Illuminate/Database/DatabaseManager.php index 84602917ea1a..25747291cc60 100755 --- a/src/Illuminate/Database/DatabaseManager.php +++ b/src/Illuminate/Database/DatabaseManager.php @@ -2,7 +2,6 @@ namespace Illuminate\Database; -use Doctrine\DBAL\Types\Type; use Illuminate\Database\Connectors\ConnectionFactory; use Illuminate\Database\Events\ConnectionEstablished; use Illuminate\Support\Arr; @@ -11,7 +10,6 @@ use Illuminate\Support\Traits\Macroable; use InvalidArgumentException; use PDO; -use RuntimeException; /** * @mixin \Illuminate\Database\Connection @@ -57,13 +55,6 @@ class DatabaseManager implements ConnectionResolverInterface */ protected $reconnector; - /** - * The custom Doctrine column types. - * - * @var array - */ - protected $doctrineTypes = []; - /** * Create a new database manager instance. * @@ -204,8 +195,6 @@ protected function configure(Connection $connection, $type) // the connection, which will allow us to reconnect from the connections. $connection->setReconnector($this->reconnector); - $this->registerConfiguredDoctrineTypes($connection); - return $connection; } @@ -227,49 +216,6 @@ protected function setPdoForType(Connection $connection, $type = null) return $connection; } - /** - * Register custom Doctrine types with the connection. - * - * @param \Illuminate\Database\Connection $connection - * @return void - */ - protected function registerConfiguredDoctrineTypes(Connection $connection): void - { - foreach ($this->app['config']->get('database.dbal.types', []) as $name => $class) { - $this->registerDoctrineType($class, $name, $name); - } - - foreach ($this->doctrineTypes as $name => [$type, $class]) { - $connection->registerDoctrineType($class, $name, $type); - } - } - - /** - * Register a custom Doctrine type. - * - * @param string $class - * @param string $name - * @param string $type - * @return void - * - * @throws \Doctrine\DBAL\Exception - * @throws \RuntimeException - */ - public function registerDoctrineType(string $class, string $name, string $type): void - { - if (! class_exists('Doctrine\DBAL\Connection')) { - throw new RuntimeException( - 'Registering a custom Doctrine type requires Doctrine DBAL (doctrine/dbal).' - ); - } - - if (! Type::hasType($name)) { - Type::addType($name, $class); - } - - $this->doctrineTypes[$name] = [$type, $class]; - } - /** * Disconnect from the given database and remove from local cache. * From b60b221599c13573883565912d0e99101c53f25e Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 18 Dec 2023 19:41:19 +0330 Subject: [PATCH 38/52] remove unused config methods and property --- src/Illuminate/Database/Connection.php | 12 ------------ src/Illuminate/Database/Schema/Builder.php | 22 ---------------------- src/Illuminate/Support/Facades/DB.php | 1 + 3 files changed, 1 insertion(+), 34 deletions(-) diff --git a/src/Illuminate/Database/Connection.php b/src/Illuminate/Database/Connection.php index 7fd2c1afa7d9..fbf59a357c71 100755 --- a/src/Illuminate/Database/Connection.php +++ b/src/Illuminate/Database/Connection.php @@ -1200,18 +1200,6 @@ public function useWriteConnectionWhenReading($value = true) return $this; } - /** - * Indicates whether native alter operations will be used when dropping, renaming, or modifying columns, even if Doctrine DBAL is installed. - * - * @deprecated Will be removed in a future Laravel version. - * - * @return bool - */ - public function usingNativeSchemaOperations() - { - return ! class_exists('Doctrine\DBAL\Connection') || SchemaBuilder::$alwaysUsesNativeSchemaOperationsIfPossible; - } - /** * Get the Doctrine DBAL database connection instance. * diff --git a/src/Illuminate/Database/Schema/Builder.php b/src/Illuminate/Database/Schema/Builder.php index 24e2543a3aa4..658828195396 100755 --- a/src/Illuminate/Database/Schema/Builder.php +++ b/src/Illuminate/Database/Schema/Builder.php @@ -45,15 +45,6 @@ class Builder */ public static $defaultMorphKeyType = 'int'; - /** - * Indicates whether Doctrine DBAL usage will be prevented if possible when dropping, renaming, and modifying columns. - * - * @deprecated Will be removed in a future Laravel version. - * - * @var bool - */ - public static $alwaysUsesNativeSchemaOperationsIfPossible = false; - /** * Create a new database Schema manager. * @@ -114,19 +105,6 @@ 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. - * - * @deprecated Will be removed in a future Laravel version. - * - * @param bool $value - * @return void - */ - public static function useNativeSchemaOperationsIfPossible(bool $value = true) - { - static::$alwaysUsesNativeSchemaOperationsIfPossible = $value; - } - /** * Create a database in the schema. * diff --git a/src/Illuminate/Support/Facades/DB.php b/src/Illuminate/Support/Facades/DB.php index 596909319edf..5fdb74ce6877 100755 --- a/src/Illuminate/Support/Facades/DB.php +++ b/src/Illuminate/Support/Facades/DB.php @@ -95,6 +95,7 @@ * @method static string getTablePrefix() * @method static \Illuminate\Database\Connection setTablePrefix(string $prefix) * @method static \Illuminate\Database\Grammar withTablePrefix(\Illuminate\Database\Grammar $grammar) + * @method static string getServerVersion() * @method static void resolverFor(string $driver, \Closure $callback) * @method static mixed getResolver(string $driver) * @method static mixed transaction(\Closure $callback, int $attempts = 1) From 35666b3696308fe5e4368dc60bdf7d54f1ddb2dd Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 19 Dec 2023 19:04:43 +0330 Subject: [PATCH 39/52] remove redundant semicolon --- src/Illuminate/Database/Console/ShowCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Console/ShowCommand.php b/src/Illuminate/Database/Console/ShowCommand.php index 9a0712af25f9..05dc2138f521 100644 --- a/src/Illuminate/Database/Console/ShowCommand.php +++ b/src/Illuminate/Database/Console/ShowCommand.php @@ -19,7 +19,7 @@ class ShowCommand extends DatabaseInspectionCommand */ protected $signature = 'db:show {--database= : The database connection} {--json : Output the database information as JSON} - {--counts : Show the table row count Note: This can be slow on large databases }; + {--counts : Show the table row count Note: This can be slow on large databases } {--views : Show the database views Note: This can be slow on large databases } {--types : Show the user-defined types}'; From 3a8acd2bee3946e3e342d21abf15e86d2c458727 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 21 Dec 2023 23:15:48 +0330 Subject: [PATCH 40/52] force re-run tests From 29cfb8133191363849e9e53a3c9c9ecc7d1b7e54 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 22 Dec 2023 03:36:43 +0330 Subject: [PATCH 41/52] remove doctrine related conflicts reverts #49438 and #49456 --- composer.json | 2 -- src/Illuminate/Database/composer.json | 4 ---- 2 files changed, 6 deletions(-) diff --git a/composer.json b/composer.json index 50df57146ad9..90ec88d74a67 100644 --- a/composer.json +++ b/composer.json @@ -120,8 +120,6 @@ "psr/simple-cache-implementation": "1.0|2.0|3.0" }, "conflict": { - "carbonphp/carbon-doctrine-types": "<3.0.0|>=4.0", - "doctrine/dbal": "<4.0.0|>=5.0", "tightenco/collect": "<5.5.33" }, "autoload": { diff --git a/src/Illuminate/Database/composer.json b/src/Illuminate/Database/composer.json index 0fa0f54a87dd..3a79f754e44e 100644 --- a/src/Illuminate/Database/composer.json +++ b/src/Illuminate/Database/composer.json @@ -34,10 +34,6 @@ "dev-master": "11.x-dev" } }, - "conflict": { - "carbonphp/carbon-doctrine-types": "<3.0.0|>=4.0", - "doctrine/dbal": "<4.0.0|>=5.0" - }, "suggest": { "ext-filter": "Required to use the Postgres database driver.", "fakerphp/faker": "Required to use the eloquent factory builder (^1.21).", From fc4dd91060ee46f59f0015c216d758419a73f385 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 22 Dec 2023 04:22:52 +0330 Subject: [PATCH 42/52] remove doctrine/dbal from require-dev --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 90ec88d74a67..c292af255c20 100644 --- a/composer.json +++ b/composer.json @@ -96,7 +96,6 @@ "ext-gmp": "*", "ably/ably-php": "^1.0", "aws/aws-sdk-php": "^3.235.5", - "doctrine/dbal": "^4.0", "fakerphp/faker": "^1.21", "guzzlehttp/guzzle": "^7.6", "league/flysystem-aws-s3-v3": "^3.0", From fa197e086516724e5466c0f4bcc55b03943de321 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 22 Dec 2023 04:25:46 +0330 Subject: [PATCH 43/52] Revert "remove doctrine/dbal from require-dev" This reverts commit fc4dd91060ee46f59f0015c216d758419a73f385. --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index c292af255c20..90ec88d74a67 100644 --- a/composer.json +++ b/composer.json @@ -96,6 +96,7 @@ "ext-gmp": "*", "ably/ably-php": "^1.0", "aws/aws-sdk-php": "^3.235.5", + "doctrine/dbal": "^4.0", "fakerphp/faker": "^1.21", "guzzlehttp/guzzle": "^7.6", "league/flysystem-aws-s3-v3": "^3.0", From 2fb96c46fa667f5d381489b18b323a9c57d63af9 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 25 Dec 2023 19:10:24 +0330 Subject: [PATCH 44/52] revert unrelated changes --- src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php index b332922e8464..982ca3a786e0 100644 --- a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php @@ -37,7 +37,7 @@ class SQLiteGrammar extends Grammar */ public function compileTableExists() { - return "select sql from sqlite_master where type = 'table' and name = ?"; + return "select * from sqlite_master where type = 'table' and name = ?"; } /** From d7e1ddf950d414f56629ecd79ce23631acab3c36 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 25 Dec 2023 20:39:27 +0330 Subject: [PATCH 45/52] disable foreign key constraints when altering table on SQLite --- .../Schema/Grammars/SQLiteGrammar.php | 7 +++-- .../Database/SchemaBuilderTest.php | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php index 982ca3a786e0..bf03157e3885 100644 --- a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php @@ -350,7 +350,10 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $table = $this->wrap($this->getTablePrefix().$table); $columnNames = implode(', ', $columnNames); - return array_merge([ + $foreignKeyConstraintsEnabled = $connection->scalar('pragma foreign_keys'); + + return array_filter(array_merge([ + $foreignKeyConstraintsEnabled ? $this->compileDisableForeignKeyConstraints() : null, sprintf('create table %s (%s%s%s)', $tempTable, implode(', ', $columns), @@ -360,7 +363,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection sprintf('insert into %s (%s) select %s from %s', $tempTable, $columnNames, $columnNames, $table), sprintf('drop table %s', $table), sprintf('alter table %s rename to %s', $tempTable, $table), - ], $indexes); + ], $indexes, [$foreignKeyConstraintsEnabled ? $this->compileEnableForeignKeyConstraints() : null])); } /** diff --git a/tests/Integration/Database/SchemaBuilderTest.php b/tests/Integration/Database/SchemaBuilderTest.php index ff92102515cd..83742130e33e 100644 --- a/tests/Integration/Database/SchemaBuilderTest.php +++ b/tests/Integration/Database/SchemaBuilderTest.php @@ -340,4 +340,33 @@ public function testGetCompoundForeignKeys() && $foreign['foreign_columns'] === ['b', 'a'] )); } + + public function testAlteringTableWithForeignKeyConstraintsEnabledOnSqlite() + { + Schema::enableForeignKeyConstraints(); + + Schema::create('parents', function (Blueprint $table) { + $table->id(); + $table->text('name'); + }); + + Schema::create('children', function (Blueprint $table) { + $table->foreignId('parent_id')->constrained(); + }); + + $id = DB::table('parents')->insertGetId(['name' => 'foo']); + DB::table('children')->insert(['parent_id' => $id]); + + Schema::table('parents', function (Blueprint $table) { + $table->string('name')->change(); + }); + + $foreignKeys = Schema::getForeignKeys('children'); + + $this->assertCount(1, $foreignKeys); + $this->assertTrue(collect($foreignKeys)->contains( + fn ($foreign) => $foreign['columns'] === ['parent_id'] + && $foreign['foreign_table'] === 'parents' && $foreign['foreign_columns'] === ['id'] + )); + } } From 0aa3207177f11c9b68e69c44d4a125628816ea2d Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 25 Dec 2023 20:41:38 +0330 Subject: [PATCH 46/52] fix styling --- src/Illuminate/Database/Console/DatabaseInspectionCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Console/DatabaseInspectionCommand.php b/src/Illuminate/Database/Console/DatabaseInspectionCommand.php index 6c1908cad405..fbbe41bdd448 100644 --- a/src/Illuminate/Database/Console/DatabaseInspectionCommand.php +++ b/src/Illuminate/Database/Console/DatabaseInspectionCommand.php @@ -22,7 +22,7 @@ abstract class DatabaseInspectionCommand extends Command protected function getConnectionName(ConnectionInterface $connection, $database) { return match (true) { - $connection instanceof MySqlConnection && $connection->isMaria() => 'MariaDB', + $connection instanceof MySqlConnection && $connection->isMaria() => 'MariaDB', $connection instanceof MySqlConnection => 'MySQL', $connection instanceof PostgresConnection => 'PostgreSQL', $connection instanceof SQLiteConnection => 'SQLite', From 606663d5a70f98e7b07b0518c0eae20a686c20c8 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 26 Dec 2023 00:58:47 +0330 Subject: [PATCH 47/52] consider backticks when parsing collate on SQLite --- src/Illuminate/Database/Query/Processors/SQLiteProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Query/Processors/SQLiteProcessor.php b/src/Illuminate/Database/Query/Processors/SQLiteProcessor.php index 4d546b524983..0415132b5ad9 100644 --- a/src/Illuminate/Database/Query/Processors/SQLiteProcessor.php +++ b/src/Illuminate/Database/Query/Processors/SQLiteProcessor.php @@ -36,7 +36,7 @@ public function processColumns($results, $sql = '') $type = strtolower($result->type); $collation = preg_match('/\b'.preg_quote($result->name) - .'\b[^,(]+(?:\([^()]+\)[^,]*)?(?:(?:default|check|as)\s*(?:\(.*?\))?[^,]*)*collate\s+["\']?(\w+)/i', + .'\b[^,(]+(?:\([^()]+\)[^,]*)?(?:(?:default|check|as)\s*(?:\(.*?\))?[^,]*)*collate\s+["\'`]?(\w+)/i', $sql, $matches) === 1 ? strtolower($matches[1]) : null; return [ From 02eef27630c76c1d58fcf168332bf983547af533 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 27 Dec 2023 17:38:30 +0330 Subject: [PATCH 48/52] update facade docblocks --- src/Illuminate/Support/Facades/DB.php | 1 - src/Illuminate/Support/Facades/Schema.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Illuminate/Support/Facades/DB.php b/src/Illuminate/Support/Facades/DB.php index 5fdb74ce6877..2bf6e06d3d1f 100755 --- a/src/Illuminate/Support/Facades/DB.php +++ b/src/Illuminate/Support/Facades/DB.php @@ -59,7 +59,6 @@ * @method static \Illuminate\Database\Connection setRecordModificationState(bool $value) * @method static void forgetRecordModificationState() * @method static \Illuminate\Database\Connection useWriteConnectionWhenReading(bool $value = true) - * @method static bool usingNativeSchemaOperations() * @method static \Doctrine\DBAL\Connection getDoctrineConnection() * @method static \PDO getPdo() * @method static \PDO|\Closure|null getRawPdo() diff --git a/src/Illuminate/Support/Facades/Schema.php b/src/Illuminate/Support/Facades/Schema.php index 8d4d9b0c0dc8..e2731895f59a 100755 --- a/src/Illuminate/Support/Facades/Schema.php +++ b/src/Illuminate/Support/Facades/Schema.php @@ -7,7 +7,6 @@ * @method static void defaultMorphKeyType(string $type) * @method static void morphUsingUuids() * @method static void morphUsingUlids() - * @method static void useNativeSchemaOperationsIfPossible(bool $value = true) * @method static bool createDatabase(string $name) * @method static bool dropDatabaseIfExists(string $name) * @method static bool hasTable(string $table) From 5e99d964272e754e3c8adcbb9c3352fb8b2ce7dd Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 27 Dec 2023 18:34:46 +0330 Subject: [PATCH 49/52] remove doctrine connection --- composer.json | 1 - src/Illuminate/Database/Connection.php | 33 ---- src/Illuminate/Database/MySqlConnection.php | 11 -- .../PDO/Concerns/ConnectsToDatabase.php | 28 --- src/Illuminate/Database/PDO/Connection.php | 186 ------------------ src/Illuminate/Database/PDO/MySqlDriver.php | 19 -- .../Database/PDO/PostgresDriver.php | 19 -- src/Illuminate/Database/PDO/SQLiteDriver.php | 19 -- .../Database/PDO/SqlServerConnection.php | 152 -------------- .../Database/PDO/SqlServerDriver.php | 30 --- .../Database/PostgresConnection.php | 11 -- src/Illuminate/Database/SQLiteConnection.php | 11 -- .../Database/SqlServerConnection.php | 11 -- src/Illuminate/Support/Facades/DB.php | 1 - 14 files changed, 532 deletions(-) delete mode 100644 src/Illuminate/Database/PDO/Concerns/ConnectsToDatabase.php delete mode 100644 src/Illuminate/Database/PDO/Connection.php delete mode 100644 src/Illuminate/Database/PDO/MySqlDriver.php delete mode 100644 src/Illuminate/Database/PDO/PostgresDriver.php delete mode 100644 src/Illuminate/Database/PDO/SQLiteDriver.php delete mode 100644 src/Illuminate/Database/PDO/SqlServerConnection.php delete mode 100644 src/Illuminate/Database/PDO/SqlServerDriver.php diff --git a/composer.json b/composer.json index 90ec88d74a67..c292af255c20 100644 --- a/composer.json +++ b/composer.json @@ -96,7 +96,6 @@ "ext-gmp": "*", "ably/ably-php": "^1.0", "aws/aws-sdk-php": "^3.235.5", - "doctrine/dbal": "^4.0", "fakerphp/faker": "^1.21", "guzzlehttp/guzzle": "^7.6", "league/flysystem-aws-s3-v3": "^3.0", diff --git a/src/Illuminate/Database/Connection.php b/src/Illuminate/Database/Connection.php index fbf59a357c71..502ca290e833 100755 --- a/src/Illuminate/Database/Connection.php +++ b/src/Illuminate/Database/Connection.php @@ -5,7 +5,6 @@ use Carbon\CarbonInterval; use Closure; use DateTimeInterface; -use Doctrine\DBAL\Connection as DoctrineConnection; use Exception; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Database\Events\QueryExecuted; @@ -188,13 +187,6 @@ class Connection implements ConnectionInterface */ protected $beforeExecutingCallbacks = []; - /** - * The instance of Doctrine connection. - * - * @var \Doctrine\DBAL\Connection - */ - protected $doctrineConnection; - /** * The connection resolvers. * @@ -981,8 +973,6 @@ protected function tryAgainIfCausedByLostConnection(QueryException $e, $query, $ public function reconnect() { if (is_callable($this->reconnector)) { - $this->doctrineConnection = null; - return call_user_func($this->reconnector, $this); } @@ -1009,8 +999,6 @@ public function reconnectIfMissingConnection() public function disconnect() { $this->setPdo(null)->setReadPdo(null); - - $this->doctrineConnection = null; } /** @@ -1200,27 +1188,6 @@ public function useWriteConnectionWhenReading($value = true) return $this; } - /** - * Get the Doctrine DBAL database connection instance. - * - * @return \Doctrine\DBAL\Connection - */ - public function getDoctrineConnection() - { - if (is_null($this->doctrineConnection)) { - $driver = $this->getDoctrineDriver(); - - $this->doctrineConnection = new DoctrineConnection(array_filter([ - 'pdo' => $this->getPdo(), - 'dbname' => $this->getDatabaseName(), - 'driver' => $driver->getName(), - 'serverVersion' => $this->getConfig('server_version'), - ]), $driver); - } - - return $this->doctrineConnection; - } - /** * Get the current PDO connection. * diff --git a/src/Illuminate/Database/MySqlConnection.php b/src/Illuminate/Database/MySqlConnection.php index cb15e869cbba..00d212e9481d 100755 --- a/src/Illuminate/Database/MySqlConnection.php +++ b/src/Illuminate/Database/MySqlConnection.php @@ -3,7 +3,6 @@ namespace Illuminate\Database; use Exception; -use Illuminate\Database\PDO\MySqlDriver; use Illuminate\Database\Query\Grammars\MySqlGrammar as QueryGrammar; use Illuminate\Database\Query\Processors\MySqlProcessor; use Illuminate\Database\Schema\Grammars\MySqlGrammar as SchemaGrammar; @@ -120,14 +119,4 @@ protected function getDefaultPostProcessor() { return new MySqlProcessor; } - - /** - * Get the Doctrine DBAL driver. - * - * @return \Illuminate\Database\PDO\MySqlDriver - */ - protected function getDoctrineDriver() - { - return new MySqlDriver; - } } diff --git a/src/Illuminate/Database/PDO/Concerns/ConnectsToDatabase.php b/src/Illuminate/Database/PDO/Concerns/ConnectsToDatabase.php deleted file mode 100644 index a2354182bc2c..000000000000 --- a/src/Illuminate/Database/PDO/Concerns/ConnectsToDatabase.php +++ /dev/null @@ -1,28 +0,0 @@ -connection = $connection; - } - - /** - * Execute an SQL statement. - * - * @param string $statement - * @return int - */ - public function exec(string $statement): int - { - try { - $result = $this->connection->exec($statement); - - \assert($result !== false); - - return $result; - } catch (PDOException $exception) { - throw Exception::new($exception); - } - } - - /** - * Prepare a new SQL statement. - * - * @param string $sql - * @return \Doctrine\DBAL\Driver\Statement - * - * @throws \Doctrine\DBAL\Driver\PDO\Exception - */ - public function prepare(string $sql): StatementInterface - { - try { - return $this->createStatement( - $this->connection->prepare($sql) - ); - } catch (PDOException $exception) { - throw Exception::new($exception); - } - } - - /** - * Execute a new query against the connection. - * - * @param string $sql - * @return \Doctrine\DBAL\Driver\Result - */ - public function query(string $sql): ResultInterface - { - try { - $stmt = $this->connection->query($sql); - - \assert($stmt instanceof PDOStatement); - - return new Result($stmt); - } catch (PDOException $exception) { - throw Exception::new($exception); - } - } - - /** - * Get the last insert ID. - * - * @param string|null $name - * @return string|int - * - * @throws \Doctrine\DBAL\Driver\PDO\Exception - */ - public function lastInsertId($name = null): string|int - { - try { - if ($name === null) { - return $this->connection->lastInsertId(); - } - - return $this->connection->lastInsertId($name); - } catch (PDOException $exception) { - throw Exception::new($exception); - } - } - - /** - * Create a new statement instance. - * - * @param \PDOStatement $stmt - * @return \Doctrine\DBAL\Driver\PDO\Statement - */ - protected function createStatement(PDOStatement $stmt): Statement - { - return new Statement($stmt); - } - - /** - * Begin a new database transaction. - * - * @return void - */ - public function beginTransaction(): void - { - $this->connection->beginTransaction(); - } - - /** - * Commit a database transaction. - * - * @return void - */ - public function commit(): void - { - $this->connection->commit(); - } - - /** - * Rollback a database transaction. - * - * @return void - */ - public function rollBack(): void - { - $this->connection->rollBack(); - } - - /** - * Wrap quotes around the given input. - * - * @param string $input - * @param string $type - * @return string - */ - public function quote($input, $type = ParameterType::STRING): string - { - return $this->connection->quote($input, $type); - } - - /** - * Get the server version for the connection. - * - * @return string - */ - public function getServerVersion(): string - { - return $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION); - } - - /** - * Get the native PDO connection. - * - * @return \PDO - */ - public function getNativeConnection(): PDO - { - return $this->connection; - } -} diff --git a/src/Illuminate/Database/PDO/MySqlDriver.php b/src/Illuminate/Database/PDO/MySqlDriver.php deleted file mode 100644 index 54ac37536189..000000000000 --- a/src/Illuminate/Database/PDO/MySqlDriver.php +++ /dev/null @@ -1,19 +0,0 @@ -connection = $connection; - } - - /** - * Prepare a new SQL statement. - * - * @param string $sql - * @return \Doctrine\DBAL\Driver\Statement - */ - public function prepare(string $sql): StatementInterface - { - return new Statement( - $this->connection->prepare($sql) - ); - } - - /** - * Execute a new query against the connection. - * - * @param string $sql - * @return \Doctrine\DBAL\Driver\Result - */ - public function query(string $sql): Result - { - return $this->connection->query($sql); - } - - /** - * Execute an SQL statement. - * - * @param string $statement - * @return int - */ - public function exec(string $statement): int - { - return $this->connection->exec($statement); - } - - /** - * Get the last insert ID. - * - * @param string|null $name - * @return string|int - */ - public function lastInsertId($name = null): string|int - { - if ($name === null) { - return $this->connection->lastInsertId($name); - } - - return $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?') - ->execute([$name]) - ->fetchOne(); - } - - /** - * Begin a new database transaction. - * - * @return void - */ - public function beginTransaction(): void - { - $this->connection->beginTransaction(); - } - - /** - * Commit a database transaction. - * - * @return void - */ - public function commit(): void - { - $this->connection->commit(); - } - - /** - * Rollback a database transaction. - * - * @return void - */ - public function rollBack(): void - { - $this->connection->rollBack(); - } - - /** - * Wrap quotes around the given input. - * - * @param string $value - * @param int $type - * @return string - */ - public function quote($value, $type = ParameterType::STRING): string - { - $val = $this->connection->quote($value, $type); - - // Fix for a driver version terminating all values with null byte... - if (\is_string($val) && str_contains($val, "\0")) { - $val = \substr($val, 0, -1); - } - - return $val; - } - - /** - * Get the server version for the connection. - * - * @return string - */ - public function getServerVersion(): string - { - return $this->connection->getServerVersion(); - } - - /** - * Get the native PDO connection. - * - * @return \PDO - */ - public function getNativeConnection(): PDO - { - return $this->connection->getWrappedConnection(); - } -} diff --git a/src/Illuminate/Database/PDO/SqlServerDriver.php b/src/Illuminate/Database/PDO/SqlServerDriver.php deleted file mode 100644 index ac7b8a1aedef..000000000000 --- a/src/Illuminate/Database/PDO/SqlServerDriver.php +++ /dev/null @@ -1,30 +0,0 @@ - Date: Wed, 3 Jan 2024 03:47:50 +0330 Subject: [PATCH 50/52] formatting --- tests/Integration/Database/SchemaBuilderTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Integration/Database/SchemaBuilderTest.php b/tests/Integration/Database/SchemaBuilderTest.php index 9bcd922112c7..b66aa137671e 100644 --- a/tests/Integration/Database/SchemaBuilderTest.php +++ b/tests/Integration/Database/SchemaBuilderTest.php @@ -368,6 +368,7 @@ public function testAlteringTableWithForeignKeyConstraintsEnabledOnSqlite() fn ($foreign) => $foreign['columns'] === ['parent_id'] && $foreign['foreign_table'] === 'parents' && $foreign['foreign_columns'] === ['id'] )); + } public function testSystemVersionedTables() { From 055e1cd170bda177d65af2d9240c56dfe4ed2300 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 9 Jan 2024 13:01:53 +0330 Subject: [PATCH 51/52] fix conflicts --- src/Illuminate/Database/DatabaseManager.php | 1 + tests/Integration/Database/SchemaBuilderTest.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Database/DatabaseManager.php b/src/Illuminate/Database/DatabaseManager.php index 521b9144e026..76680ff2f2d4 100755 --- a/src/Illuminate/Database/DatabaseManager.php +++ b/src/Illuminate/Database/DatabaseManager.php @@ -10,6 +10,7 @@ use Illuminate\Support\Traits\Macroable; use InvalidArgumentException; use PDO; +use RuntimeException; /** * @mixin \Illuminate\Database\Connection diff --git a/tests/Integration/Database/SchemaBuilderTest.php b/tests/Integration/Database/SchemaBuilderTest.php index 7501fd429ccc..52ac4f7c17fc 100644 --- a/tests/Integration/Database/SchemaBuilderTest.php +++ b/tests/Integration/Database/SchemaBuilderTest.php @@ -341,7 +341,7 @@ public function testGetCompoundForeignKeys() )); } - public function testAlteringTableWithForeignKeyConstraintsEnabledOnSqlite() + public function testAlteringTableWithForeignKeyConstraintsEnabled() { Schema::enableForeignKeyConstraints(); From 63ea4d69de556c0f4bc694591863f9dc7c0a6dbf Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 9 Jan 2024 10:38:59 -0600 Subject: [PATCH 52/52] formatting --- src/Illuminate/Database/Console/ShowCommand.php | 2 +- src/Illuminate/Database/Console/TableCommand.php | 2 -- .../Database/Query/Processors/SQLiteProcessor.php | 8 +++++--- src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php | 5 ++--- src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php | 4 ++-- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Illuminate/Database/Console/ShowCommand.php b/src/Illuminate/Database/Console/ShowCommand.php index 05dc2138f521..d711a01bc685 100644 --- a/src/Illuminate/Database/Console/ShowCommand.php +++ b/src/Illuminate/Database/Console/ShowCommand.php @@ -21,7 +21,7 @@ class ShowCommand extends DatabaseInspectionCommand {--json : Output the database information as JSON} {--counts : Show the table row count Note: This can be slow on large databases } {--views : Show the database views Note: This can be slow on large databases } - {--types : Show the user-defined types}'; + {--types : Show the user defined types}'; /** * The console command description. diff --git a/src/Illuminate/Database/Console/TableCommand.php b/src/Illuminate/Database/Console/TableCommand.php index 5a86c0c81cd0..0bd67e1fdba1 100644 --- a/src/Illuminate/Database/Console/TableCommand.php +++ b/src/Illuminate/Database/Console/TableCommand.php @@ -38,9 +38,7 @@ class TableCommand extends DatabaseInspectionCommand public function handle(ConnectionResolverInterface $connections) { $connection = $connections->connection($this->input->getOption('database')); - $schema = $connection->getSchemaBuilder(); - $tables = $schema->getTables(); $tableName = $this->argument('table') ?: select( diff --git a/src/Illuminate/Database/Query/Processors/SQLiteProcessor.php b/src/Illuminate/Database/Query/Processors/SQLiteProcessor.php index 0415132b5ad9..123718eb1ba0 100644 --- a/src/Illuminate/Database/Query/Processors/SQLiteProcessor.php +++ b/src/Illuminate/Database/Query/Processors/SQLiteProcessor.php @@ -35,9 +35,11 @@ public function processColumns($results, $sql = '') $type = strtolower($result->type); - $collation = preg_match('/\b'.preg_quote($result->name) - .'\b[^,(]+(?:\([^()]+\)[^,]*)?(?:(?:default|check|as)\s*(?:\(.*?\))?[^,]*)*collate\s+["\'`]?(\w+)/i', - $sql, $matches) === 1 ? strtolower($matches[1]) : null; + $collation = preg_match( + '/\b'.preg_quote($result->name).'\b[^,(]+(?:\([^()]+\)[^,]*)?(?:(?:default|check|as)\s*(?:\(.*?\))?[^,]*)*collate\s+["\'`]?(\w+)/i', + $sql, + $matches + ) === 1 ? strtolower($matches[1]) : null; return [ 'name' => $result->name, diff --git a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php index 3d4d4c42d154..07bd41291414 100755 --- a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php @@ -359,9 +359,8 @@ public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Conne { $version = $connection->getServerVersion(); - if (($connection->isMaria() && version_compare($version, '10.5.2', '<')) - || (! $connection->isMaria() && version_compare($version, '8.0.3', '<')) - ) { + if (($connection->isMaria() && version_compare($version, '10.5.2', '<')) || + (! $connection->isMaria() && version_compare($version, '8.0.3', '<'))) { $column = collect($connection->getSchemaBuilder()->getColumns($blueprint->getTable())) ->firstWhere('name', $command->from); diff --git a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php index bf03157e3885..fcbaf0798685 100644 --- a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php @@ -237,7 +237,7 @@ protected function getForeignKey($foreign) // If this foreign key specifies the action to be taken on update we will add // that to the statement here. We'll append it to this SQL and then return - // the SQL so we can keep adding any other foreign constraints onto this. + // this SQL so we can keep adding any other foreign constraints to this. if (! is_null($foreign->onUpdate)) { $sql .= " on update {$foreign->onUpdate}"; } @@ -572,7 +572,7 @@ public function compileRenameIndex(Blueprint $blueprint, Fluent $command, Connec } if ($index['primary']) { - throw new RuntimeException('Sqlite does not support alter primary key.'); + throw new RuntimeException('SQLite does not support altering primary keys.'); } if ($index['unique']) {