#3615: Can't define forward references in fixtures using MySQL with InnoDB
-------------------------------------+-------------------------------------
               Reporter:  russellm   |          Owner:  nobody
                   Type:  Bug        |         Status:  new
              Milestone:             |      Component:  Database layer
                Version:  SVN        |  (models, ORM)
             Resolution:             |       Severity:  Normal
           Triage Stage:  Accepted   |       Keywords:  mysql innodb myisam
    Needs documentation:  0          |  reference fixture
Patch needs improvement:  1          |      Has patch:  1
                  UI/UX:  0          |    Needs tests:  0
                                     |  Easy pickings:  0
-------------------------------------+-------------------------------------

Comment (by jsdalton):

 Okay, I'm uploading a new patch which, while a bit rough around the edges,
 solves the core problem of executing constraint checks on table after data
 has been loaded to it with foreign_key_checks off. It passes both tests
 that I introduced (loading fixtures with future refs work, loading
 fixtures with nonexistent references errors) and since it entails just
 using a few SELECT statements it should be highly performant.

 Patch to follow, but here's a brief rundown.

 The basic approach was cribbed from this answer
 http://stackoverflow.com/questions/2250775/force-innodb-to-recheck-
 foreign-keys-on-a-table-tables . Whoever wrote the answer must be a DBA
 who loves wrapping it all up in a neat and tidy stored procedure package,
 but it took me about 10 minutes or so to sit down and unpack from all the
 abstractions. At its core, all it does is select all the key columns for a
 given table, then it does a SELECT on each of those that joins them to
 each other when the referring column id is null. This is what that
 statement looks like when I use concrete values (for a test table I have):


 {{{
 SELECT * FROM `fixtures_regress_book` as REFERRING
 LEFT JOIN `fixtures_regress_person` as REFERRED
 ON (REFERRING.`author_id` = REFERRED.`id`)
 WHERE REFERRING.`author_id` IS NOT NULL
 AND REFERRED.`id` IS NULL
 }}}


 Not so bad. Basically, any rows this statement return represent bad data.

 Implementing this wasn't too big of a deal. In loaddata, for each model
 that was loaded we now run the check on the associated table. The check is
 implemented in the MySQL connection class as
 `check_foreign_key_constraints_for_table()`. It first grabs all the key
 columns via a new method on the MySQL Introspection class, and then
 executes a version of the above SQL SELECT on each key column. If any bad
 rows are found, it raises an IntegrityError.

 This patch still needs work to clean a few up and ensure there is enough
 test coverage. Specifically:

 * `check_foreign_key_constraints_for_table()` is kind of weak right now.
 It probably should not raise IntegrityError directly, but instead just
 return an empty list if all goes well and a list of bad rows if not. The
 IntegrityError is probably best handled in whatever code is implementing
 (i.e. loaddata can figure out what to report)
 * We should probably strive to output a meaningful list of bad data.
 * The `mysql.DatabaseIntrospectionIntrospection.get_key_columns()` might
 need testing. I just extracted it from `get_relations()` which was already
 running the correct statement but not returning results in a form that
 could be used for this.
 * We need to do whatever we need to do elsewhere in the test suite to make
 sure MySQL isn't breaking when forward references are loaded (since not
 all cases uses loaddata).

 I'd really like some feedback before I plunge into any next steps to make
 sure this sounds like I'm on the right track. Please do let me know if you
 have any thoughts on this approach.

 I'm really hopeful that the basic solution has been nailed though.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/3615#comment:25>
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 post to this group, send email to django-updates@googlegroups.com.
To unsubscribe from this group, send email to 
django-updates+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to