123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- unit sqliteds;
- {
- This is TSqliteDataset, a TDataset descendant class for use with fpc compiler
- Copyright (C) 2004 Luiz Américo Pereira Câmara
- Email: [email protected]
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- }
- {$mode objfpc}
- {$H+}
- { $Define DEBUG}
- interface
- uses
- Classes, SysUtils, customsqliteds;
- type
- { TSqliteDataset }
- TSqliteDataset = class (TCustomSqliteDataset)
- private
- function SqliteExec(AHandle: Pointer; ASql:PChar):Integer;override;
- function GetSqliteHandle: Pointer; override;
- function GetSqliteEncoding: String;
- function GetSqliteVersion: String; override;
- procedure SqliteClose(AHandle: Pointer);override;
- procedure BuildLinkedList; override;
- protected
- procedure InternalInitFieldDefs; override;
- public
- function SqliteReturnString: String; override;
- function TableExists: Boolean;override;
- function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;override;
- property SqliteEncoding: String read GetSqliteEncoding;
- end;
- implementation
- uses
- sqlite,db;
- var
- DummyAutoIncFieldNo:Integer;
- function GetAutoIncValue(NextValue: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): integer; cdecl;
- var
- CodeError, TempInt: Integer;
- begin
- TempInt:=-1;
- if ColumnValues[0] <> nil then
- begin
- Val(StrPas(ColumnValues[0]),TempInt,CodeError);
- if CodeError <> 0 then
- DatabaseError('SqliteDs - Error trying to get last autoinc value');
- end;
- Integer(NextValue^):=Succ(TempInt);
- Result:=1;
- end;
- function GetFieldDefs(TheDataset: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): integer; cdecl;
- var
- FieldSize:Word;
- Counter:Integer;
- AType:TFieldType;
- ColumnStr:String;
- begin
- // Sqlite is typeless (allows any type in any field)
- // regardless of what is in Create Table, but returns
- // exactly what is in Create Table statement
- // here is a trick to get the datatype.
- // If the field contains another type, there will be problems
- for Counter:= 0 to Columns - 1 do
- begin
- ColumnStr:= UpperCase(StrPas(ColumnNames[Counter + Columns]));
- if (ColumnStr = 'INTEGER') then
- begin
- AType:= ftInteger;
- FieldSize:=SizeOf(LongInt);
- end else if (ColumnStr = 'VARCHAR') then
- begin
- AType:= ftString;
- FieldSize:=10;//??
- end else if (ColumnStr = 'BOOLEAN') then
- begin
- AType:= ftBoolean;
- FieldSize:=SizeOf(Boolean);
- end else if (ColumnStr = 'FLOAT') then
- begin
- AType:= ftFloat;
- FieldSize:=SizeOf(Double);
- end else if (ColumnStr = 'WORD') then
- begin
- AType:= ftWord;
- FieldSize:=SizeOf(Word);
- end else if (ColumnStr = 'DATETIME') then
- begin
- AType:= ftDateTime;
- FieldSize:=SizeOf(TDateTime);
- end else if (ColumnStr = 'DATE') then
- begin
- AType:= ftDate;
- FieldSize:=SizeOf(TDateTime);
- end else if (ColumnStr = 'TIME') then
- begin
- AType:= ftTime;
- FieldSize:=SizeOf(TDateTime);
- end else if (ColumnStr = 'LARGEINT') then
- begin
- AType:= ftLargeInt;
- FieldSize:=SizeOf(LargeInt);
- end else if (ColumnStr = 'CURRENCY') then
- begin
- AType:= ftCurrency;
- FieldSize:=SizeOf(Double);
- end else if (ColumnStr = 'MEMO') then
- begin
- AType:= ftMemo;
- FieldSize:=10;//??
- end else if (ColumnStr = 'AUTOINC') then
- begin
- AType:= ftAutoInc;
- FieldSize:=SizeOf(Integer);
- if DummyAutoIncFieldNo = -1 then
- DummyAutoIncFieldNo:= Counter;
- end else
- begin
- DatabaseError('Field type "'+ColumnStr+'" not recognized',TDataset(TheDataset));
- end;
- TDataset(TheDataset).FieldDefs.Add(StrPas(ColumnNames[Counter]), AType, FieldSize, False);
- end;
- result:=-1;
- end;
- { TSqliteDataset }
- function TSqliteDataset.SqliteExec(AHandle: Pointer; ASql: PChar): Integer;
- begin
- Result:=sqlite_exec(AHandle, ASql, nil, nil, nil);
- end;
- procedure TSqliteDataset.SqliteClose(AHandle: Pointer);
- begin
- sqlite_close(AHandle);
- end;
- function TSqliteDataset.GetSqliteHandle: Pointer;
- begin
- Result:=sqlite_open(PChar(FFileName),0,nil);
- end;
- procedure TSqliteDataset.InternalInitFieldDefs;
- begin
- FieldDefs.Clear;
- sqlite_exec(FSqliteHandle,PChar('PRAGMA empty_result_callbacks = ON;PRAGMA show_datatypes = ON;'),nil,nil,nil);
- DummyAutoIncFieldNo:=-1;
- FSqliteReturnId:=sqlite_exec(FSqliteHandle,PChar(FSql),@GetFieldDefs,Self,nil);
- FAutoIncFieldNo:=DummyAutoIncFieldNo;
- {
- if FSqliteReturnId <> SQLITE_ABORT then
- DatabaseError(SqliteReturnString,Self);
- }
- FRowBufferSize:=(SizeOf(PPChar)*FieldDefs.Count);
- end;
- procedure TSqliteDataset.BuildLinkedList;
- var
- TempItem:PDataRecord;
- vm:Pointer;
- ColumnNames,ColumnValues:PPChar;
- Counter:Integer;
- begin
- //Get AutoInc Field initial value
- if FAutoIncFieldNo <> -1 then
- sqlite_exec(FSqliteHandle,PChar('Select Max('+Fields[FAutoIncFieldNo].FieldName+') from ' + FTableName),
- @GetAutoIncValue,@FNextAutoInc,nil);
- FSqliteReturnId:=sqlite_compile(FSqliteHandle,Pchar(FSql),nil,@vm,nil);
- if FSqliteReturnId <> SQLITE_OK then
- case FSqliteReturnId of
- SQLITE_ERROR:
- DatabaseError('Invalid SQL',Self);
- else
- DatabaseError('Error returned by sqlite while retrieving data: '+SqliteReturnString,Self);
- end;
- FDataAllocated:=True;
- TempItem:=FBeginItem;
- FRecordCount:=0;
- FSqliteReturnId:=sqlite_step(vm,@FRowCount,@ColumnValues,@ColumnNames);
- while FSqliteReturnId = SQLITE_ROW do
- begin
- Inc(FRecordCount);
- New(TempItem^.Next);
- TempItem^.Next^.Previous:=TempItem;
- TempItem:=TempItem^.Next;
- GetMem(TempItem^.Row,FRowBufferSize);
- For Counter := 0 to FRowCount - 1 do
- TempItem^.Row[Counter]:=StrNew(ColumnValues[Counter]);
- FSqliteReturnId:=sqlite_step(vm,@FRowCount,@ColumnValues,@ColumnNames);
- end;
- sqlite_finalize(vm, nil);
- // Attach EndItem
- TempItem^.Next:=FEndItem;
- FEndItem^.Previous:=TempItem;
- // Alloc item used in append/insert
- GetMem(FCacheItem^.Row,FRowBufferSize);
- for Counter := 0 to FRowCount - 1 do
- FCacheItem^.Row[Counter]:=nil;
- // Fill FBeginItem.Row with nil -> necessary for avoid exceptions in empty datasets
- GetMem(FBeginItem^.Row,FRowBufferSize);
- for Counter := 0 to FRowCount - 1 do
- FBeginItem^.Row[Counter]:=nil;
- end;
- function TSqliteDataset.TableExists: Boolean;
- var
- AHandle,vm:Pointer;
- ColumnNames,ColumnValues:PPChar;
- AInt:Integer;
- begin
- Result:=False;
- if not (FTableName = '') and FileExists(FFileName) then
- begin
- if FSqliteHandle = nil then
- begin
- {$ifdef DEBUG}
- writeln('TableExists - FSqliteHandle=nil : Opening a file');
- {$endif}
- AHandle:=GetSqliteHandle;
- end
- else
- begin
- {$ifdef DEBUG}
- writeln('TableExists - FSqliteHandle<>nil : Using FSqliteHandle');
- {$endif}
- AHandle:=FSqliteHandle;
- end;
- FSqliteReturnId:=sqlite_compile(AHandle,
- Pchar('SELECT name FROM SQLITE_MASTER WHERE type = ''table'' AND name LIKE '''+ FTableName+ ''';'),
- nil,@vm,nil);
- {$ifdef DEBUG}
- WriteLn('TableExists.sqlite_compile - SqliteReturnString:',SqliteReturnString);
- {$endif}
- FSqliteReturnId:=sqlite_step(vm,@AInt,@ColumnValues,@ColumnNames);
- {$ifdef DEBUG}
- WriteLn('TableExists.sqlite_step - SqliteReturnString:',SqliteReturnString);
- {$endif}
- Result:=FSqliteReturnId = SQLITE_ROW;
- sqlite_finalize(vm, nil);
- if FSqliteHandle = nil then
- SqliteClose(AHandle);
- end;
- {$ifdef DEBUG}
- WriteLn('TableExists ('+FTableName+') Result:',Result);
- {$endif}
- end;
- function TSqliteDataset.SqliteReturnString: String;
- begin
- case FSqliteReturnId of
- SQLITE_OK : Result := 'SQLITE_OK ';
- SQLITE_ERROR : Result := 'SQLITE_ERROR ';
- SQLITE_INTERNAL : Result := 'SQLITE_INTERNAL ';
- SQLITE_PERM : Result := 'SQLITE_PERM ';
- SQLITE_ABORT : Result := 'SQLITE_ABORT ';
- SQLITE_BUSY : Result := 'SQLITE_BUSY ';
- SQLITE_LOCKED : Result := 'SQLITE_LOCKED ';
- SQLITE_NOMEM : Result := 'SQLITE_NOMEM ';
- SQLITE_READONLY : Result := 'SQLITE_READONLY ';
- SQLITE_INTERRUPT : Result := 'SQLITE_INTERRUPT ';
- SQLITE_IOERR : Result := 'SQLITE_IOERR ';
- SQLITE_CORRUPT : Result := 'SQLITE_CORRUPT ';
- SQLITE_NOTFOUND : Result := 'SQLITE_NOTFOUND ';
- SQLITE_FULL : Result := 'SQLITE_FULL ';
- SQLITE_CANTOPEN : Result := 'SQLITE_CANTOPEN ';
- SQLITE_PROTOCOL : Result := 'SQLITE_PROTOCOL ';
- SQLITE_EMPTY : Result := 'SQLITE_EMPTY ';
- SQLITE_SCHEMA : Result := 'SQLITE_SCHEMA ';
- SQLITE_TOOBIG : Result := 'SQLITE_TOOBIG ';
- SQLITE_CONSTRAINT : Result := 'SQLITE_CONSTRAINT ';
- SQLITE_MISMATCH : Result := 'SQLITE_MISMATCH ';
- SQLITE_MISUSE : Result := 'SQLITE_MISUSE ';
- SQLITE_NOLFS : Result := 'SQLITE_NOLFS ';
- SQLITE_AUTH : Result := 'SQLITE_AUTH ';
- SQLITE_FORMAT : Result := 'SQLITE_FORMAT ';
- SQLITE_RANGE : Result := 'SQLITE_RANGE ';
- SQLITE_ROW : Result := 'SQLITE_ROW ';
- SQLITE_DONE : Result := 'SQLITE_DONE ';
- else
- Result:='Unknow Return Value';
- end;
- end;
- function TSqliteDataset.GetSqliteEncoding: String;
- begin
- Result:=StrPas(sqlite_encoding);
- end;
-
- function TSqliteDataset.GetSqliteVersion: String;
- begin
- Result:=StrPas(sqlite_version);
- end;
- function TSqliteDataset.QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;
- var
- vm,AHandle:Pointer;
- ColumnNames,ColumnValues:PPChar;
- ColCount:Integer;
-
- procedure FillStrings;
- begin
- while FSqliteReturnId = SQLITE_ROW do
- begin
- AStrList.Add(StrPas(ColumnValues[0]));
- FSqliteReturnId:=sqlite_step(vm,@ColCount,@ColumnValues,@ColumnNames);
- end;
- end;
- procedure FillStringsAndObjects;
- begin
- while FSqliteReturnId = SQLITE_ROW do
- begin
- // I know, this code is really dirty!!
- AStrList.AddObject(StrPas(ColumnValues[0]),TObject(PtrInt(StrToInt(StrPas(ColumnValues[1])))));
- FSqliteReturnId:=sqlite_step(vm,@ColCount,@ColumnValues,@ColumnNames);
- end;
- end;
- begin
- if FSqliteHandle <> nil then
- AHandle:=FSqliteHandle
- else
- if FileExists(FFileName) then
- AHandle:=GetSqliteHandle
- else
- DatabaseError('File '+FFileName+' not Exists',Self);
- Result:='';
- // It's up to the caller clear or not the list
- //if AStrList <> nil then
- // AStrList.Clear;
- FSqliteReturnId:=sqlite_compile(AHandle,Pchar(ASql),nil,@vm,nil);
- if FSqliteReturnId <> SQLITE_OK then
- DatabaseError('Error returned by sqlite in QuickQuery: '+SqliteReturnString,Self);
-
- FSqliteReturnId:=sqlite_step(vm,@ColCount,@ColumnValues,@ColumnNames);
- if (FSqliteReturnId = SQLITE_ROW) and (ColCount > 0) then
- begin
- Result:=StrPas(ColumnValues[0]);
- if AStrList <> nil then
- begin
- if FillObjects and (ColCount > 1) then
- FillStringsAndObjects
- else
- FillStrings;
- end;
- end;
- sqlite_finalize(vm, nil);
- if FSqliteHandle = nil then
- sqlite_close(AHandle);
- end;
-
- end.
|