{ SQLite for Delphi and FreePascal/Lazarus © 2010 Yury Plashenkov http://www.indasoftware.com/sqlite/ SQLite is a software library that implements a self-contained, serverless, zero-configuration, transactional SQL database engine. The source code for SQLite is in the public domain and is thus free for use for any purpose, commercial or private. SQLite is the most widely deployed SQL database engine in the world. This package contains complete SQLite3 API translation for Delphi and FreePascal, as well as a simple Unicode-enabled object wrapper to simplify the use of this database engine. The contents of this file are used with permission, subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/MPL-1.1.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. } { Simple object wrapper over SQLite3 API } unit SQLite3Wrap; {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} interface uses SysUtils, Classes, SQLite3; type ESQLite3Error = class(Exception); TSQLite3Statement = class; TSQLite3BlobHandler = class; { TSQLite3Database class } TSQLite3Database = class(TObject) private FHandle: PSQLite3; FStatementList: TList; FBlobHandlerList: TList; FTransactionOpen: Boolean; procedure Check(const ErrCode: Integer); procedure CheckHandle; public constructor Create; destructor Destroy; override; procedure Open(const FileName: WideString); procedure Close; procedure Execute(const SQL: WideString); function LastInsertRowID: Int64; function Prepare(const SQL: WideString): TSQLite3Statement; function BlobOpen(const Table, Column: WideString; const RowID: Int64; const WriteAccess: Boolean = True): TSQLite3BlobHandler; procedure BeginTransaction; procedure Commit; procedure Rollback; property Handle: PSQLite3 read FHandle; property TransactionOpen: Boolean read FTransactionOpen; end; { TSQLite3Statement class } TSQLite3Statement = class(TObject) private FHandle: PSQLite3Stmt; FOwnerDatabase: TSQLite3Database; function ParamIndexByName(const ParamName: WideString): Integer; public constructor Create(OwnerDatabase: TSQLite3Database; const SQL: WideString); destructor Destroy; override; procedure BindInt(const ParamIndex: Integer; const Value: Integer); overload; procedure BindInt64(const ParamIndex: Integer; const Value: Int64); overload; procedure BindDouble(const ParamIndex: Integer; const Value: Double); overload; procedure BindText(const ParamIndex: Integer; const Value: WideString); overload; procedure BindNull(const ParamIndex: Integer); overload; procedure BindBlob(const ParamIndex: Integer; Data: Pointer; const Size: Integer); overload; procedure BindZeroBlob(const ParamIndex: Integer; const Size: Integer); overload; procedure BindInt(const ParamName: WideString; const Value: Integer); overload; procedure BindInt64(const ParamName: WideString; const Value: Int64); overload; procedure BindDouble(const ParamName: WideString; const Value: Double); overload; procedure BindText(const ParamName: WideString; const Value: WideString); overload; procedure BindNull(const ParamName: WideString); overload; procedure BindBlob(const ParamName: WideString; Data: Pointer; const Size: Integer); overload; procedure BindZeroBlob(const ParamName: WideString; const Size: Integer); overload; procedure ClearBindings; function Step: Integer; function ColumnCount: Integer; function ColumnName(const ColumnIndex: Integer): WideString; function ColumnType(const ColumnIndex: Integer): Integer; function ColumnInt(const ColumnIndex: Integer): Integer; function ColumnInt64(const ColumnIndex: Integer): Int64; function ColumnDouble(const ColumnIndex: Integer): Double; function ColumnText(const ColumnIndex: Integer): WideString; function ColumnBlob(const ColumnIndex: Integer): Pointer; function ColumnBytes(const ColumnIndex: Integer): Integer; procedure Reset; procedure StepAndReset; property Handle: PSQLite3Stmt read FHandle; property OwnerDatabase: TSQLite3Database read FOwnerDatabase; end; { TSQLite3BlobHandler class } TSQLite3BlobHandler = class(TObject) private FHandle: PSQLite3Blob; FOwnerDatabase: TSQLite3Database; public constructor Create(OwnerDatabase: TSQLite3Database; const Table, Column: WideString; const RowID: Int64; const WriteAccess: Boolean = True); destructor Destroy; override; function Bytes: Integer; procedure Read(Buffer: Pointer; const Size, Offset: Integer); procedure Write(Buffer: Pointer; const Size, Offset: Integer); property Handle: PSQLite3Blob read FHandle; property OwnerDatabase: TSQLite3Database read FOwnerDatabase; end; implementation uses SQLite3Utils; resourcestring SErrorMessage = 'SQLite3 error: %s'; SDatabaseNotConnected = 'SQLite3 error: database is not connected.'; STransactionAlreadyOpen = 'Transaction is already opened.'; SNoTransactionOpen = 'No transaction is open'; { TSQLite3Database } procedure TSQLite3Database.BeginTransaction; begin if not FTransactionOpen then begin Execute('BEGIN TRANSACTION;'); FTransactionOpen := True; end else raise ESQLite3Error.Create(STransactionAlreadyOpen); end; function TSQLite3Database.BlobOpen(const Table, Column: WideString; const RowID: Int64; const WriteAccess: Boolean): TSQLite3BlobHandler; begin Result := TSQLite3BlobHandler.Create(Self, Table, Column, RowID, WriteAccess); end; procedure TSQLite3Database.Check(const ErrCode: Integer); begin if ErrCode <> SQLITE_OK then raise ESQLite3Error.CreateFmt(SErrorMessage, [UTF8ToStr(sqlite3_errmsg(FHandle))]); end; procedure TSQLite3Database.CheckHandle; begin if FHandle = nil then raise ESQLite3Error.Create(SDatabaseNotConnected); end; procedure TSQLite3Database.Close; var I: Integer; begin if FHandle <> nil then begin if FTransactionOpen then Rollback; // Delete all statements for I := FStatementList.Count - 1 downto 0 do TSQLite3Statement(FStatementList[I]).Free; // Delete all blob handlers for I := FBlobHandlerList.Count - 1 downto 0 do TSQLite3BlobHandler(FBlobHandlerList[I]).Free; sqlite3_close(FHandle); FHandle := nil; end; end; procedure TSQLite3Database.Commit; begin if FTransactionOpen then begin Execute('COMMIT;'); FTransactionOpen := False; end else raise ESQLite3Error.Create(SNoTransactionOpen); end; constructor TSQLite3Database.Create; begin FHandle := nil; FStatementList := TList.Create; FBlobHandlerList := TList.Create; end; destructor TSQLite3Database.Destroy; begin Close; FBlobHandlerList.Free; FStatementList.Free; inherited; end; procedure TSQLite3Database.Execute(const SQL: WideString); begin CheckHandle; Check(sqlite3_exec(FHandle, PAnsiChar(StrToUTF8(SQL)), nil, nil, nil)); end; function TSQLite3Database.LastInsertRowID: Int64; begin CheckHandle; Result := sqlite3_last_insert_rowid(FHandle); end; procedure TSQLite3Database.Open(const FileName: WideString); begin Close; Check(sqlite3_open(PAnsiChar(StrToUTF8(FileName)), FHandle)); end; function TSQLite3Database.Prepare(const SQL: WideString): TSQLite3Statement; begin Result := TSQLite3Statement.Create(Self, SQL); end; procedure TSQLite3Database.Rollback; begin if FTransactionOpen then begin Execute('ROLLBACK;'); FTransactionOpen := False; end else raise ESQLite3Error.Create(SNoTransactionOpen); end; { TSQLite3Statement } procedure TSQLite3Statement.BindBlob(const ParamIndex: Integer; Data: Pointer; const Size: Integer); begin FOwnerDatabase.Check(sqlite3_bind_blob(FHandle, ParamIndex, Data, Size, SQLITE_STATIC)); end; procedure TSQLite3Statement.BindDouble(const ParamIndex: Integer; const Value: Double); begin FOwnerDatabase.Check(sqlite3_bind_double(FHandle, ParamIndex, Value)); end; procedure TSQLite3Statement.BindInt(const ParamIndex, Value: Integer); begin FOwnerDatabase.Check(sqlite3_bind_int(FHandle, ParamIndex, Value)); end; procedure TSQLite3Statement.BindInt64(const ParamIndex: Integer; const Value: Int64); begin FOwnerDatabase.Check(sqlite3_bind_int64(FHandle, ParamIndex, Value)); end; procedure TSQLite3Statement.BindNull(const ParamIndex: Integer); begin FOwnerDatabase.Check(sqlite3_bind_null(FHandle, ParamIndex)); end; procedure TSQLite3Statement.BindText(const ParamIndex: Integer; const Value: WideString); var S: AnsiString; { UTF-8 string } begin S := StrToUTF8(Value); FOwnerDatabase.Check( sqlite3_bind_text(FHandle, ParamIndex, PAnsiChar(S), Length(S), SQLITE_STATIC) ); end; procedure TSQLite3Statement.BindZeroBlob(const ParamIndex, Size: Integer); begin FOwnerDatabase.Check(sqlite3_bind_zeroblob(FHandle, ParamIndex, Size)); end; procedure TSQLite3Statement.ClearBindings; begin FOwnerDatabase.Check(sqlite3_clear_bindings(FHandle)); end; function TSQLite3Statement.ColumnBlob(const ColumnIndex: Integer): Pointer; begin Result := sqlite3_column_blob(FHandle, ColumnIndex); end; function TSQLite3Statement.ColumnBytes(const ColumnIndex: Integer): Integer; begin Result := sqlite3_column_bytes(FHandle, ColumnIndex); end; function TSQLite3Statement.ColumnCount: Integer; begin Result := sqlite3_column_count(FHandle); end; function TSQLite3Statement.ColumnDouble(const ColumnIndex: Integer): Double; begin Result := sqlite3_column_double(FHandle, ColumnIndex); end; function TSQLite3Statement.ColumnInt(const ColumnIndex: Integer): Integer; begin Result := sqlite3_column_int(FHandle, ColumnIndex); end; function TSQLite3Statement.ColumnInt64(const ColumnIndex: Integer): Int64; begin Result := sqlite3_column_int64(FHandle, ColumnIndex); end; function TSQLite3Statement.ColumnName(const ColumnIndex: Integer): WideString; begin Result := UTF8ToStr(sqlite3_column_name(FHandle, ColumnIndex)); end; function TSQLite3Statement.ColumnText(const ColumnIndex: Integer): WideString; var Len: Integer; begin Len := ColumnBytes(ColumnIndex); Result := UTF8ToStr(sqlite3_column_text(FHandle, ColumnIndex), Len); end; function TSQLite3Statement.ColumnType(const ColumnIndex: Integer): Integer; begin Result := sqlite3_column_type(FHandle, ColumnIndex); end; constructor TSQLite3Statement.Create(OwnerDatabase: TSQLite3Database; const SQL: WideString); begin FOwnerDatabase := OwnerDatabase; FOwnerDatabase.CheckHandle; FOwnerDatabase.Check( sqlite3_prepare_v2(FOwnerDatabase.Handle, PAnsiChar(StrToUTF8(SQL)), -1, FHandle, nil) ); FOwnerDatabase.FStatementList.Add(Self); end; destructor TSQLite3Statement.Destroy; begin FOwnerDatabase.FStatementList.Remove(Self); sqlite3_finalize(FHandle); inherited; end; function TSQLite3Statement.ParamIndexByName(const ParamName: WideString): Integer; begin Result := sqlite3_bind_parameter_index(FHandle, PAnsiChar(StrToUTF8(ParamName))); end; procedure TSQLite3Statement.Reset; begin sqlite3_reset(FHandle); end; function TSQLite3Statement.Step: Integer; begin Result := sqlite3_step(FHandle); end; procedure TSQLite3Statement.StepAndReset; begin Step; Reset; end; procedure TSQLite3Statement.BindBlob(const ParamName: WideString; Data: Pointer; const Size: Integer); begin BindBlob(ParamIndexByName(ParamName), Data, Size); end; procedure TSQLite3Statement.BindDouble(const ParamName: WideString; const Value: Double); begin BindDouble(ParamIndexByName(ParamName), Value); end; procedure TSQLite3Statement.BindInt(const ParamName: WideString; const Value: Integer); begin BindInt(ParamIndexByName(ParamName), Value); end; procedure TSQLite3Statement.BindInt64(const ParamName: WideString; const Value: Int64); begin BindInt64(ParamIndexByName(ParamName), Value); end; procedure TSQLite3Statement.BindNull(const ParamName: WideString); begin BindNull(ParamIndexByName(ParamName)); end; procedure TSQLite3Statement.BindText(const ParamName, Value: WideString); begin BindText(ParamIndexByName(ParamName), Value); end; procedure TSQLite3Statement.BindZeroBlob(const ParamName: WideString; const Size: Integer); begin BindZeroBlob(ParamIndexByName(ParamName), Size); end; { TSQLite3BlobHandler } function TSQLite3BlobHandler.Bytes: Integer; begin Result := sqlite3_blob_bytes(FHandle); end; constructor TSQLite3BlobHandler.Create(OwnerDatabase: TSQLite3Database; const Table, Column: WideString; const RowID: Int64; const WriteAccess: Boolean); begin FOwnerDatabase := OwnerDatabase; FOwnerDatabase.CheckHandle; FOwnerDatabase.Check( sqlite3_blob_open(FOwnerDatabase.FHandle, 'main', PAnsiChar(StrToUTF8(Table)), PAnsiChar(StrToUTF8(Column)), RowID, Ord(WriteAccess), FHandle) ); FOwnerDatabase.FBlobHandlerList.Add(Self); end; destructor TSQLite3BlobHandler.Destroy; begin FOwnerDatabase.FBlobHandlerList.Remove(Self); sqlite3_blob_close(FHandle); inherited; end; procedure TSQLite3BlobHandler.Read(Buffer: Pointer; const Size, Offset: Integer); begin FOwnerDatabase.Check(sqlite3_blob_read(FHandle, Buffer, Size, Offset)); end; procedure TSQLite3BlobHandler.Write(Buffer: Pointer; const Size, Offset: Integer); begin FOwnerDatabase.Check(sqlite3_blob_write(FHandle, Buffer, Size, Offset)); end; end.