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

481 lines
14 KiB
ObjectPascal
Raw Normal View History

2013-10-09 15:18:56 +07:00
{*
* SQLite for Delphi and FreePascal/Lazarus
*
* This unit contains easy-to-use object wrapper over SQLite3 API functions
*
* Copyright 2010-2013 Yury Plashenkov
2013-10-10 02:03:29 +07:00
* http://plashenkov.github.io/sqlite/
2013-10-09 15:18:56 +07:00
*
* The MIT License (MIT)
*
2013-10-09 15:29:36 +07:00
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
2013-10-09 15:18:56 +07:00
*
2013-10-09 15:29:36 +07:00
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
2013-10-09 15:18:56 +07:00
*
2013-10-09 15:29:36 +07:00
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
2013-10-09 15:18:56 +07:00
*}
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; Flags: Integer = 0);
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;
procedure Reset;
function StepAndReset: 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;
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; Flags: Integer);
begin
Close;
if Flags = 0 then
Check(sqlite3_open(PAnsiChar(StrToUTF8(FileName)), FHandle))
else
Check(sqlite3_open_v2(PAnsiChar(StrToUTF8(FileName)), FHandle, Flags, nil));
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_TRANSIENT));
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_TRANSIENT)
);
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;
function TSQLite3Statement.StepAndReset: Integer;
begin
Result := 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.