A atualização do MySQL que altera várias colunas não é atômica?
Eu estou tendo o seguinte problema usando o Django com o MySQL 5.5.22.
Dada uma tabela com colunas id, level e uma matriz 2x2 armazenada como a11, a12, a21, a22, tenho esta linha:
id a11 a12 a21 a22 level
324 3 2 5 3 2
Dado um qs queryset, eu faço a seguinte atualização:
qs.update(
a11=(b12 * a21 - b11 * a22) * F('a11') + (b11 * a12 - b12 * a11) * F('a21'),
a12=(b12 * a21 - b11 * a22) * F('a12') + (b11 * a12 - b12 * a11) * F('a22'),
a21=(b22 * a21 - b21 * a22) * F('a11') + (b21 * a12 - b22 * a11) * F('a21'),
a22=(b22 * a21 - b21 * a22) * F('a12') + (b21 * a12 - b22 * a11) * F('a22'),
level=(F('level') - 1)
)
Para o qual django gera a seguinte consulta (obtida de db.connection.queries, remova a cláusula where para brevidade):
UPDATE `storage`
SET
`a21` = (3 * `storage`.`a11`) + (-1 * `storage`.`a21`),
`a22` = (3 * `storage`.`a12`) + (-1 * `storage`.`a22`),
`level` = `storage`.`level` - -1,
`a11` = (2 * `storage`.`a11`) + (-1 * `storage`.`a21`),
`a12` = (2 * `storage`.`a12`) + (-1 * `storage`.`a22`)
E minha linha fica assim depois disso:
id a11 a12 a21 a22 level
324 2 1 4 3 1
Para qualquer fila,a12*a21 - a11*a22 = 1
é suposto ser verdadeiro, e de acordo com isso, a linha deveria ser:
id a11 a12 a21 a22 level
324 1 1 4 3 1
Isso é o que eu obtenho no SQLite, com o Django gerando a mesma consulta, e levei muito tempo para descobrir que o MySQL estava fazendo algo diferente. A partir da consulta, parece que ao atualizar várias linhas interdependentes, o MySQL não a trata como uma única operação atômica e, como as colunas são atualizadas, elas afetam os valores dependentes delas. Confirmei que isso parece ser o que acontece com o seguinte código no prompt do Python:
>>> a11, a12, a21, a22 = (3, 2, 5, 3)
>>> (2 * a11) + (-1 * a21),\
... (2 * a12) + (-1 * a22),\
... (3 * a11) + (-1 * a21),\
... (3 * a12) + (-1 * a22)
(1, 1, 4, 3)
Se as colunas forem atualizadas uma por vez, na mesma ordem dada pela consulta:
>>> a11, a12, a21, a22 = (3, 2, 5, 3)
>>> a21 = (3*a11) + (-1*a21)
>>> a22 = (3*a12) + (-1*a22)
>>> a11 = (2*a11) + (-1*a21)
>>> a12 = (2*a12) + (-1*a22)
>>> (a11, a12, a21, a22)
(2, 1, 4, 3)
Esse é um comportamento realmente assustador, já que essa é uma biblioteca que deve ser usada em várias plataformas. Minhas perguntas são:
Qual deles está fazendo errado, MySQL ou SQLite? Isso pode ser considerado um bug?O que posso esperar de outros grandes bancos de dados (Oracle, PostgreSQL e SQLServer)?O que eu posso fazer com o Django ORM (sem consultas cruas) para normalizar esse comportamento?editar
O problema é claro, mas ainda estou procurando uma solução. Puxar todos os valores e empurrá-los de volta não é uma solução aceitável para esse aplicativo específico.