Browse Source

+ Original test examples and class included

michael 22 years ago
parent
commit
c7b339b3ce
2 changed files with 467 additions and 0 deletions
  1. 414 0
      packages/base/sqlite/sqlitedb.pas
  2. 53 0
      packages/base/sqlite/test.pas

+ 414 - 0
packages/base/sqlite/sqlitedb.pas

@@ -0,0 +1,414 @@
+{$mode objfpc}
+{$h+}
+
+unit SQLitedb;
+
+interface
+
+uses  Classes,strings,sqlite;
+
+type
+  TSQLiteExecCallback = function(Sender: TObject; Columns: Integer; ColumnValues: Pointer; ColumnNames: Pointer): integer of object; cdecl;
+  TSQLiteBusyCallback = function(Sender: TObject; ObjectName: PChar; BusyCount: integer): integer of object; cdecl;
+  TOnData = Procedure(Sender: TObject; Columns: Integer; ColumnNames, ColumnValues: String)  of object;
+  TOnBusy = Procedure(Sender: TObject; ObjectName: String; BusyCount: integer; var Cancel: Boolean) of object;
+  TOnQueryComplete = Procedure(Sender: TObject) of object;
+
+  TSQLite = class(TObject)
+  private
+    fSQLite: Pointer;
+    fMsg: String;
+    fIsOpen: Boolean;
+    fBusy: Boolean;
+    fError: Integer;
+    fVersion: String;
+    fEncoding: String;
+    fTable: TStrings;
+    fLstName: TStringList;
+    fLstVal: TStringList;
+    fOnData: TOnData;
+    fOnBusy: TOnBusy;
+    fOnQueryComplete: TOnQueryComplete;
+    fBusyTimeout: integer;
+    fPMsg: PChar;
+    fChangeCount: integer;
+ 	fNb_Champ :  Integer; 
+  	fList_FieldName : TStringList;
+  	fList_Field : TList;
+    procedure SetBusyTimeout(Timeout: integer);
+  public
+    constructor Create(DBFileName: String);
+    destructor Destroy; override;
+    function Query(Sql: String; Table: TStrings ): boolean;
+    function ErrorMessage(ErrNo: Integer): string;
+    function IsComplete(Sql: String): boolean;
+    function LastInsertRow: integer;
+    function Cancel: boolean;
+    function DatabaseDetails(Table: TStrings): boolean;
+    property LastErrorMessage: string read fMsg;
+    property LastError: Integer read fError;
+    property Version: String read fVersion;
+    property Encoding: String read fEncoding;
+    property OnData: TOnData read fOnData write fOnData;
+    property OnBusy: TOnBusy read fOnBusy write fOnBusy;
+    property OnQueryComplete: TOnQueryComplete read fOnQueryComplete write fOnQueryComplete;
+    property BusyTimeout: Integer read fBusyTimeout write SetBusyTimeout;
+    property ChangeCount: Integer read fChangeCount;
+    property List_FieldName: TStringList read fList_FieldName write fList_FieldName;
+    property List_Field: TList read fList_Field write fList_Field;
+    property Nb_Champ: integer read fNb_Champ write fNb_Champ;
+    
+  procedure SQLOnData(Sender: TObject; Columns: Integer; ColumnNames, ColumnValues: String);
+    
+  end;
+  function Pas2SQLStr(const PasString: string): string;
+  function SQL2PasStr(const SQLString: string): string;
+  function QuoteStr(const s: string; QuoteChar: Char ): string;
+  function UnQuoteStr(const s: string; QuoteChar: Char ): string;
+  procedure ValueList(const ColumnNames, ColumnValues: String; NameValuePairs: TStrings);
+
+implementation
+
+Const
+  DblQuote: Char    = '"';
+  SngQuote: Char    = #39;
+  Crlf: String      = #13#10;
+  Tab: Char         = #9;
+
+var
+  MsgNoError: String;
+
+function QuoteStr(const s: string; QuoteChar: Char ): string;
+begin
+  Result := Concat(QuoteChar, s, QuoteChar);
+end;
+
+function UnQuoteStr(const s: string; QuoteChar: Char ): string;
+begin
+  Result := s;
+  if length(Result) > 1 then
+  begin
+    if Result[1] = QuoteChar then
+      Delete(Result, 1, 1);
+    if Result[Length(Result)] = QuoteChar then
+      Delete(Result, Length(Result), 1);
+  end;
+end;
+
+function Pas2SQLStr(const PasString: string): string;
+var
+  n: integer;
+begin
+  Result := SQL2PasStr(PasString);
+  n := Length(Result);
+  while n > 0 do
+  begin
+    if Result[n] = SngQuote then
+      Insert(SngQuote, Result, n);
+    dec(n);
+  end;
+  Result := QuoteStr(Result,#39);
+end;
+
+function SQL2PasStr(const SQLString: string): string;
+const
+  DblSngQuote: String = #39#39;
+var
+  p: integer;
+begin
+  Result := SQLString;
+  p := pos(DblSngQuote, Result);
+  while p > 0 do
+  begin
+    Delete(Result, p, 1);
+    p := pos(DblSngQuote, Result);
+  end;
+  Result := UnQuoteStr(Result,#39);
+end;
+
+procedure ValueList(const ColumnNames, ColumnValues: String; NameValuePairs: TStrings);
+var
+  n: integer;
+  lstName, lstValue: TStringList;
+begin
+  if NameValuePairs <> nil then
+  begin
+    lstName := TStringList.Create;
+    lstValue := TStringList.Create;
+    lstName.CommaText := ColumnNames;
+    lstValue.CommaText := ColumnValues;
+    NameValuePairs.Clear;
+    if lstName.Count = LstValue.Count then
+      if lstName.Count > 0 then
+        for n := 0 to lstName.Count - 1 do
+          NameValuePairs.Append(Concat(lstName.Strings[n], '=', lstValue.Strings[n]));
+    lstValue.Free;
+    lstName.Free;
+  end;
+end;
+
+
+
+function SystemErrorMsg(ErrNo: Integer ): String;
+var
+  buf: PChar;
+  size: Integer;
+  MsgLen: Integer;
+begin
+{  size := 256;
+  GetMem(buf, size);
+  If ErrNo = - 1 then
+    ErrNo := GetLastError;
+  MsgLen := FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil, ErrNo, 0, buf, size, nil);
+  if MsgLen = 0 then
+    Result := 'ERROR'
+  else
+    Result := buf;}
+end;
+
+function BusyCallback(Sender: pointer; ObjectName: PChar; BusyCount: integer): integer; cdecl;
+var
+  sObjName: String;
+  bCancel: Boolean;
+begin
+  Result := -1;
+  with TObject(Sender) as TSQLite do
+  begin
+    if Assigned(fOnBusy) then
+    begin
+      bCancel := False;
+      sObjName := ObjectName;
+      fOnBusy(Tobject(Sender), sObjName, BusyCount, bCancel);
+      if bCancel then
+        Result := 0;
+    end;
+  end;
+end;
+
+function ExecCallback(Sender: TObject; Columns: Integer; ColumnValues: Pointer; ColumnNames: Pointer): integer; cdecl;
+var
+  PVal, PName: ^PChar;
+  n: integer;
+  sVal, sName: String;
+begin
+  Result := 0;
+  with Sender as TSQLite do
+  begin
+    if (Assigned(fOnData) or Assigned(fTable)) then
+    begin
+      fLstName.Clear;
+      fLstVal.Clear;
+      if Columns > 0 then
+      begin
+        PName := ColumnNames;
+        PVal := ColumnValues;
+        for n := 0 to Columns - 1 do
+        begin
+          fLstName.Append(PName^);
+          fLstVal.Append(PVal^);
+          inc(PName);
+          inc(PVal);
+        end;
+      end;
+      sVal := fLstVal.CommaText;
+      sName := fLstName.CommaText;
+      if Assigned(fOnData) then
+        fOnData(Sender, Columns, sName, sVal);
+      if Assigned(fTable) then
+      begin
+        if fTable.Count = 0 then
+          fTable.Append(sName);
+        fTable.Append(sVal);
+      end;
+    end;
+  end;
+end;
+
+
+procedure TSQLite.SQLOnData(Sender: TObject; Columns: Integer; ColumnNames, ColumnValues: String);
+Var i : Integer;
+	  InterS,val : String;
+	  Field : TStringList;	
+		  
+	  function Pos1(a: String ; s : char) : integer;
+	  var i,j : Integer;
+	  
+	  begin
+	  j:=-1;
+	  	for i:=1 to length(a) Do
+	  	begin
+	  		if a[i] = s then
+	  		begin
+	  			j:=i;
+	  			break;
+	  		end;
+	  	end;
+	  	result:=j;
+	  end;
+begin
+	If Nb_Champ = -1 Then
+  	Begin // Put the fields name in List_FieldName
+  		Nb_Champ:=Columns;
+		InterS:=ColumnNames;
+		While (Pos1(InterS,',') > 0)  do
+		begin
+			val:=copy(InterS,1,Pos1(InterS,',')-1);
+			InterS:=copy(InterS,Pos1(InterS,',')+1,length(InterS)); 
+			List_FieldName.add(val);
+		end;
+		if length(InterS) > 0 then List_FieldName.add(InterS);
+	end;
+	// Put the list of TStringList of value
+	Field :=TStringList.Create;
+	InterS:=ColumnValues;
+	While (Pos1(InterS,',') > 0)  do
+	begin
+		val:=copy(InterS,1,Pos1(InterS,',')-1);
+		InterS:=copy(InterS,Pos1(InterS,',')+1,length(InterS)); 
+		Field.add(val);
+	end;
+	if length(InterS) > 0 then Field.add(InterS);
+	List_Field.add(Field);
+end;
+
+constructor TSQLite.Create(DBFileName: String);
+var
+  fPMsg1: PChar;
+  name : pchar;
+begin
+  inherited Create;
+  List_FieldName := TStringList.Create;
+  List_Field := TList.Create;
+  fError := SQLITE_ERROR;
+  fIsOpen := False;
+  fLstName := TStringList.Create;
+  fLstVal := TStringList.Create;
+  fOnData := nil;
+  fOnBusy := nil;
+  fOnQueryComplete := nil;
+  fChangeCount := 0;
+   name:=StrAlloc (length(DBFileName)+1);  
+   strpcopy(name,DBFileName);
+   OnData:=@SQLOnData;
+    fSQLite := SQLite_Open(name, 1, @fPMsg);
+    SQLite_FreeMem(fPMsg);
+    if fSQLite <> nil then
+    begin
+      //fVersion := String(SQLite_Version);
+      //fEncoding := SQLite_Encoding;
+      fIsOpen := True;
+      fError := SQLITE_OK;
+    end;
+  fMsg := ErrorMessage(fError);
+end;
+
+destructor TSQLite.Destroy;
+begin
+  if fIsOpen then
+    SQLite_Close(fSQLite);
+  fIsOpen := False;
+  fLstName.Free;
+  fLstVal.Free;
+  fSQLite := nil;
+  fOnData := nil;
+  fOnBusy := nil;
+  fOnQueryComplete := nil;
+  fLstName := nil;
+  fLstVal := nil;
+  List_FieldName.destroy; 
+  List_Field.destroy; 
+  inherited Destroy;
+end;
+
+function TSQLite.Query(Sql: String; Table: TStrings ): boolean;
+//var
+//  fPMsg: PChar;
+var Psql : pchar;
+begin
+  fError := SQLITE_ERROR;
+  if fIsOpen then
+  begin
+    fPMsg := nil;
+    fBusy := True;
+    fTable := Table;
+    if fTable <> nil then
+      fTable.Clear;
+   Psql:=StrAlloc (length(Sql)+1);  
+   strpcopy(Psql,Sql);
+   List_FieldName.clear;
+   List_Field.clear;
+   Nb_Champ:=-1;
+    fError := SQLite_Exec(fSQLite, Psql, @ExecCallback, Self, @fPMsg);
+    strdispose(Psql);
+    SQLite_FreeMem(fPMsg);
+    fChangeCount := SQLite_Changes(fSQLite);
+    fTable := nil;
+    fBusy := False;
+    if Assigned(fOnQueryComplete) then
+      fOnQueryComplete(Self);
+  end;
+  fMsg := ErrorMessage(fError);
+  Result := (fError <> SQLITE_OK);
+end;
+
+function TSQLite.Cancel: boolean;
+begin
+  Result := False;
+  if fBusy and fIsOpen then
+  begin
+    do_SQLite_interrupt(fSQLite);
+    fBusy := false;
+    Result := True;
+  end;
+end;
+
+procedure TSQLite.SetBusyTimeout(Timeout: Integer);
+begin
+  fBusyTimeout := Timeout;
+  if fIsOpen then
+  begin
+    SQLite_Busy_Timeout(fSQLite, fBusyTimeout);
+    if fBusyTimeout > 0 then
+      SQLite_Busy_Handler(fSQLite, @BusyCallback, Self)
+    else
+      SQLite_Busy_Handler(fSQLite, nil, nil);
+  end;
+end;
+
+function TSQLite.LastInsertRow: integer;
+begin
+  if fIsOpen then
+    Result := SQLite_Last_Insert_RowID(fSQLite)
+  else
+    Result := -1;
+end;
+
+function TSQLite.ErrorMessage(ErrNo: Integer): string;
+begin
+  exit;
+  if ErrNo = 0 then
+    Result := MsgNoError
+  else
+    Result := SQLite_Error_String(ErrNo);
+end;
+
+function TSQLite.IsComplete(Sql: String): boolean;
+var Psql : pchar;
+begin
+  Psql:=StrAlloc (length(Sql)+1);  
+  strpcopy(Psql,Sql);
+  Writeln('Testing: ',psql);
+  Result := SQLite_Complete(Psql)<>0;
+  strdispose(Psql);
+end;
+
+function TSQLite.DatabaseDetails(Table: TStrings): boolean;
+begin
+  Result := Query('SELECT * FROM SQLITE_MASTER;', Table);
+end;
+
+initialization
+
+finalization
+
+end.

+ 53 - 0
packages/base/sqlite/test.pas

@@ -0,0 +1,53 @@
+program test;
+uses sqlite,sqlitedb, strings,classes;
+
+
+
+var
+  MySQL: TSQLite;
+  SQL: String;
+  i,j : Integer;
+  a : TStringList;
+begin
+  Writeln('Creating class');
+  MySQL := TSQLite.Create('test.db');
+  MySQL.BusyTimeout := 1000;
+  
+ // writeln(MySQL.Version);
+  Writeln('Creating table');
+  SQL := 'CREATE TABLE Test(No int, Nom varchar(32),Prenom varchar(32));';
+  MySQL.Query(sql, nil);
+  SQL := 'INSERT INTO Test VALUES(1,''Coursiere'', ''Olivier'');';
+  if MySQL.IsComplete(sql) then
+    begin
+    Writeln('Inserting first row');
+    MySQL.Query(sql, nil);
+    end;
+  SQL := 'INSERT INTO Test VALUES(2,''Jourde'', ''Eric'');';
+  if MySQL.IsComplete(sql) then
+    begin
+    Writeln('Inserting second row') ;
+    MySQL.Query(sql, nil);
+    end;
+  Writeln('Selecting rows') ;
+
+  SQL := 'SELECT * FROM Test;';
+  MySQL.Query(sql, nil);
+  writeln('Fields Names -------------------');
+  for i:=0 to MySQL.List_FieldName.count-1 do 
+    writeln(i,' -> ',MySQL.List_FieldName.Strings[i]);
+  writeln('Fields -------------------');
+  for i:=0 to MySQL.List_Field.count-1 do 
+      begin
+ 	a:=TStringList(MySQL.List_Field.items[i]); 
+ 	write(i,' -> ');
+        for j:=0 to a.count-1 do  
+          write(a.Strings[j],'  ');
+        writeln('');
+      end;   
+
+// Uncomment to remove table again.
+//  SQL := 'DROP TABLE Test;';
+//  MySQL.Query(sql, nil);
+  MySQL.Free;
+end.