479 lines
14 KiB
ObjectPascal
479 lines
14 KiB
ObjectPascal
{
|
|
$Id$
|
|
|
|
Complete SQLite3 API translation and simple wrapper for Delphi and FreePascal
|
|
Copyright © 2010 IndaSoftware Inc. and contributors. All rights reserved.
|
|
http://www.indasoftware.com/fordev/sqlite3/
|
|
|
|
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.
|