Files
SQLite3-Delphi-FPC/Source/SQLite3Wrap.pas

478 lines
13 KiB
ObjectPascal
Raw Normal View History

2012-10-02 14:48:13 +07:00
{
2012-10-02 16:06:15 +07:00
SQLite for Delphi and FreePascal/Lazarus
2012-10-02 14:48:13 +07:00
2012-10-02 16:06:15 +07:00
<EFBFBD> 2010 Yury Plashenkov <plashenkov@gmail.com>
http://www.indasoftware.com/sqlite/
2012-10-02 14:48:13 +07:00
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.