-
Notifications
You must be signed in to change notification settings - Fork 116
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
"FOREIGN KEY constraint failed" after updating to 4.4.0 #741
Comments
Thanks for opening this issue. How could I reproduce this problem?
What does the table schema look like? Does |
Good point, forgot to investigate and mention the real cause of this issue. The bug occur because this table is in relation ManyToMany with other table, and we have field constraints between the primary key in this model and the join table table that connects those 2 tables. So the way to reproduce it would be:
The actual behavior: Because the sqlite logic is not just modifying the original table, but rather creating totally new table, copying the values. My guess the issue will be the same for both ManyToMany relation and also for hasMany relation if you have a proper field constraint set on the relation. |
Thanks for the more detailed steps. I'll have some time in a few days to look into this. |
Attempt to reproduce the problem reported in #741 but added a test that covers the scenario based on current information. Perhaps the problem can be reproduced in CI. There is also a chance that the test suite harness makes this challenging to reproduce because of wrapping transactions.
I was able to reproduce the error you're seeing, but I'm not sure that another <?php
declare(strict_types=1);
use Migrations\AbstractMigration;
class CreateUsersRolesTables extends AbstractMigration
{
/**
* Change Method.
*
* More information on this method is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
* @return void
*/
public function change(): void
{
$roles = $this->table('constraint_roles');
$roles->addColumn('name', 'string')
->create();
$users = $this->table('constraint_users');
$users->addColumn('username', 'string')
->addColumn('role_id', 'integer', ['null' => false])
->addForeignKey(['role_id'], $roles->getTable(), ['id'])
->create();
$adapter = $this->getAdapter();
$adapter->insert($roles->getTable(), ['name' => 'admin']);
$adapter->insert($users->getTable(), ['username' => 'test', 'role_id' => 1]);
}
} and <?php
declare(strict_types=1);
use Migrations\AbstractMigration;
class ModifyRoles extends AbstractMigration
{
/**
* Change Method.
*
* More information on this method is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
* @return void
*/
public function change(): void
{
$roles = $this->table('constraint_roles');
$roles
->addColumn('description', 'string', ['default' => 'short desc'])
->update();
}
} I'm able to reproduce the issue if my connection includes
foreign_keys are already disabled. I think what is happening is a bug in the migrations/phinx integration where |
I also don't think that doing Because the logic is creating new table and doing rename, just disabling it would still result in having bad table state. If it can't be just altered, then the proper way would be to actually list all indexes, remove them, and them add them back later. This way foreign constraint errors should not happen. Thanks for your investigation, will also try to debug it more, starting with printing all the sql commands it execute (and try to pinpoint it more specific). |
Yes, the newly created table is lacking constraints. Constraints are currently not part of the schema that is preserved. Only triggers and indicies are preserved. This is also how phinx behaved. We should be able to expand the functionality of sqlite schema changes to also preserve foreign keys. We'll need to collect all existing foreign keys, generate or mangle the existing constraint definitions and drop/create new constraints. |
十分抱歉 !我的账户在前段时间被黑客盗用,并且传播相同的病毒文件,如果你们遇到要下载一个fix.zip的千万不要下载!就是它导致我账户被攻占的 |
I think I've tracked this down to how transactions are being managed. The existing logic in migrations will use ᐉ sqlite3 app.sqlite
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> pragma foreign_keys = on;
sqlite> pragma foreign_keys;
1
sqlite> pragma foreign_keys = off;
sqlite> pragma foreign_keys;
0
sqlite> pragma foreign_keys = on;
sqlite> begin;
sqlite> pragma foreign_keys = off;
sqlite> pragma foreign_keys;
1
sqlite> commit;
sqlite> pragma foreign_keys;
1 While phinx also used transactions, the migrations plugin did not preserve the $this->getAdapter()->rollbackTransaction();
// Migration contents here
$this->getAdapter()->beginTransaction(); This will close the implicit transactions that migrations is creating, allow your migration to run and then open a new empty transaction. This tricks the framework into doing the right thing. I'll work on a fix now that the problem has been identified. |
There are schema operations that you don't want a wrapping transaction, like if you're modifying a sqlite database. This new method allows migrations to opt-out of having a transaction wrapped around each migration automatically. Refs #741
@kolorafa I opened #745 to add a hook method to migrations that allow you to escape the wrapping transaction if you need (which you now do for sqlite alters). I considered making this behavior automatic, but felt that it could have unsafe consequences and chose an opt-in API instead. I would appreciate any feedback you have on this solution. |
This is a (multiple allowed):
bug
enhancement
feature-discussion (RFC)
CakePHP Version: 5.1.1
Migrations plugin version: 4.4.0 (works when downgraded to 4.3.2)
Database server (MySQL, SQLite, Postgres): SQLite Library => 3.37.2
PHP Version: 8.3
Platform / OS: Ubuntu-20.04 LTS / Linux Mint 21.03
What you did
Expected Behavior
Work :)
Actual Behavior
The issue happen in:
vendor/cakephp/migrations/src/Db/Adapter/SqliteAdapter.php:1049
It looks like the logic for the alter is to copy the table to
tmp_some_table
, then drop existing and rename the new one.I tracked the issue down to some old issue
cakephp/phinx#2130
ndm2/phinx@9191730#diff-8bd456d859714dd59755ea246b4d2e1584560144dde972213c28faf1d51eef8eR849
The text was updated successfully, but these errors were encountered: