SqliteのDetachにおけるlockedエラー

解決


渡辺  2025-01-14 12:01:36  No: 151757

Sqlite3の処理について、教えてください。Delphiは 12 29.0.52631.8427  Win10 です。
デスク上のSqliteのDBからメモリ上にBackupし、Backupを更新した後、デスク上のSqliteのDBに書き戻す処理をしたいです。
下記において、Detachの行で、「プロジェクト PrgAAAA.exe は例外クラス ESQLiteNativeException (メッセージ '[FireDAC][Phys][SQLite] ERROR: database DisckDB is locked')を送出しました。」が発生し困っています。
省略・単純化をしてますが、流れは次のとおりです。
//*******
  // FDコネクション1のセット
  FDConnection1.Connected := False;
  FDConnection1.DriverName := 'SQLite';
  FDConnection1.Params.Database := ':memory:';

  //DBからメモリー上のSqliteにコピー
  FDSQLiteBackup1.DriverLink  := FDPhysSQLiteDriverLink1;
  FDSQLiteBackup1.Database    := SauceDataBaseFileName();  //SauceDataBaseFileName()はデスク上のデータベース名
  FDSQLiteBackup1.DestDatabaseObj := FDConnection1.CliObj;
  FDSQLiteBackup1.Backup;

  FDConnection1.Connected := True;

 //[:memory:]に対して更新処理
       //省略
 //処理完了後  [:memory:]から、DisckDBに書き戻し
 //Attach
  FDConnection1.ExecSQL('Attach ' + SauceDataBaseFileName().QuotedString(#39) + ' as DisckDB');
  //処理 AAA
  FDQuery1.SQL.Clear;
  FDQuery1.SQL.Add('Delete From DisckDB.TableAAA');
  FDConnection1.StartTransaction;
  try
    FDQuery1.ExecSQL;
    FDConnection1.Commit;
  except
    FDConnection1.Rollback;
  end;

  //処理 BBB
  FDQuery1.SQL.Clear;
  FDQuery1.SQL.Add('Insert Into DisckDB.TableAAA Select * from TableAAA');
  FDConnection1.StartTransaction;
  try
    FDQuery1.ExecSQL;
    FDConnection1.Commit;
  except
    FDConnection1.Rollback;
  end;

//Detach
  FDConnection1.ExecSQL('Detach DisckDB'); 

//*****

最後の、 Detach処理で「プロジェクト PrgAAAA.exe は例外クラス ESQLiteNativeException (メッセージ '[FireDAC][Phys][SQLite] ERROR: database DisckDB is locked')を送出しました。」が発生します。
Delete、Insert処理ともCommitされ、DB Browser for SQLitewからデスク上のDBを確認すると、 [:memory:]から、DisckDBにコビーが成功していることも確認できます。また、lockedされてはいません。
「//処理 AAA」と「//処理 BBB」省いて、Attachの直後にDetachをした場合は、エラーは発生しません。
また、試しに、 Delete  Insert (処理 AAAと 処理 BBB)に替えて、下記のようにSelect文でOpen,Closeしただけでも、Detachでエラーが発生します。
  FDQuery1.SQL.Clear;
  FDQuery1.SQL.Add('Select * from DisckDB.TableAAA');
  FDQuery1.Open;
  FDQuery1.Close;

Detachの前に必要な処理があるのでしょうか??。
よろしくお願いします。


mam  2025-01-14 17:32:02  No: 151758

あまりわかっていないのですいません。
SQLiteのトランザクション動作はデフォルトで遅延だそうです。
なので、
FDConnection1.Commit;
としても、即時実行して完了しているわけではないのでInsert文が未だ実行中の為テーブルはロックされたままなのではないかと思います。
以下の(a)又は(b)を試すのは如何でしょうか。

(a)接続を閉じると自動的にdetachされるので
FDConnection1.ExecSQL('Detach DisckDB'); の行を消す
(b)パラメータに
sqlite_use_immediate_transaction=1
を追加して即時実行させる
  FDConnection1.Params.Add('sqlite_use_immediate_transaction=1');

外していたらすいません。


渡辺  2025-01-15 10:14:27  No: 151759

mamさん ありがとうございました。
結果としては、解決しました。
ただ、原因は特定できず、すっきりしません。
Sqliteは、データベースファイル名が「123.Sqlite」の場合、ロックされると「123.Sqlite-journal」が作成されます。
追跡したところ、-journalファイルは、前述の例では FDQuery1.ExecSQL によって生成され、 FDConnection1.Commitで、消滅してます。
また、単純なselect文では当然-journalは生成されず、ロックも発生しません。それでも、detachでDB is locked エラーが発生します。
どうも、ロックされているかどうかではなく、別に原因があるような気がします。
過去のプログラムでは、FDConnection1.ExecSQLでのAttach、Detachは問題は発生してませんでしたので、ますます原因不明です。
今回解決した方法は、試行錯誤で、
「FDConnection1.ExecSQL('Detach DisckDB');」を
「FDQuery1.ExecSQL('Detach DisckDB');」に替えたところ、エラーが発生しなくなりました。
どうしてこれで解決するのか理解できてませんが、先に進めそうです。
皆さん、お騒がせしました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加