diff --git a/.gitattributes b/.gitattributes index 8ee83b77dd..1d53f03078 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2086,6 +2086,7 @@ packages/fcl-db/src/sqlite/customsqliteds.pas svneol=native#text/plain packages/fcl-db/src/sqlite/fillds.pas svneol=native#text/plain packages/fcl-db/src/sqlite/fpmake.inc svneol=native#text/plain packages/fcl-db/src/sqlite/fpmake.pp svneol=native#text/plain +packages/fcl-db/src/sqlite/sqlite3backup.pas svneol=native#text/plain packages/fcl-db/src/sqlite/sqlite3ds.pas svneol=native#text/plain packages/fcl-db/src/sqlite/sqliteds.pas svneol=native#text/plain packages/fcl-db/src/sqlite/testds.pas svneol=native#text/plain diff --git a/packages/fcl-db/fpmake.pp b/packages/fcl-db/fpmake.pp index 712bdb187f..366368a5ae 100644 --- a/packages/fcl-db/fpmake.pp +++ b/packages/fcl-db/fpmake.pp @@ -456,6 +456,7 @@ begin AddUnit('db'); AddUnit('dbconst'); end; + T:=P.Targets.AddUnit('fpddsqldb.pp', DatadictOSes); T.ResourceStrings:=true; with T.Dependencies do @@ -716,7 +717,11 @@ begin AddUnit('customsqliteds'); AddUnit('db'); end; - + T:=P.Targets.AddUnit('sqlite3backup.pas', SqliteOSes); + with T.Dependencies do + begin + AddUnit('sqlite3conn'); + end; // SQL T:=P.Targets.AddUnit('fpsqltree.pp'); T:=P.Targets.AddUnit('fpsqlscanner.pp'); diff --git a/packages/fcl-db/src/sqlite/sqlite3backup.pas b/packages/fcl-db/src/sqlite/sqlite3backup.pas new file mode 100644 index 0000000000..d4588110c6 --- /dev/null +++ b/packages/fcl-db/src/sqlite/sqlite3backup.pas @@ -0,0 +1,161 @@ +unit sqlite3backup; + +{ SQLite3 backup class. + + Copyright (C) 2012 Ludo Brands + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version with the following modification: + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent modules,and + to copy and distribute the resulting executable under terms of your choice, + provided that you also meet, for each linked independent module, the terms + and conditions of the license of that module. An independent module is a + module which is not derived from or based on this library. If you modify + this library, you may extend this exception to your version of the library, + but you are not obligated to do so. If you do not wish to do so, delete this + exception statement from your version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License + for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +} +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils,sqlite3conn,sqlite3dyn; + +type + + TOnBackupProgress = procedure (Sender: TObject; Remaining, PageCount: integer) of object; + + { TSQLite3Backup } + + TSQLite3Backup=class + private + FErrorMessage: string; + FLockReleaseTime: integer; + FOnBackupProgress: TOnBackupProgress; + FPageStep: integer; + public + constructor Create; + //Backup one TSQLite3Connection to another TSQLite3Connection + //SourceDBName and DestinationDBName is "main" for the main database, "temp" for the temporary + //database, or the name specified after the AS keyword in an ATTACH statement for an + //attached database + //LockUntilFinished: Set to false when simultanuous access from other processes is required. + //The backup process will restart when another process writes to the database. + //Pro: the backup is a correct snapshot + //Contra: the backup can take a very long time when a lot of writes to the database are made during backup + //Warning: don't use the destination TSQLite3Connection (the handle) for anything else while doing a backup. + //It can corrupt the database and even cause a mutex deadlock. + function Backup(Source,Destination:TSQLite3Connection;LockUntilFinished:boolean=true; + SourceDBName:string='main';DestinationDBName:string='main'):boolean; + //Backup a database to file + function Backup(Source:TSQLite3Connection;FileName:string;LockUntilFinished:boolean=true; + SourceDBName:string='main'):boolean; + //Restore a database from file + function Restore(FileName:string;Destination:TSQLite3Connection;LockUntilFinished:boolean=true; + DestinationDBName:string='main'):boolean; + published + //Delay between backup steps in ms. Default:100ms. Only used when LockUntilFinished=false + property LockReleaseTime:integer read FLockReleaseTime write FLockReleaseTime; + //Page size between backup steps. Default:10 Only used when LockUntilFinished=false + property PageStep:integer read FPageStep write FPageStep; + property ErrorMessage:string read FErrorMessage; + property OnBackupProgress:TOnBackupProgress read FOnBackupProgress write FOnBackupProgress; + end; + +implementation + + + + +{ TSQLite3Backup } + +constructor TSQLite3Backup.Create; +begin + FLockReleaseTime:=100; + FPageStep:=10; +end; + +function TSQLite3Backup.Backup(Source, Destination: TSQLite3Connection; + LockUntilFinished: boolean; SourceDBName: string; DestinationDBName: string + ): boolean; +var + pBackup:psqlite3backup; + nPage:integer; + res: integer; +begin + FErrorMessage:=''; + Source.Connected:=true; + Destination.Connected:=true; + pBackup := sqlite3_backup_init(Destination.Handle, pchar(DestinationDBName), + Source.Handle, pchar(SourceDBName)); + if LockUntilFinished then + nPage:=-1 + else + nPage:=FPageStep; + result:=false; + if assigned(pBackup) then + begin +{ Each iteration of this loop copies PageStep database pages from database + pDb to the backup database. If the return value of backup_step() + indicates that there are still further pages to copy, sleep for + LockReleaseTime ms before repeating. } + repeat + res := sqlite3_backup_step(pBackup, nPage); + if assigned(FOnBackupProgress) then + FOnBackupProgress(self,sqlite3_backup_remaining(pBackup), + sqlite3_backup_pagecount(pBackup)); + if not LockUntilFinished and (res in [SQLITE_OK,SQLITE_BUSY,SQLITE_LOCKED]) then + sqlite3_sleep(LockReleaseTime); + until LockUntilFinished or not (res in [SQLITE_OK,SQLITE_BUSY,SQLITE_LOCKED]); + result:=sqlite3_backup_finish(pBackup)=SQLITE_OK; + end; + if not result then + FErrorMessage:=strpas(sqlite3_errmsg(Destination.Handle)); +end; + +function TSQLite3Backup.Backup(Source: TSQLite3Connection; FileName: string; + LockUntilFinished: boolean; SourceDBName: string): boolean; +var conn:TSQLite3Connection; +begin + conn:=TSQLite3Connection.Create(nil); + try + conn.DatabaseName:=FileName; + conn.Connected:=true; + result:=Backup(Source,conn,LockUntilFinished,SourceDBName); + finally + conn.Destroy; + end; +end; + +function TSQLite3Backup.Restore(FileName: string; + Destination: TSQLite3Connection; LockUntilFinished: boolean; + DestinationDBName: string): boolean; +var conn:TSQLite3Connection; +begin + conn:=TSQLite3Connection.Create(nil); + try + conn.DatabaseName:=FileName; + conn.Connected:=true; + result:=Backup(conn,Destination,LockUntilFinished,'main',DestinationDBName); + finally + conn.Destroy; + end; +end; + +end. +