der SQLite-Header sqlite3 ist ja der reine übersetzte C-Header.
Daran hab' ich auszusetzen, dass die 3 Varargs-Methoden zum Escapen von Strings einfach ausgelassen wurden. Man kann also keine Queries escapen. (Stört mich jetzt aber nicht, wenn ich nur mit prepared Statements arbeite, was man ja grundlegend nur tun sollte)
Dann gibt es noch den sqlite3db-Header. Dort beginnt das Grauen erst: Die Query-Funktion bastelt alle Daten mit Kommas zusammen, unescaped, und ich soll das ganze dann wieder parsen? Nein danke.
Gibt es noch andere (ordentliche) Header für SQLite?
Ich schreibe momentan meinen eigenen, der die Unit fpg fleißig nutzt, um die Ergebnisse zu speichern.
Ich teile das Ergebnis einfach mal mit euch, vielleicht kann es ja einer ganz gut gebrauchen.
Code: Alles auswählen
unit sqlite_db;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, sqlite3, fgl, unixtype;
type
TResultSet = specialize TFPGMap<string, string>;
TResultTable_ = specialize TFPGList<TResultSet>;
{ TResultTable }
TResultTable = class (TResultTable_)
destructor Destroy; override; // ein Destruktor, der die Kinder mit tötet
end;
TSqlPreparedStatement = class;
{ TSqliteDatabase }
TSqliteDatabase = class
private
db: psqlite3;
public
constructor create(filename: pchar);
destructor Destroy; override;
procedure queryCallback(query: string; callback: sqlite3_callback; user: pointer);
function queryList(query: string): TResultTable; // Liste von TFPHashList
function prepare(query: string): TSqlPreparedStatement;
end;
{ TSqlPreparedStatement }
TSqlPreparedStatement = class
private
stmt: psqlite3_stmt;
constructor create(db: TSqliteDatabase; query: string);
public
destructor Destroy; override;
procedure bindNull(pos: cint);
procedure bindInt(pos: cint; val: cint);
procedure bindInt64(pos: cint; val: int64);
procedure bindDouble(pos: cint; val: double);
procedure bindString(pos: cint; val: string);
procedure bindBlob(pos: cint; val: pointer; len: integer); overload;
procedure bindBlob(pos: cint; val: string); overload;
function readRow: boolean;
function numCols: cint;
function readNull(pos: cint):boolean;
function readInt(pos: cint): cint;
function readInt64(pos: cint): int64;
function readDouble(pos: cint): double;
function readString(pos: cint): string;
function readBlob(pos: cint): pointer;
function readBlobLength(pos: cint): integer;
function readBlobString(pos: cint): string;
function execute: TResultTable;
procedure reset;
end;
TDatabaseConnectionError = class (Exception)
end;
TSqlException = class (Exception)
end;
implementation
{ TResultTable }
destructor TResultTable.Destroy;
var i: integer;
begin
for i:=0 to Count-1 do begin
// Einzelelemente entfernen
Self[i].Free;
end;
inherited Destroy;
end;
{ TSqlPreparedStatement }
constructor TSqlPreparedStatement.create(db: TSqliteDatabase; query: string);
begin
if sqlite3_prepare(db.db, pchar(query), length(query), @stmt, nil) <> SQLITE_OK then begin
raise TSqlException.Create('Could not create SQL statement');
end;
end;
destructor TSqlPreparedStatement.Destroy;
begin
sqlite3_finalize(stmt);
inherited Destroy;
end;
procedure TSqlPreparedStatement.bindNull(pos: cint);
begin
sqlite3_bind_null(stmt, pos);
end;
procedure TSqlPreparedStatement.bindInt(pos: cint; val: cint);
begin
sqlite3_bind_int(stmt, pos, val);
end;
procedure TSqlPreparedStatement.bindInt64(pos: cint; val: int64);
begin
sqlite3_bind_int64(stmt, pos, val);
end;
procedure TSqlPreparedStatement.bindDouble(pos: cint; val: double);
begin
sqlite3_bind_double(stmt, pos, val);
end;
procedure TSqlPreparedStatement.bindString(pos: cint; val: string);
begin
sqlite3_bind_text(stmt, pos, @val[1], length(val), nil);
end;
procedure TSqlPreparedStatement.bindBlob(pos: cint; val: pointer; len: integer);
begin
sqlite3_bind_blob(stmt, pos, val, len, nil);
end;
procedure TSqlPreparedStatement.bindBlob(pos: cint; val: string);
begin
bindBlob(pos, @val[1], length(val));
end;
function TSqlPreparedStatement.readRow: boolean;
begin
result:=sqlite3_step(stmt) = SQLITE_ROW;
end;
function TSqlPreparedStatement.numCols: cint;
begin
result:=sqlite3_data_count(stmt);
end;
function TSqlPreparedStatement.readNull(pos: cint): boolean;
begin
result:=sqlite3_column_type(stmt, pos) = SQLITE_NULL;
end;
function TSqlPreparedStatement.readInt(pos: cint): cint;
begin
result:=sqlite3_column_int(stmt, pos);
end;
function TSqlPreparedStatement.readInt64(pos: cint): int64;
begin
result:=sqlite3_column_int64(stmt, pos);
end;
function TSqlPreparedStatement.readDouble(pos: cint): double;
begin
result:=sqlite3_column_double(stmt, pos);
end;
function TSqlPreparedStatement.readString(pos: cint): string;
begin
result:=sqlite3_column_text(stmt, pos);
end;
function TSqlPreparedStatement.readBlob(pos: cint): pointer;
begin
result:=sqlite3_column_blob(stmt, pos);
end;
function TSqlPreparedStatement.readBlobLength(pos: cint): integer;
begin
result:=sqlite3_column_bytes(stmt, pos);
end;
function TSqlPreparedStatement.readBlobString(pos: cint): string;
var p: pointer;
begin
SetLength(result, readBlobLength(pos));
p:=readBlob(pos);
Move(p^, result[1], length(result));
end;
function TSqlPreparedStatement.execute: TResultTable;
var c, i: cint;
row: TResultSet;
begin
result:=TResultTable.Create;
while readRow do begin
row:=TResultSet.Create;
c:=numCols;
for i:=0 to c-1 do begin
// alles ins assoziative Array rein....
row[sqlite3_column_name(stmt, i)]:=readString(i);
end;
result.Add(row);
end;
end;
procedure TSqlPreparedStatement.reset;
begin
sqlite3_reset(stmt);
end;
{ TSqliteDatabase }
constructor TSqliteDatabase.create(filename: pchar);
begin
db:=nil;
if sqlite3_open(filename, @db) <> 0 then raise TDatabaseConnectionError.Create('No Database Connection possible');
end;
destructor TSqliteDatabase.Destroy;
begin
sqlite3_close(db);
inherited Destroy;
end;
procedure TSqliteDatabase.queryCallback(query: string;
callback: sqlite3_callback; user: pointer);
var err: PChar;
x: TSqlException;
begin
sqlite3_exec(db, pchar(query), callback, user, @err);
if err<>nil then begin
x:=TSqlException.Create(err);
sqlite3_free(err); // vorher Speicher freigeben
// Fehler werfen
raise x;
end;
end;
function fillTable(user: pointer; cols: cint; values, name: ppchar): cint; cdecl;
var i: integer;
s: TResultSet;
begin
s:=TResultSet.Create;
for i:=0 to cols-1 do s[name[i]]:=values[i];
TResultTable(user).Add(s); // Ergebnis in die Liste aufnehmen
end;
function TSqliteDatabase.queryList(query: string): TResultTable;
begin
result:=TResultTable.Create;
// Callback-Query aufrufen
queryCallback(query, @fillTable, result);
end;
function TSqliteDatabase.prepare(query: string): TSqlPreparedStatement;
begin
result:=TSqlPreparedStatement.create(self, query);
end;
end.