-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SQLite Foreign Key Behavior BC Break #4243
Comments
It looks like the regex in dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php Lines 126 to 139 in f69c990
CREATE TABLE "users_contact" (
"user_id" integer not null,
"dect" varchar null,
"mobile" varchar null,
"email" varchar null,
foreign key("user_id") references "users"("id") on delete cascade on update cascade,
primary key ("user_id")
)
|
The problem is that it creates queries like, #4244 contains example code
The dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php Lines 859 to 864 in f69c990
(called in dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php Lines 119 to 120 in ce85bd8
-> the id field is used as a fallback when no
and it's in that case the 0 which then does not get escaped
|
A solution could be to change it to null as its expected on other parts of the code #4246 |
That one could be #4247 but it somehow does not feel that right as it adds a name where none was before |
Any thoughts about that @taylorotwell? |
In the list of changes since previous version, #3762 stands out as related by its title. EDIT: I see that you found it too in the linked bug report |
Jea, thats the change that "broke" it aka "enabled the bug" but it was already working "wrong" before it |
Is the enabled bug also located in |
It is, yes The problem is that numbers in reference constraint names are not escaped and/or that "ids" are used as names, depending on how you see it |
@MyIgel the issues you looked into seem a little "late", meaning they relate to listing foreign keys, but you also reported that "it creates queries like" CONSTRAINT 0 FOREIGN KEY (user_id) REFERENCES users (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE That sounds more interesting… is it possible that the issue is with the creation of the foreign key, as opposed to listing a table that has it? I'm not sure creating a foreign key named |
I'm not familiar with this change enough to propose a quick solution. Having a failing test at hand could help to understand the problem and facilitate the solution. |
The relevant stack traces are Creating the "interesting" constraint without escaping its name in \Doctrine\DBAL\Platforms\AbstractPlatform::getForeignKeyBaseDeclarationSQL:
And when getting the foreign keys from the sqlite db in \Doctrine\DBAL\Schema\SqliteSchemaManager::listTableForeignKeys:
The important points here:
My two change suggestions try to fix both of the points and i can create PRs for both of them if it helps |
Ok, I understand far better now, thanks a lot! Please do send the PRs for your suggested changes 👍 |
And here they are, also updated the links above |
Are newly-created FKs created without a name too? |
As these are unit test migrations that run before every test i would say no, they have no name |
Hi here is my call stack. I created main issue on laravel repository (laravel/framework#34093) and after re-checking my fresh installation, found that is not relevant to this problem and removed that part from main issue. I also added migration file that can be seen in call stack. SQLSTATE[HY000]: General error: 1 near "0": syntax error (SQL: CREATE TABLE tasks (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, account_id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, subject VARCHAR(255) NOT NULL COLLATE BINARY, start_date DATETIME NOT NULL, state INTEGER DEFAULT 0 NOT NULL, task_type INTEGER DEFAULT 0 NOT NULL, task_queue_id INTEGER NOT NULL, created_by_type VARCHAR(255) DEFAULT NULL COLLATE BINARY, created_by_id INTEGER DEFAULT NULL, created_at DATETIME DEFAULT NULL, updated_at DATETIME DEFAULT NULL, content CLOB DEFAULT NULL COLLATE BINARY, end_date DATETIME DEFAULT NULL, CONSTRAINT 0 FOREIGN KEY (user_id) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT 1 FOREIGN KEY (account_id) REFERENCES accounts (id) ON UPDATE NO ACTION ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE))
at vendor/laravel/framework/src/Illuminate/Database/Connection.php:671
667| // If an exception occurs when attempting to run a query, we'll format the error
668| // message to include the bindings with SQL, which will make this exception a
669| // lot more helpful to the developer instead of just the database's errors.
670| catch (Exception $e) {
> 671| throw new QueryException(
672| $query, $this->prepareBindings($bindings), $e
673| );
674| }
675|
1 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:72
Doctrine\DBAL\Driver\PDOException::("SQLSTATE[HY000]: General error: 1 near "0": syntax error")
2 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:67
PDOException::("SQLSTATE[HY000]: General error: 1 near "0": syntax error")
3 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:67
PDO::prepare()
4 vendor/laravel/framework/src/Illuminate/Database/Connection.php:458
Doctrine\DBAL\Driver\PDOConnection::prepare()
5 vendor/laravel/framework/src/Illuminate/Database/Connection.php:664
Illuminate\Database\Connection::Illuminate\Database\{closure}()
6 vendor/laravel/framework/src/Illuminate/Database/Connection.php:631
Illuminate\Database\Connection::runQueryCallback()
7 vendor/laravel/framework/src/Illuminate/Database/Connection.php:465
Illuminate\Database\Connection::run()
8 vendor/laravel/framework/src/Illuminate/Database/Schema/Blueprint.php:102
Illuminate\Database\Connection::statement()
9 vendor/laravel/framework/src/Illuminate/Database/Schema/Builder.php:290
Illuminate\Database\Schema\Blueprint::build()
10 vendor/laravel/framework/src/Illuminate/Database/Schema/Builder.php:151
Illuminate\Database\Schema\Builder::build()
11 vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:261
Illuminate\Database\Schema\Builder::table()
12 database/migrations/2020_05_04_085002_add_nullable_to_some_fields_in_tasks_table.php:19
Illuminate\Support\Facades\Facade::__callStatic()
13 vendor/laravel/framework/src/Illuminate/Database/Migrations/Migrator.php:392
AddNullableToSomeFieldsInTasksTable::up()
14 vendor/laravel/framework/src/Illuminate/Database/Migrations/Migrator.php:401
Illuminate\Database\Migrations\Migrator::Illuminate\Database\Migrations\{closure}()
15 vendor/laravel/framework/src/Illuminate/Database/Migrations/Migrator.php:200
Illuminate\Database\Migrations\Migrator::runMigration()
16 vendor/laravel/framework/src/Illuminate/Database/Migrations/Migrator.php:165
Illuminate\Database\Migrations\Migrator::runUp()
17 vendor/laravel/framework/src/Illuminate/Database/Migrations/Migrator.php:110
Illuminate\Database\Migrations\Migrator::runPending()
18 vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/MigrateCommand.php:72
Illuminate\Database\Migrations\Migrator::run()
19 vendor/laravel/framework/src/Illuminate/Database/Migrations/Migrator.php:541
Illuminate\Database\Console\Migrations\MigrateCommand::Illuminate\Database\Console\Migrations\{closure}()
20 vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/MigrateCommand.php:81
Illuminate\Database\Migrations\Migrator::usingConnection()
21 vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:37
Illuminate\Database\Console\Migrations\MigrateCommand::handle()
22 vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:37
call_user_func_array()
23 vendor/laravel/framework/src/Illuminate/Container/Util.php:37
Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()
24 vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:95
Illuminate\Container\Util::unwrapIfClosure()
25 vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:39
Illuminate\Container\BoundMethod::callBoundMethod()
26 vendor/laravel/framework/src/Illuminate/Container/Container.php:596
Illuminate\Container\BoundMethod::call()
27 vendor/laravel/framework/src/Illuminate/Console/Command.php:134
Illuminate\Container\Container::call()
28 vendor/symfony/console/Command/Command.php:258
Illuminate\Console\Command::execute()
29 vendor/laravel/framework/src/Illuminate/Console/Command.php:121
Symfony\Component\Console\Command\Command::run()
30 vendor/symfony/console/Application.php:916
Illuminate\Console\Command::run()
31 vendor/symfony/console/Application.php:264
Symfony\Component\Console\Application::doRunCommand()
32 vendor/symfony/console/Application.php:140
Symfony\Component\Console\Application::doRun()
33 vendor/laravel/framework/src/Illuminate/Console/Application.php:93
Symfony\Component\Console\Application::run()
34 vendor/laravel/framework/src/Illuminate/Console/Application.php:185
Illuminate\Console\Application::run()
35 vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php:263
Illuminate\Console\Application::call()
36 vendor/laravel/framework/src/Illuminate/Testing/PendingCommand.php:171
Illuminate\Foundation\Console\Kernel::call() <?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddNullableToSomeFieldsInTasksTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('tasks', function (Blueprint $table) {
$table->text('content')->nullable()->change();
$table->dateTime('end_date')->nullable()->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('tasks', function (Blueprint $table) {
$table->text('content')->change();
$table->dateTime('end_date')->change();
});
}
} |
Thanks for the stack trace! It shows that Laravel is calling Doctrine with an SQL query as string: https://github.com/laravel/framework/blob/caec40ed2695c2aa84b5eb195c822e3a961fc445/src/Illuminate/Database/Connection.php#L458 Where is that query built? Is it built by Blueprint, and built differently because Doctrine says SQlite supports foreign keys? |
Regarding |
For me the create table migration is not failing, but what is failing is the migration that alters table, for some reason it tries to drop an index that was not created or at least drop it with wrong name maybe? ... The create table
Then here the failing migration (alter table by deleting
I traced it down to this behaviour, but I'm not familiar really with dbal internals -> Method The diff dumping
dumping $diff->fromTable->getIndexes() for 2.10.2
Maybe the problem (newly enabled bug) is with |
… fk with multiple columns.
… fk with multiple columns.
…mprove other parts.
…mprove other parts.
…onalTestCase" This reverts commit 0c384b4.
…erFunctionalTestCase"" This reverts commit bf724a7.
When introducing SQLite foreign key support a case was not considered where not Doctrine DBAL is creating the schema, but some other code that is not naming foreign key constraints. This leads to triggr a bug that the code already had but was never before executed.
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
BC Break Report
Summary
A change in SQLite foreign key constraint behavior is a breaking change that was released as a patch release.
See: laravel/framework#34093
The text was updated successfully, but these errors were encountered: