#29182: SQLite database migration breaks ForeignKey constraint, leaving
<table_name>__old in db schema
----------------------------------+--------------------------------------
     Reporter:  ezaquarii         |                    Owner:  nobody
         Type:  Uncategorized     |                   Status:  new
    Component:  Migrations        |                  Version:  2.0
     Severity:  Normal            |               Resolution:
     Keywords:  sqlite migration  |             Triage Stage:  Unreviewed
    Has patch:  0                 |      Needs documentation:  0
  Needs tests:  0                 |  Patch needs improvement:  0
Easy pickings:  0                 |                    UI/UX:  0
----------------------------------+--------------------------------------
Description changed by ezaquarii:

Old description:

> SQLite table alteration uses rename and copy method. When running a
> database migration from Django command (via `call_command('migrate')`),
> database is left with references to <table_name>__old table that has been
> dropped after migration.
>
> This happens only when running SQLite DB migrations from management
> command.
> Running migrations via `./manage.py migrate` does not cause any issues.
>
> This is the demonstration:
> https://github.com/ezaquarii/openvpn-at-home/tree/sqlite-migration-bug
>
> Steps to reproduce the bug
> 1. build the project with `make devel`
>
> 2. enter Python virtualenv by `source backend/env/bin/activate` and enter
> `backend` subdirectory
>
> 3. run `./manage.py bootstrap` to initialize the database
>
> 4. bootstrap command runs db migration by calling
> `call_command('migrate')` in
> `openvpnathome/apps/management/management/commands/bootstrap.py:37`:
>     - `openvpn_server` table is created
>     - `openvpn_client` table is created; it uses foreign key to
> `openvpn_server`
>     -  another `openvpn_server` migration is run, adding dummy field
>     - `openvpn_server` table is renamed to `openvpn_server__old`
>     - `openvpn_client` foreign key is re-linked to `openvpn_server__old`
>     - new table `openvpn_server` is created and data from
> `openvpn_server__old` is copied with applied alterations
>     - `openvpn_server__old` is dropped
>     - `openvpn_client` still references `openvpn_sever__old` - ForeignKey
> constraint is **not** updated and DB is left in unusable state
> 6. try creating `Client` object in django shell - SQL cannot be executed
> due to missing table
>

> Exact commands (available in provided `bug_repro.sh` script):
> {{{
> cd backend
> make devel
> source ./env/bin/activate
> ./manage.py bootstrap
>
> echo '
> Now type that:
>
> ./manage.py shell
> In [1]: from openvpnathome.apps.openvpn.models import Server, Client
>
> In [2]: Client.objects.create()
>
> ... snip ...
>
> OperationalError: no such table: main.openvpn_server__old
> '
> }}}
>
> Exported database schema clearly references deleted `openvpn_server__old`
> table:
>
> {{{
> CREATE TABLE "openvpn_client" ("id" integer NOT NULL PRIMARY KEY
> AUTOINCREMENT, "created" datetime NOT NULL, "name" varchar(64) NOT NULL,
> "cert_id" integer NOT NULL REFERENCES "x509_cert" ("id") DEFERRABLE
> INITIALLY DEFERRED, "owner_id" integer NOT NULL REFERENCES
> "accounts_user" ("id") DEFERRABLE INITIALLY DEFERRED, "server_id" integer
> NOT NULL REFERENCES "openvpn_server__old" ("id") DEFERRABLE INITIALLY
> DEFERRED);
> }}}
>
> Please refer to `db_schema.sql` file placed in the `backend`
> subdirectory.
>
> Tested with Django versions: 2.0.1 and 2.0.2

New description:

 SQLite table alteration uses rename and copy method. When running a
 database migration from Django command (via `call_command('migrate')`),
 database is left with references to <table_name>__old table that has been
 dropped after migration.

 This happens only when running SQLite DB migrations from management
 command under transaction.
 Running migrations via `./manage.py migrate` does not cause any issues.

 This is the demonstration:
 https://github.com/ezaquarii/django-sqlite-migration-bug

 Steps to reproduce the bug
 1. run ./bootstrap.sh
 2. from import app.models import *
 3. Bravo.objects.create()
 4. SQL cannot be executed due to missing table `main.app_alpha__old`

     - `app/management/commands/bootstrap.py:9`: run the migration
     - `alpha` table is created
     - `bravo` table is created; it uses foreign key to `alpha`
     -  another `alpha` migration is run, adding dummy field
     - `alpha` table is renamed to `alpha__old`
     - `bravo` foreign key is re-linked to `alpha__old`
     - new table `alpha` is created and data from `alpha__old` is copied
 with applied alterations
     - `alpha__old` is dropped
     - `bravo` still references `alpha__old` - ForeignKey constraint is
 **not** updated and DB is left in unusable state


 {{{
 CREATE TABLE "app_bravo" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
 "dummy" varchar(10) NOT NULL, "alpha_id" integer NOT NULL REFERENCES
 "app_alpha__old" ("id") DEFERRABLE INITIALLY DEFERRED);
 }}}

 Tested with Django versions: 2.0.1 and 2.0.2

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/29182#comment:4>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/067.9a985504ad0190cb91037ecbf1f0f4d5%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to