--- D:/daten/SQLiteTable3.pas Mon Oct 13 12:38:52 2008
+++ D:/daten/Projekte/ultrastardx/linuxtrunk/src/lib/SQLite/SQLiteTable3.pas Mon Oct 13 12:56:30 2008
@@ -54,12 +54,20 @@
Adapted by Tim Anderson (tim@itwriting.com)
Originally created by Pablo Pissanetzky (pablo@myhtpc.net)
Modified and enhanced by Lukas Gebauer
+ Modified and enhanced by Tobias Gunkel
}
interface
+{$IFDEF FPC}
+ {$MODE Delphi}{$H+}
+{$ENDIF}
+
uses
- Windows, SQLite3, Classes, SysUtils;
+ {$IFDEF WIN32}
+ Windows,
+ {$ENDIF}
+ SQLite3, Classes, SysUtils;
const
@@ -102,23 +110,29 @@
FOnQuery: THookQuery;
procedure RaiseError(s: string; SQL: string);
procedure SetParams(Stmt: TSQLiteStmt);
- function getRowsChanged: integer;
+ procedure BindData(Stmt: TSQLiteStmt; const Bindings: array of const);
+ function GetRowsChanged: integer;
protected
procedure SetSynchronised(Value: boolean);
procedure DoQuery(value: string);
public
constructor Create(const FileName: string);
destructor Destroy; override;
- function GetTable(const SQL: string): TSQLiteTable;
+ function GetTable(const SQL: string): TSQLiteTable; overload;
+ function GetTable(const SQL: string; const Bindings: array of const): TSQLiteTable; overload;
procedure ExecSQL(const SQL: string); overload;
+ procedure ExecSQL(const SQL: string; const Bindings: array of const); overload;
procedure ExecSQL(Query: TSQLiteQuery); overload;
function PrepareSQL(const SQL: string): TSQLiteQuery;
procedure BindSQL(Query: TSQLiteQuery; const Index: Integer; const Value: Integer); overload;
procedure BindSQL(Query: TSQLiteQuery; const Index: Integer; const Value: String); overload;
procedure ReleaseSQL(Query: TSQLiteQuery);
- function GetUniTable(const SQL: string): TSQLiteUniTable;
- function GetTableValue(const SQL: string): int64;
- function GetTableString(const SQL: string): string;
+ function GetUniTable(const SQL: string): TSQLiteUniTable; overload;
+ function GetUniTable(const SQL: string; const Bindings: array of const): TSQLiteUniTable; overload;
+ function GetTableValue(const SQL: string): int64; overload;
+ function GetTableValue(const SQL: string; const Bindings: array of const): int64; overload;
+ function GetTableString(const SQL: string): string; overload;
+ function GetTableString(const SQL: string; const Bindings: array of const): string; overload;
procedure GetTableStrings(const SQL: string; const Value: TStrings);
procedure UpdateBlob(const SQL: string; BlobData: TStream);
procedure BeginTransaction;
@@ -128,7 +142,7 @@
function GetLastInsertRowID: int64;
function GetLastChangedRows: int64;
procedure SetTimeout(Value: integer);
- function version: string;
+ function Version: string;
procedure AddCustomCollate(name: string; xCompare: TCollateXCompare);
//adds collate named SYSTEM for correct data sorting by user's locale
Procedure AddSystemCollate;
@@ -139,7 +153,7 @@
procedure AddParamNull(name: string);
property DB: TSQLiteDB read fDB;
published
- property isTransactionOpen: boolean read fInTrans;
+ property IsTransactionOpen: boolean read fInTrans;
//database rows that were changed (or inserted or deleted) by the most recent SQL statement
property RowsChanged : integer read getRowsChanged;
property Synchronised: boolean read FSync write SetSynchronised;
@@ -163,7 +177,8 @@
function GetCount: integer;
function GetCountResult: integer;
public
- constructor Create(DB: TSQLiteDatabase; const SQL: string);
+ constructor Create(DB: TSQLiteDatabase; const SQL: string); overload;
+ constructor Create(DB: TSQLiteDatabase; const SQL: string; const Bindings: array of const); overload;
destructor Destroy; override;
function FieldAsInteger(I: cardinal): int64;
function FieldAsBlob(I: cardinal): TMemoryStream;
@@ -196,7 +211,6 @@
private
fColCount: cardinal;
fCols: TStringList;
- fColTypes: TList;
fRow: cardinal;
fEOF: boolean;
fStmt: TSQLiteStmt;
@@ -207,10 +221,12 @@
function GetFieldByName(FieldName: string): string;
function GetFieldIndex(FieldName: string): integer;
public
- constructor Create(DB: TSQLiteDatabase; const SQL: string);
+ constructor Create(DB: TSQLiteDatabase; const SQL: string); overload;
+ constructor Create(DB: TSQLiteDatabase; const SQL: string; const Bindings: array of const); overload;
destructor Destroy; override;
function FieldAsInteger(I: cardinal): int64;
function FieldAsBlob(I: cardinal): TMemoryStream;
+ function FieldAsBlobPtr(I: cardinal; out iNumBytes: integer): Pointer;
function FieldAsBlobText(I: cardinal): string;
function FieldIsNull(I: cardinal): boolean;
function FieldAsString(I: cardinal): string;
@@ -227,8 +243,10 @@
procedure DisposePointer(ptr: pointer); cdecl;
+{$IFDEF WIN32}
function SystemCollate(Userdta: pointer; Buf1Len: integer; Buf1: pointer;
Buf2Len: integer; Buf2: pointer): integer; cdecl;
+{$ENDIF}
implementation
@@ -238,12 +256,14 @@
freemem(ptr);
end;
+{$IFDEF WIN32}
function SystemCollate(Userdta: pointer; Buf1Len: integer; Buf1: pointer;
Buf2Len: integer; Buf2: pointer): integer; cdecl;
begin
Result := CompareStringW(LOCALE_USER_DEFAULT, 0, PWideChar(Buf1), Buf1Len,
PWideChar(Buf2), Buf2Len) - 2;
end;
+{$ENDIF}
//------------------------------------------------------------------------------
// TSQLiteDatabase
@@ -347,7 +367,126 @@
end;
end;
+procedure TSQLiteDatabase.BindData(Stmt: TSQLiteStmt; const Bindings: array of const);
+var
+ BlobMemStream: TCustomMemoryStream;
+ BlobStdStream: TStream;
+ DataPtr: Pointer;
+ DataSize: integer;
+ AnsiStr: AnsiString;
+ AnsiStrPtr: PAnsiString;
+ I: integer;
+begin
+ for I := 0 to High(Bindings) do
+ begin
+ case Bindings[I].VType of
+ vtString,
+ vtAnsiString, vtPChar,
+ vtWideString, vtPWideChar,
+ vtChar, vtWideChar:
+ begin
+ case Bindings[I].VType of
+ vtString: begin // ShortString
+ AnsiStr := Bindings[I].VString^;
+ DataPtr := PChar(AnsiStr);
+ DataSize := Length(AnsiStr)+1;
+ end;
+ vtPChar: begin
+ DataPtr := Bindings[I].VPChar;
+ DataSize := -1;
+ end;
+ vtAnsiString: begin
+ AnsiStrPtr := PString(@Bindings[I].VAnsiString);
+ DataPtr := PChar(AnsiStrPtr^);
+ DataSize := Length(AnsiStrPtr^)+1;
+ end;
+ vtPWideChar: begin
+ DataPtr := PChar(UTF8Encode(WideString(Bindings[I].VPWideChar)));
+ DataSize := -1;
+ end;
+ vtWideString: begin
+ DataPtr := PChar(UTF8Encode(PWideString(@Bindings[I].VWideString)^));
+ DataSize := -1;
+ end;
+ vtChar: begin
+ DataPtr := PChar(String(Bindings[I].VChar));
+ DataSize := 2;
+ end;
+ vtWideChar: begin
+ DataPtr := PChar(UTF8Encode(WideString(Bindings[I].VWideChar)));
+ DataSize := -1;
+ end;
+ else
+ raise ESqliteException.Create('Unknown string-type');
+ end;
+ if (sqlite3_bind_text(Stmt, I+1, DataPtr, DataSize, SQLITE_STATIC) <> SQLITE_OK) then
+ RaiseError('Could not bind text', 'BindData');
+ end;
+ vtInteger:
+ if (sqlite3_bind_int(Stmt, I+1, Bindings[I].VInteger) <> SQLITE_OK) then
+ RaiseError('Could not bind integer', 'BindData');
+ vtInt64:
+ if (sqlite3_bind_int64(Stmt, I+1, Bindings[I].VInt64^) <> SQLITE_OK) then
+ RaiseError('Could not bind int64', 'BindData');
+ vtExtended:
+ if (sqlite3_bind_double(Stmt, I+1, Bindings[I].VExtended^) <> SQLITE_OK) then
+ RaiseError('Could not bind extended', 'BindData');
+ vtBoolean:
+ if (sqlite3_bind_int(Stmt, I+1, Integer(Bindings[I].VBoolean)) <> SQLITE_OK) then
+ RaiseError('Could not bind boolean', 'BindData');
+ vtPointer:
+ begin
+ if (Bindings[I].VPointer = nil) then
+ begin
+ if (sqlite3_bind_null(Stmt, I+1) <> SQLITE_OK) then
+ RaiseError('Could not bind null', 'BindData');
+ end
+ else
+ raise ESqliteException.Create('Unhandled pointer (<> nil)');
+ end;
+ vtObject:
+ begin
+ if (Bindings[I].VObject is TCustomMemoryStream) then
+ begin
+ BlobMemStream := TCustomMemoryStream(Bindings[I].VObject);
+ if (sqlite3_bind_blob(Stmt, I+1, @PChar(BlobMemStream.Memory)[BlobMemStream.Position],
+ BlobMemStream.Size-BlobMemStream.Position, SQLITE_STATIC) <> SQLITE_OK) then
+ begin
+ RaiseError('Could not bind BLOB', 'BindData');
+ end;
+ end
+ else if (Bindings[I].VObject is TStream) then
+ begin
+ BlobStdStream := TStream(Bindings[I].VObject);
+ DataSize := BlobStdStream.Size;
+
+ GetMem(DataPtr, DataSize);
+ if (DataPtr = nil) then
+ raise ESqliteException.Create('Error getting memory to save blob');
+
+ BlobStdStream.Position := 0;
+ BlobStdStream.Read(DataPtr^, DataSize);
+
+ if (sqlite3_bind_blob(stmt, I+1, DataPtr, DataSize, @DisposePointer) <> SQLITE_OK) then
+ RaiseError('Could not bind BLOB', 'BindData');
+ end
+ else
+ raise ESqliteException.Create('Unhandled object-type in binding');
+ end
+ else
+ begin
+ raise ESqliteException.Create('Unhandled binding');
+ end;
+ end;
+ end;
+end;
+
procedure TSQLiteDatabase.ExecSQL(const SQL: string);
+begin
+ ExecSQL(SQL, []);
+end;
+
+procedure TSQLiteDatabase.ExecSQL(const SQL: string; const Bindings: array of const);
var
Stmt: TSQLiteStmt;
NextSQLStatement: Pchar;
@@ -361,6 +500,8 @@
RaiseError('Could not prepare SQL statement', SQL);
DoQuery(SQL);
SetParams(Stmt);
+ BindData(Stmt, Bindings);
+
iStepResult := Sqlite3_step(Stmt);
if (iStepResult <> SQLITE_DONE) then
begin
@@ -417,7 +558,7 @@
procedure TSQLiteDatabase.BindSQL(Query: TSQLiteQuery; const Index: Integer; const Value: Integer);
begin
if Assigned(Query.Statement) then
- Sqlite3_BindInt(Query.Statement, Index, Value)
+ sqlite3_Bind_Int(Query.Statement, Index, Value)
else
RaiseError('Could not bind integer to prepared SQL statement', Query.SQL);
end;
@@ -514,17 +655,32 @@
Result := TSQLiteTable.Create(Self, SQL);
end;
+function TSQLiteDatabase.GetTable(const SQL: string; const Bindings: array of const): TSQLiteTable;
+begin
+ Result := TSQLiteTable.Create(Self, SQL, Bindings);
+end;
+
function TSQLiteDatabase.GetUniTable(const SQL: string): TSQLiteUniTable;
begin
Result := TSQLiteUniTable.Create(Self, SQL);
end;
+function TSQLiteDatabase.GetUniTable(const SQL: string; const Bindings: array of const): TSQLiteUniTable;
+begin
+ Result := TSQLiteUniTable.Create(Self, SQL, Bindings);
+end;
+
function TSQLiteDatabase.GetTableValue(const SQL: string): int64;
+begin
+ Result := GetTableValue(SQL, []);
+end;
+
+function TSQLiteDatabase.GetTableValue(const SQL: string; const Bindings: array of const): int64;
var
Table: TSQLiteUniTable;
begin
Result := 0;
- Table := self.GetUniTable(SQL);
+ Table := self.GetUniTable(SQL, Bindings);
try
if not Table.EOF then
Result := Table.FieldAsInteger(0);
@@ -534,11 +690,16 @@
end;
function TSQLiteDatabase.GetTableString(const SQL: string): String;
+begin
+ Result := GetTableString(SQL, []);
+end;
+
+function TSQLiteDatabase.GetTableString(const SQL: string; const Bindings: array of const): String;
var
Table: TSQLiteUniTable;
begin
Result := '';
- Table := self.GetUniTable(SQL);
+ Table := self.GetUniTable(SQL, Bindings);
try
if not Table.EOF then
Result := Table.FieldAsString(0);
@@ -609,7 +770,7 @@
SQLite3_BusyTimeout(self.fDB, Value);
end;
-function TSQLiteDatabase.version: string;
+function TSQLiteDatabase.Version: string;
begin
Result := SQLite3_Version;
end;
@@ -622,7 +783,9 @@
procedure TSQLiteDatabase.AddSystemCollate;
begin
+ {$IFDEF WIN32}
sqlite3_create_collation(fdb, 'SYSTEM', SQLITE_UTF16LE, nil, @SystemCollate);
+ {$ENDIF}
end;
procedure TSQLiteDatabase.ParamsClear;
@@ -709,7 +872,7 @@
end;
//database rows that were changed (or inserted or deleted) by the most recent SQL statement
-function TSQLiteDatabase.getRowsChanged: integer;
+function TSQLiteDatabase.GetRowsChanged: integer;
begin
Result := SQLite3_Changes(self.fDB);
end;
@@ -725,6 +888,11 @@
//------------------------------------------------------------------------------
constructor TSQLiteTable.Create(DB: TSQLiteDatabase; const SQL: string);
+begin
+ Create(DB, SQL, []);
+end;
+
+constructor TSQLiteTable.Create(DB: TSQLiteDatabase; const SQL: string; const Bindings: array of const);
var
Stmt: TSQLiteStmt;
NextSQLStatement: Pchar;
@@ -753,6 +921,8 @@
DB.RaiseError('Could not prepare SQL statement', SQL);
DB.DoQuery(SQL);
DB.SetParams(Stmt);
+ DB.BindData(Stmt, Bindings);
+
iStepResult := Sqlite3_step(Stmt);
while (iStepResult <> SQLITE_DONE) do
begin
@@ -1122,6 +1292,7 @@
end;
end;
+{$WARNINGS OFF}
function TSQLiteTable.MoveTo(position: cardinal): boolean;
begin
Result := False;
@@ -1131,13 +1302,18 @@
Result := True;
end;
end;
-
+{$WARNINGS ON}
{ TSQLiteUniTable }
constructor TSQLiteUniTable.Create(DB: TSQLiteDatabase; const SQL: string);
+begin
+ Create(DB, SQL, []);
+end;
+
+constructor TSQLiteUniTable.Create(DB: TSQLiteDatabase; const SQL: string; const Bindings: array of const);
var
NextSQLStatement: Pchar;
thisColType: pInteger;
@@ -1156,36 +1332,14 @@
DB.RaiseError('Could not prepare SQL statement', SQL);
DB.DoQuery(SQL);
DB.SetParams(fStmt);
+ DB.BindData(fStmt, Bindings);
//get data types
fCols := TStringList.Create;
- fColTypes := TList.Create;
fColCount := SQLite3_ColumnCount(fstmt);
for i := 0 to Pred(fColCount) do
fCols.Add(AnsiUpperCase(Sqlite3_ColumnName(fstmt, i)));
- for i := 0 to Pred(fColCount) do
- begin
- new(thisColType);
- DeclaredColType := Sqlite3_ColumnDeclType(fstmt, i);
- if DeclaredColType = nil then
- thisColType^ := Sqlite3_ColumnType(fstmt, i) //use the actual column type instead
- //seems to be needed for last_insert_rowid
- else
- if (DeclaredColType = 'INTEGER') or (DeclaredColType = 'BOOLEAN') then
- thisColType^ := dtInt
- else
- if (DeclaredColType = 'NUMERIC') or
- (DeclaredColType = 'FLOAT') or
- (DeclaredColType = 'DOUBLE') or
- (DeclaredColType = 'REAL') then
- thisColType^ := dtNumeric
- else
- if DeclaredColType = 'BLOB' then
- thisColType^ := dtBlob
- else
- thisColType^ := dtStr;
- fColTypes.Add(thiscoltype);
- end;
+
Next;
end;
@@ -1197,10 +1351,6 @@
Sqlite3_Finalize(fstmt);
if Assigned(fCols) then
fCols.Free;
- if Assigned(fColTypes) then
- for i := 0 to fColTypes.Count - 1 do
- dispose(fColTypes[i]);
- fColTypes.Free;
inherited;
end;
@@ -1217,6 +1367,12 @@
Result.writebuffer(ptr^, iNumBytes);
Result.Position := 0;
end;
+end;
+
+function TSQLiteUniTable.FieldAsBlobPtr(I: cardinal; out iNumBytes: integer): Pointer;
+begin
+ iNumBytes := Sqlite3_ColumnBytes(fstmt, i);
+ Result := Sqlite3_ColumnBlob(fstmt, i);
end;
function TSQLiteUniTable.FieldAsBlobText(I: cardinal): string;