Ordentlicher SQLite-Header

Für Themen zu Datenbanken und Zugriff auf diese. Auch für Datenbankkomponenten.
Antworten
carli
Beiträge: 657
Registriert: Sa 9. Jan 2010, 17:32
OS, Lazarus, FPC: Linux 2.6.x, SVN-Lazarus, FPC 2.4.0-2
CPU-Target: 64Bit

Ordentlicher SQLite-Header

Beitrag von carli »

Hi,

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.
 

MmVisual
Beiträge: 1581
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 4 FPC 3.2.2)
CPU-Target: 32/64Bit

Re: Ordentlicher SQLite-Header

Beitrag von MmVisual »

Praktischer weise gibt es die DB-Komponente ZEOS. Die macht schon alles ganz alleine. Dataset und DBGrid, DBEdit usw. kann man damit auch schon fertig verbinden.
Ich verstehe jetzt nicht den Sinn dieser Quellcodezeilen.
EleLa - Elektronik Lagerverwaltung - www.elela.de

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Ordentlicher SQLite-Header

Beitrag von Socke »

carli hat geschrieben:Gibt es noch andere (ordentliche) Header für SQLite?
Ja; die hatte ich sogar schonmal hier hochgeladen, finde den Thread aber nicht mehr. In der Zwischenzeit hat sich vermutlich auch wieder etwas daran geändert. Von daher gibt es jetzt hier die aktuelle Version vom 26.12.2012.
Dateianhänge
sqlite3obj.pas
SQLite3Obj.pas vom 26.12.2012
(26.92 KiB) 110-mal heruntergeladen
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Antworten