2017-09-07 7 views
1

Wir haben eine SQLite CTE UPDATE, die in Versionen bis einschließlich 3.18.0 funktionierte, aber begann in 3.19.0 mit einer FOREIGN KEY-Einschränkung (Fehler 19).SQLite rekursive CTE UPDATE arbeiten in 3.18.0, SQL_CONSTRAINT in 3.19.0

Das folgende Beispiel zeigt eine Spielzeugdatenbank, die das Verhalten zeigt.

sqlite> .version 
SQLite 3.18.0 2017-03-28 18:48:43 424a0d380332858ee55bdebc4af3789f74e70a2b3ba1cf29d84b9b4bcf3e2e37 
sqlite> .dump t2 
PRAGMA foreign_keys=OFF; 
BEGIN TRANSACTION; 
CREATE TABLE T2 (Id INTEGER PRIMARY KEY AUTOINCREMENT , ParentId INTEGER NOT NULL , Name TEXT NOT NULL , FavoriteState INT NOT NULL DEFAULT 0, FOREIGN KEY (ParentId) REFERENCES T2(Id)); 
INSERT INTO T2 VALUES(1,0,'/',2); 
CREATE INDEX idx_pid_t2 ON T2 (ParentId); 
CREATE INDEX idx_files_favstate_t2 on T2 (FavoriteState); 
CREATE UNIQUE INDEX idx_pnc_t2 ON T2 (ParentId, Name); 
COMMIT; 
sqlite> PRAGMA foreign_keys=ON; 
sqlite> WITH RECURSIVE under_favorite_path(parent) AS (VALUES(1) UNION ALL SELECT T2.Id FROM T2 JOIN under_favorite_path ON T2.ParentId = under_favorite_path.parent) UPDATE T2 SET FavoriteState = 1 
WHERE Id IN under_favorite_path; 
sqlite> select * from t2; 
1|0|/|1 
sqlite> WITH RECURSIVE under_favorite_path(parent) AS (VALUES(1) UNION ALL SELECT T2.Id FROM T2 JOIN under_favorite_path ON T2.ParentId = under_favorite_path.parent) UPDATE T2 SET FavoriteState = 2 
WHERE Id IN under_favorite_path; 
sqlite> select * from t2; 
1|0|/|2 

Das Deaktivieren von Fremdschlüsseln in 3.19.0 ermöglicht das UPDATE.

sqlite> .version 
SQLite 3.19.0 2017-05-22 13:58:13 28a94eb282822cad1d1420f2dad6bf65e4b8b9062eda4a0b9ee8270b2c608e40 
sqlite> .dump t2 
PRAGMA foreign_keys=OFF; 
BEGIN TRANSACTION; 
CREATE TABLE T2 (Id INTEGER PRIMARY KEY AUTOINCREMENT , ParentId INTEGER NOT NULL , Name TEXT NOT NULL , FavoriteState INT NOT NULL DEFAULT 0, FOREIGN KEY (ParentId) REFERENCES T2(Id)); 
INSERT INTO T2 VALUES(1,0,'/',2); 
CREATE INDEX idx_pid_t2 ON T2 (ParentId); 
CREATE INDEX idx_files_favstate_t2 on T2 (FavoriteState); 
CREATE UNIQUE INDEX idx_pnc_t2 ON T2 (ParentId, Name); 
COMMIT; 
sqlite> PRAGMA foreign_keys=ON; 
sqlite> WITH RECURSIVE under_favorite_path(parent) AS (VALUES(1) UNION ALL SELECT T2.Id FROM T2 JOIN under_favorite_path ON T2.ParentId = under_favorite_path.parent) UPDATE T2 SET FavoriteState = 1 
WHERE Id IN under_favorite_path; 
Error: FOREIGN KEY constraint failed 
sqlite> PRAGMA foreign_keys=OFF; 
sqlite> WITH RECURSIVE under_favorite_path(parent) AS (VALUES(1) UNION ALL SELECT T2.Id FROM T2 JOIN under_favorite_path ON T2.ParentId = under_favorite_path.parent) UPDATE T2 SET FavoriteState = 1 
WHERE Id IN under_favorite_path; 
sqlite> select * from t2; 
1|0|/|1 

Ist das ein Problem mit unserer CTE, die zuvor markiert haben sollen, oder ist dies etwas, das in späteren Versionen SQLite regressiert haben könnte?

Antwort

1

Das Problem ist nicht mit dem CTE, sondern, dass die Tabelle enthält ungültige Daten (0 ist keine gültige ID):

 
sqlite> pragma foreign_key_check; 
table  rowid  parent  fkid 
---------- ---------- ---------- ---------- 
T2   1   T2   0 

Wenn die Zeile in keine Weise verändert wird, wird die Randbedingungsüberprüft

Anscheinend hat die alte Version die Einschränkung nicht überprüft, wenn die FK-Spalte nicht geändert wurde.

+0

Ja, sieht definitiv wie das Verhalten jetzt aus. Ich frage mich, ob diese Änderung beabsichtigt war oder nicht, basierend auf dieser Zeile aus den Versionshinweisen zu 3.19.0 https://www.sqlite.org/releaselog/3_19_0.html, weil ich gelesen habe, dass das Gegenteil von dem ist, was passiert: _Vermeide unnötigen Fremdschlüssel Verarbeitung in UPDATE-Anweisungen, die nicht die Spalten berühren, die durch die Fremdschlüssel eingeschränkt sind._ – ribram

+0

Diese "unnötige Verarbeitung von Fremdschlüsseln" [aktualisiert "nicht betroffene Indizes"] (http://www.sqlite.org/cgi/src/info/477bea9ed0dd0fa6). Es scheint eine andere Veränderung gegeben zu haben ... –