浏览代码

+ Added sqlite3 support

git-svn-id: trunk@578 -
michael 20 年之前
父节点
当前提交
3b99356a0c
共有 6 个文件被更改,包括 1537 次插入1069 次删除
  1. 2 0
      .gitattributes
  2. 34 34
      fcl/db/sqlite/Makefile
  3. 1 1
      fcl/db/sqlite/Makefile.fpc
  4. 1139 0
      fcl/db/sqlite/customsqliteds.pas
  5. 290 0
      fcl/db/sqlite/sqlite3ds.pas
  6. 71 1034
      fcl/db/sqlite/sqliteds.pas

+ 2 - 0
.gitattributes

@@ -662,9 +662,11 @@ fcl/db/sqlite/Makefile.fpc svneol=native#text/plain
 fcl/db/sqlite/browseds.pas svneol=native#text/plain
 fcl/db/sqlite/concurrencyds.pas svneol=native#text/plain
 fcl/db/sqlite/createds.pas svneol=native#text/plain
+fcl/db/sqlite/customsqliteds.pas svneol=native#text/plain
 fcl/db/sqlite/fillds.pas svneol=native#text/plain
 fcl/db/sqlite/fpmake.inc svneol=native#text/plain
 fcl/db/sqlite/fpmake.pp svneol=native#text/plain
+fcl/db/sqlite/sqlite3ds.pas svneol=native#text/plain
 fcl/db/sqlite/sqlitedataset.pas svneol=native#text/plain
 fcl/db/sqlite/sqliteds.pas svneol=native#text/plain
 fcl/db/sqlite/testds.pas svneol=native#text/plain

+ 34 - 34
fcl/db/sqlite/Makefile

@@ -1,5 +1,5 @@
 #
-# Don't edit, this file is generated by FPCMake Version 2.0.0 [2005/05/05]
+# Don't edit, this file is generated by FPCMake Version 1.9.8 [2005/04/10]
 #
 default: all
 MAKEFILETARGETS=i386-linux i386-go32v2 i386-win32 i386-os2 i386-freebsd i386-beos i386-netbsd i386-solaris i386-qnx i386-netware i386-openbsd i386-wdosx i386-emx i386-watcom i386-netwlibc m68k-linux m68k-freebsd m68k-netbsd m68k-amiga m68k-atari m68k-openbsd m68k-palmos powerpc-linux powerpc-netbsd powerpc-macos powerpc-darwin powerpc-morphos sparc-linux sparc-netbsd sparc-solaris x86_64-linux x86_64-freebsd arm-linux
@@ -233,103 +233,103 @@ PACKAGESDIR:=$(wildcard $(FPCDIR) $(FPCDIR)/packages/base $(FPCDIR)/packages/ext
 override PACKAGE_NAME=fcl
 PACKAGEDIR_MAIN:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fcl/Makefile.fpc,$(PACKAGESDIR))))))
 ifeq ($(FULL_TARGET),i386-linux)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),i386-go32v2)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),i386-win32)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),i386-os2)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),i386-freebsd)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),i386-beos)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),i386-netbsd)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),i386-solaris)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),i386-qnx)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),i386-netware)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),i386-openbsd)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),i386-wdosx)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),i386-emx)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),i386-watcom)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),i386-netwlibc)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),m68k-linux)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),m68k-freebsd)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),m68k-netbsd)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),m68k-amiga)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),m68k-atari)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),m68k-openbsd)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),m68k-palmos)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),powerpc-linux)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),powerpc-netbsd)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),powerpc-macos)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),powerpc-darwin)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),powerpc-morphos)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),sparc-linux)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),sparc-netbsd)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),sparc-solaris)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),x86_64-linux)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),x86_64-freebsd)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 ifeq ($(FULL_TARGET),arm-linux)
-override TARGET_UNITS+=sqliteds
+override TARGET_UNITS+=customsqliteds sqliteds sqlite3ds
 endif
 override INSTALL_FPCPACKAGE=y
 ifdef REQUIRE_UNITSDIR

+ 1 - 1
fcl/db/sqlite/Makefile.fpc

@@ -6,7 +6,7 @@
 main=fcl
 
 [target]
-units=sqliteds
+units=customsqliteds sqliteds sqlite3ds
 
 [require]
 packages=sqlite

+ 1139 - 0
fcl/db/sqlite/customsqliteds.pas

@@ -0,0 +1,1139 @@
+unit customsqliteds;
+
+{
+    This is TCustomSqliteDataset, 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 USE_SQLITEDS_INTERNALS}
+{ $Define DEBUG}
+{ $Define DEBUGACTIVEBUFFER}
+
+interface
+
+uses
+  Classes, SysUtils, Db;
+
+type
+  PDataRecord = ^DataRecord;
+  PPDataRecord = ^PDataRecord;
+  DataRecord = record
+    Row: PPchar;
+    BookmarkData: Pointer;
+    BookmarkFlag: TBookmarkFlag;
+    Next: PDataRecord;
+    Previous: PDataRecord;
+  end;
+  
+  TDSStream = class(TStream)
+  private
+    FActiveItem:PDataRecord;
+    FFieldRow:PChar;  
+    FFieldIndex:Integer;
+    FRowSize: Integer;
+    FPosition: Longint;
+  public
+    constructor Create(const ActiveItem: PDataRecord; FieldIndex:Integer);
+    function Write(const Buffer; Count: Longint): Longint; override;
+    function Read(var Buffer; Count: Longint): Longint; override;
+    function Seek(Offset: Longint; Origin: Word): Longint; override;
+  end;
+  
+  TSqliteCallback = function (UserData:Pointer; Columns:longint; Values:PPchar; ColumnNames:PPchar):longint;cdecl;
+  
+  { TCustomSqliteDataset }
+
+  TCustomSqliteDataset = class(TDataSet)
+  private
+    FPrimaryKey: String;
+    FPrimaryKeyNo: Integer;
+
+    {$ifdef DEBUGACTIVEBUFFER}
+    FFCurrentItem: PDataRecord;
+    {$else}
+    FCurrentItem: PDataRecord;
+    {$endif}
+    FBufferSize: Integer;
+    FExpectedAppends: Integer;
+    FExpectedDeletes: Integer;
+    FExpectedUpdates: Integer;
+    FSaveOnClose: Boolean;
+    FSaveOnRefetch: Boolean;
+    FComplexSql: Boolean;
+    FUpdatedItems: TList;
+    FAddedItems: TList;
+    FDeletedItems: TList;
+    FOrphanItems: TList;
+    FMasterLink: TMasterDataLink;
+    FIndexFieldNames: String;
+    FIndexFieldList: TList;
+    function GetIndexFields(Value: Integer): TField;
+    procedure UpdateIndexFields;
+  protected
+    FFileName: String;
+    FSql: String;
+    FTableName: String;
+    FAutoIncFieldNo: Integer;
+    FNextAutoInc:Integer;
+    FSqliteReturnId: Integer;
+    FSqliteHandle: Pointer;
+    FDataAllocated: Boolean;
+    FRowBufferSize: Integer;
+    FRowCount: Integer;
+    FRecordCount: Integer;
+    FBeginItem: PDataRecord;
+    FEndItem: PDataRecord;
+    FCacheItem: PDataRecord;
+    function SqliteExec(AHandle: Pointer; Sql:PChar):Integer;virtual; abstract;
+    procedure SqliteClose(AHandle: Pointer);virtual;abstract;
+    function GetSqliteHandle: Pointer; virtual; abstract;
+    procedure BuildLinkedList; virtual; abstract;
+    function SqliteReturnString: String; virtual; abstract;
+    function TableExists: Boolean;virtual;abstract;
+    procedure DisposeLinkedList;
+    procedure MasterChanged(Sender: TObject);
+    procedure MasterDisabled(Sender: TObject);
+    procedure SetMasterFields(Value:String);
+    function GetMasterFields:String;
+    procedure SetMasterSource(Value: TDataSource);
+    function GetMasterSource:TDataSource;
+    //TDataSet overrides
+    function AllocRecordBuffer: PChar; override;
+    function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
+    procedure FreeRecordBuffer(var Buffer: PChar); override;
+    procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
+    function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
+    function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
+    function GetRecordCount: Integer; override;
+    function GetRecNo: Integer; override;
+    function GetRecordSize: Word; override; 
+    procedure InternalAddRecord(Buffer: Pointer; DoAppend: Boolean); override;
+    procedure InternalClose; override;
+    procedure InternalDelete; override;
+    procedure InternalFirst; override;
+    procedure InternalGotoBookmark(ABookmark: Pointer); override;
+    procedure InternalHandleException; override;
+    procedure InternalInitRecord(Buffer: PChar); override;
+    procedure InternalLast; override;
+    procedure InternalOpen; override;
+    procedure InternalPost; override;
+    procedure InternalSetToRecord(Buffer: PChar); override;
+    function IsCursorOpen: Boolean; override;    
+    procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
+    procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
+    procedure SetExpectedAppends(AValue:Integer);
+    procedure SetExpectedUpdates(AValue:Integer);
+    procedure SetExpectedDeletes(AValue:Integer);
+    procedure SetFieldData(Field: TField; Buffer: Pointer); override;  
+    procedure SetRecNo(Value: Integer); override;
+  public
+    constructor Create(AOwner: TComponent); override;
+    destructor Destroy; override;
+    function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
+    // Additional procedures
+    function ApplyUpdates: Boolean; virtual;
+    function CreateTable: Boolean; virtual;
+    function ExecSQL:Integer;
+    function ExecSQL(const ASql:String):Integer;
+    procedure RefetchData;
+    function UpdatesPending: Boolean;
+    {$ifdef DEBUGACTIVEBUFFER}
+    procedure SetCurrentItem(Value:PDataRecord);
+    property FCurrentItem: PDataRecord read FFCurrentItem write SetCurrentItem;
+    {$endif}
+    {$ifdef USE_SQLITEDS_INTERNALS}
+    property BeginItem: PDataRecord read FBeginItem;
+    property EndItem: PDataRecord read FEndItem;
+    property UpdatedItems: TList read FUpdatedItems;
+    property AddedItems: TList read FAddedItems;
+    property DeletedItems: TList read FDeletedItems;
+    {$endif}
+    property ComplexSql: Boolean read FComplexSql write FComplexSql;
+    property ExpectedAppends: Integer read FExpectedAppends write SetExpectedAppends;
+    property ExpectedUpdates: Integer read FExpectedUpdates write SetExpectedUpdates;
+    property ExpectedDeletes: Integer read FExpectedDeletes write SetExpectedDeletes;
+    property IndexFields[Value: Integer]: TField read GetIndexFields;
+    property SqliteReturnId: Integer read FSqliteReturnId;
+   published
+    property IndexFieldNames: string read FIndexFieldNames write FIndexFieldNames;
+    property FileName: String read FFileName write FFileName;
+    property PrimaryKey: String read FPrimaryKey write FPrimaryKey;
+    property SaveOnClose: Boolean read FSaveOnClose write FSaveOnClose; 
+    property SaveOnRefetch: Boolean read FSaveOnRefetch write FSaveOnRefetch;
+    property SQL: String read FSql write FSql;
+    property TableName: String read FTableName write FTableName;   
+    property MasterSource: TDataSource read GetMasterSource write SetMasterSource;
+    property MasterFields: string read GetMasterFields write SetMasterFields;
+    
+    property Active;
+    property FieldDefs;
+     
+    //Events
+    property BeforeOpen;
+    property AfterOpen;
+    property BeforeClose;
+    property AfterClose;
+    property BeforeInsert;
+    property AfterInsert;
+    property BeforeEdit;
+    property AfterEdit;
+    property BeforePost;
+    property AfterPost;
+    property BeforeCancel;
+    property AfterCancel;
+    property BeforeDelete;
+    property AfterDelete;
+    property BeforeScroll;
+    property AfterScroll;
+    property OnDeleteError;
+    property OnEditError;
+  end;
+  
+implementation
+
+uses
+  strutils;
+
+const
+  SQLITE_OK = 0;//sqlite2.x.x and sqlite3.x.x defines this equal
+
+// TDSStream
+
+constructor TDSStream.Create(const ActiveItem: PDataRecord; FieldIndex:Integer);
+begin
+  inherited Create;
+  FPosition:=0;
+  FActiveItem:=ActiveItem;
+  FFieldIndex:=FieldIndex;
+  FFieldRow:=ActiveItem^.Row[FieldIndex];
+  if FFieldRow <> nil then
+    FRowSize:=StrLen(FFieldRow)
+  else
+    FRowSize:=0;  
+end;  
+
+function TDSStream.Seek(Offset: Longint; Origin: Word): Longint;
+begin
+  Case Origin of
+    soFromBeginning : FPosition:=Offset;
+    soFromEnd       : FPosition:=FRowSize+Offset;
+    soFromCurrent   : FPosition:=FPosition+Offset;
+  end;
+  Result:=FPosition;
+end;
+
+function TDSStream.Write(const Buffer; Count: Longint): Longint; 
+var
+  NewRow:PChar;
+begin
+  Result:=Count;
+  if Count = 0 then
+    Exit;
+  //Todo: see how TDbMemo read/write to field and choose best if order
+  if FPosition = 0 then
+  begin
+    NewRow:=StrAlloc(Count+1);
+    (NewRow+Count)^:=#0;
+    Move(Buffer,NewRow^,Count);    
+  end
+  else
+  begin
+    NewRow:=StrAlloc(FRowSize+Count+1);
+    (NewRow+Count+FRowSize)^:=#0;
+    Move(FFieldRow^,NewRow^,FRowSize);
+    Move(Buffer,(NewRow+FRowSize)^,Count);
+  end;
+  FActiveItem^.Row[FFieldIndex]:=NewRow;    
+  StrDispose(FFieldRow);
+  FFieldRow:=NewRow;
+  FRowSize:=StrLen(NewRow);
+  Inc(FPosition,Count);
+  {$ifdef DEBUG}
+  WriteLn('Writing a BlobStream');
+  WriteLn('Stream.Size: ',StrLen(NewRow));
+  WriteLn('Stream Value: ',NewRow);
+  WriteLn('FPosition:',FPosition);
+  {$endif}
+end; 
+ 
+function TDSStream.Read(var Buffer; Count: Longint): Longint; 
+var
+  BytesToMove:Integer;
+begin
+  if (FRowSize - FPosition) >= Count then
+    BytesToMove:=Count
+  else
+    BytesToMove:=FRowSize - FPosition;   
+  Move((FFieldRow+FPosition)^,Buffer,BytesToMove);
+  Inc(FPosition,BytesToMove);
+  Result:=BytesToMove;  
+  {$ifdef DEBUG}
+  WriteLn('Reading a BlobStream');
+  WriteLn('Bytes requested: ',Count);
+  WriteLn('Bytes Moved: ',BytesToMove);
+  WriteLn('Stream.Size: ',FRowSize);
+  WriteLn('Stream Value: ',FFieldRow);
+  {$endif}
+end; 
+ 
+// TCustomSqliteDataset override methods
+
+function TCustomSqliteDataset.AllocRecordBuffer: PChar;
+var
+  APointer:Pointer;
+begin
+  APointer := AllocMem(FBufferSize);
+  PDataRecord(APointer^):=FBeginItem;
+  Result:=APointer;
+end;
+
+constructor TCustomSqliteDataset.Create(AOwner: TComponent);
+begin
+  // setup special items
+  New(FBeginItem);
+  New(FCacheItem);
+  New(FEndItem);
+  
+  FBeginItem^.Previous:=nil;
+  FEndItem^.Next:=nil;
+  
+  FBeginItem^.BookMarkFlag:=bfBOF;
+  FCacheItem^.BookMarkFlag:=bfEOF;
+  FEndItem^.BookMarkFlag:=bfEOF;
+  
+  FMasterLink:=TMasterDataLink.Create(Self);
+  FMasterLink.OnMasterChange:=@MasterChanged;
+  FMasterLink.OnMasterDisable:=@MasterDisabled;
+  FIndexFieldList:=TList.Create;
+  BookmarkSize := SizeOf(Pointer);
+  FBufferSize := SizeOf(PPDataRecord);
+  FUpdatedItems:= TList.Create;
+  FUpdatedItems.Capacity:=20;
+  FAddedItems:= TList.Create;
+  FAddedItems.Capacity:=20;
+  FOrphanItems:= TList.Create;
+  FOrphanItems.Capacity:=20;
+  FDeletedItems:= TList.Create;
+  FDeletedItems.Capacity:=20;
+  inherited Create(AOwner);
+end;
+
+function TCustomSqliteDataset.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
+begin
+  Result:= TDSStream.Create(PPDataRecord(ActiveBuffer)^,Field.FieldNo - 1);
+end;  
+
+destructor TCustomSqliteDataset.Destroy;
+begin
+  inherited Destroy;
+  FUpdatedItems.Destroy;
+  FAddedItems.Destroy;
+  FDeletedItems.Destroy;
+  FOrphanItems.Destroy;
+  FMasterLink.Destroy;
+  FIndexFieldList.Destroy;
+  // dispose special items
+  Dispose(FBeginItem);
+  Dispose(FCacheItem);
+  Dispose(FEndItem);
+end;
+
+function TCustomSqliteDataset.GetIndexFields(Value: Integer): TField;
+begin
+  if (Value < 0) or (Value > FIndexFieldList.Count - 1) then
+    DatabaseError('Error acessing IndexFields: Index out of bonds');
+  Result:= TField(FIndexFieldList[Value]);
+end;
+
+procedure TCustomSqliteDataset.DisposeLinkedList;
+var
+  TempItem:PDataRecord;
+  Counter,I:Integer;
+begin
+  //Todo: insert debug info
+  FDataAllocated:=False;
+  TempItem:=FBeginItem^.Next;
+  if TempItem <> nil then
+    while TempItem^.Next <> nil do
+    begin
+      for Counter:= 0 to FRowCount - 1 do
+        StrDispose(TempItem^.Row[Counter]);  
+      FreeMem(TempItem^.Row,FRowBufferSize);
+      TempItem:=TempItem^.Next;
+      Dispose(TempItem^.Previous);
+    end; 
+  
+  //Dispose FBeginItem.Row
+  FreeMem(FBeginItem^.Row,FRowBufferSize);
+    
+  //Dispose cache item
+  for Counter:= 0 to FRowCount - 1 do
+    StrDispose(FCacheItem^.Row[Counter]);
+  FreeMem(FCacheItem^.Row,FRowBufferSize);
+
+  //Dispose OrphanItems
+  for Counter:= 0 to FOrphanItems.Count - 1 do
+  begin
+    TempItem:=PDataRecord(FOrphanItems[Counter]);
+    for I:= 0 to FRowCount - 1 do
+      StrDispose(TempItem^.Row[I]);  
+    FreeMem(TempItem^.Row,FRowBufferSize);
+    Dispose(TempItem);  
+  end;     
+end;
+
+procedure TCustomSqliteDataset.FreeRecordBuffer(var Buffer: PChar);
+begin
+  FreeMem(Buffer);
+end;
+
+procedure TCustomSqliteDataset.GetBookmarkData(Buffer: PChar; Data: Pointer);
+begin
+  Pointer(Data^) := PPDataRecord(Buffer)^^.BookmarkData;
+end;
+
+function TCustomSqliteDataset.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
+begin
+  Result := PPDataRecord(Buffer)^^.BookmarkFlag;
+end;
+
+function TCustomSqliteDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
+var
+  ValError:Word;
+  FieldRow:PChar;
+begin
+  FieldRow:=PPDataRecord(ActiveBuffer)^^.Row[Field.FieldNo - 1];
+  Result := FieldRow <> nil;  
+  if Result and (Buffer <> nil) then //supports GetIsNull
+  begin
+    case Field.Datatype of
+    ftString:
+      begin
+        Move(FieldRow^,PChar(Buffer)^,StrLen(FieldRow)+1);
+      end;
+    ftInteger,ftBoolean,ftWord,ftAutoInc:
+      begin
+        Val(StrPas(FieldRow),LongInt(Buffer^),ValError);
+        Result:= ValError = 0;  
+      end;
+    ftFloat,ftDateTime,ftTime,ftDate:
+      begin
+        Val(StrPas(FieldRow),Double(Buffer^),ValError);
+        Result:= ValError = 0; 
+      end;    
+    end;
+  end;        
+end;
+
+function TCustomSqliteDataset.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
+begin
+  Result := grOk;
+  case GetMode of
+    gmPrior:
+      if (FCurrentItem^.Previous = FBeginItem) or (FCurrentItem = FBeginItem) then
+      begin
+        Result := grBOF;
+        FCurrentItem := FBeginItem;
+      end
+      else
+        FCurrentItem:=FCurrentItem^.Previous;
+    gmCurrent:
+      if (FCurrentItem = FBeginItem) or (FCurrentItem = FEndItem) then
+         Result := grError;
+    gmNext:
+      if (FCurrentItem = FEndItem) or (FCurrentItem^.Next = FEndItem) then
+        Result := grEOF
+      else
+        FCurrentItem:=FCurrentItem^.Next;
+  end; //case
+  if Result = grOk then
+  begin
+    PDataRecord(Pointer(Buffer)^):=FCurrentItem;
+    with FCurrentItem^ do
+    begin
+      BookmarkData := FCurrentItem;
+      BookmarkFlag := bfCurrent;
+    end;
+  end
+    else if (Result = grError) and DoCheck then
+      DatabaseError('SqliteDs - No records',Self);
+end;
+
+function TCustomSqliteDataset.GetRecordCount: Integer;
+begin
+  Result := FRecordCount;
+end;
+
+function TCustomSqliteDataset.GetRecNo: Integer;
+var
+  TempItem,TempActive:PDataRecord;
+begin
+  Result:= -1;
+  if FRecordCount = 0 then
+    Exit;  
+  TempItem:=FBeginItem;
+  TempActive:=PPDataRecord(ActiveBuffer)^;
+  if TempActive = FCacheItem then // Record not posted yet
+    Result:=FRecordCount 
+  else
+    while TempActive <> TempItem do
+    begin
+      if TempItem^.Next <> nil then
+      begin
+        inc(Result);
+        TempItem:=TempItem^.Next;
+      end  
+      else
+      begin
+        Result:=-1;
+        DatabaseError('Sqliteds.GetRecNo - ActiveItem Not Found',Self);
+        break;    
+      end;      
+    end;  
+end;
+
+function TCustomSqliteDataset.GetRecordSize: Word;
+begin
+  Result := FBufferSize; //??
+end;
+
+procedure TCustomSqliteDataset.InternalAddRecord(Buffer: Pointer; DoAppend: Boolean);
+var
+  NewItem: PDataRecord;
+  Counter:Integer;
+begin
+  {$ifdef DEBUG}
+  if PPDataRecord(Buffer)^ <> FCacheItem then
+    DatabaseError('PPDataRecord(Buffer) <> FCacheItem - Problem',Self);
+  {$endif}
+  New(NewItem);
+  GetMem(NewItem^.Row,FRowBufferSize);
+  for Counter := 0 to FRowCount - 1 do 
+    NewItem^.Row[Counter]:=StrNew(FCacheItem^.Row[Counter]);   
+  FEndItem^.Previous^.Next:=NewItem;
+  NewItem^.Previous:=FEndItem^.Previous;
+  NewItem^.Next:=FEndItem;
+  FEndItem^.Previous:=NewItem;
+  Inc(FRecordCount);
+  if FAutoIncFieldNo <> - 1 then
+    Inc(FNextAutoInc);
+  FAddedItems.Add(NewItem);
+end;
+
+procedure TCustomSqliteDataset.InternalClose;
+begin
+  if FSaveOnClose then
+    ApplyUpdates;
+  //BindFields(False);
+  if DefaultFields then
+    DestroyFields;
+  if FDataAllocated then
+    DisposeLinkedList;  
+  if FSqliteHandle <> nil then
+  begin
+    SqliteClose(FSqliteHandle);
+    FSqliteHandle := nil;
+  end;
+  FAddedItems.Clear;
+  FUpdatedItems.Clear;
+  FDeletedItems.Clear;
+  FOrphanItems.Clear;
+  FRecordCount:=0;
+end;
+
+procedure TCustomSqliteDataset.InternalDelete;
+var
+  TempItem:PDataRecord;
+  ValError,TempInteger:Integer;
+begin
+  If FRecordCount = 0 then
+    Exit;
+  Dec(FRecordCount);
+  TempItem:=PPDataRecord(ActiveBuffer)^;
+  // Remove from changed list
+  FUpdatedItems.Remove(TempItem);
+  if FAddedItems.Remove(TempItem) = -1 then
+    FDeletedItems.Add(TempItem);
+  FOrphanItems.Add(TempItem);
+  TempItem^.Next^.Previous:=TempItem^.Previous;
+  TempItem^.Previous^.Next:=TempItem^.Next;
+  if FCurrentItem = TempItem then
+  begin
+    if FCurrentItem^.Previous <> FBeginItem then
+      FCurrentItem:= FCurrentItem^.Previous
+    else
+      FCurrentItem:= FCurrentItem^.Next;  
+  end; 
+  // Dec FNextAutoInc (only if deleted item is the last record)  
+  if FAutoIncFieldNo <> -1 then
+  begin
+    Val(StrPas(TempItem^.Row[FAutoIncFieldNo]),TempInteger,ValError);
+    if (ValError = 0) and (TempInteger = (FNextAutoInc - 1)) then
+      Dec(FNextAutoInc);
+  end;    
+end;
+
+procedure TCustomSqliteDataset.InternalFirst;
+begin
+  FCurrentItem := FBeginItem;
+end;
+
+procedure TCustomSqliteDataset.InternalGotoBookmark(ABookmark: Pointer);
+begin
+  FCurrentItem := PDataRecord(ABookmark^);
+end;
+
+procedure TCustomSqliteDataset.InternalHandleException;
+begin
+  //??
+end;
+
+procedure TCustomSqliteDataset.InternalInitRecord(Buffer: PChar);
+var
+  Counter:Integer;
+  TempStr:String;  
+begin
+  for Counter:= 0 to FRowCount - 1 do
+  begin
+    StrDispose(FCacheItem^.Row[Counter]);
+    FCacheItem^.Row[Counter]:=nil;
+  end;
+  if FAutoIncFieldNo <> - 1 then
+  begin
+    Str(FNextAutoInc,TempStr);
+    FCacheItem^.Row[FAutoIncFieldNo]:=StrAlloc(Length(TempStr)+1);
+    StrPCopy(FCacheItem^.Row[FAutoIncFieldNo],TempStr);
+  end;  
+  PPDataRecord(Buffer)^:=FCacheItem;    
+end;
+
+procedure TCustomSqliteDataset.InternalLast;
+begin
+  FCurrentItem := FEndItem;
+end;
+
+procedure TCustomSqliteDataset.InternalOpen;
+begin
+  FAutoIncFieldNo:=-1;
+  if not FileExists(FFileName) then
+    DatabaseError('TCustomSqliteDataset - File '+FFileName+' not found');
+  if (FTablename = '') and not (FComplexSql) then
+    DatabaseError('TCustomSqliteDataset - Tablename not set');
+
+  if MasterSource <> nil then
+  begin
+    FSql := 'Select * from '+FTableName+';'; // forced to obtain all fields
+    FMasterLink.FieldNames:=MasterFields; //this should fill MasterLinks.Fields
+    //todo: ignore if Fields.Count = 0 (OnMasterChanged will not be called) or
+    // raise a error?
+    //if (FMasterLink.Fields.Count = 0) and (MasterSource.DataSet.Active) then
+    //   DatabaseError('Master Fields are not defined correctly');
+  end;
+  
+  FSqliteHandle:=GetSqliteHandle;
+  if FSql = '' then
+    FSql := 'Select * from '+FTableName+';';
+  InternalInitFieldDefs;
+
+  if DefaultFields then 
+    CreateFields;
+
+  BindFields(True);
+
+  UpdateIndexFields;
+  if FMasterLink.Active and (FIndexFieldList.Count <> FMasterLink.Fields.Count) then
+    DatabaseError('MasterFields count doesnt match IndexFields count');
+
+  // Get PrimaryKeyNo if available
+  if Fields.FindField(FPrimaryKey) <> nil then
+    FPrimaryKeyNo:=Fields.FindField(FPrimaryKey).FieldNo - 1  
+  else
+    FPrimaryKeyNo:=FAutoIncFieldNo; // -1 if there's no AutoIncField 
+       
+  BuildLinkedList;               
+  FCurrentItem:=FBeginItem;
+end;
+
+procedure TCustomSqliteDataset.InternalPost;
+begin
+  if (State<>dsEdit) then 
+    InternalAddRecord(ActiveBuffer,True);
+end;
+
+procedure TCustomSqliteDataset.InternalSetToRecord(Buffer: PChar);
+begin
+  FCurrentItem:=PPDataRecord(Buffer)^;
+end;
+
+function TCustomSqliteDataset.IsCursorOpen: Boolean;
+begin
+   Result := FDataAllocated;
+end;
+
+procedure TCustomSqliteDataset.SetBookmarkData(Buffer: PChar; Data: Pointer);
+begin
+  PPDataRecord(Buffer)^^.BookmarkData := Pointer(Data^);
+end;
+
+procedure TCustomSqliteDataset.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
+begin
+  PPDataRecord(Buffer)^^.BookmarkFlag := Value;
+end;
+
+procedure TCustomSqliteDataset.SetExpectedAppends(AValue:Integer);
+begin
+  if Assigned(FAddedItems) then
+    FAddedItems.Capacity:=AValue;
+end;  
+
+procedure TCustomSqliteDataset.SetExpectedUpdates(AValue:Integer);
+begin
+  if Assigned(FUpdatedItems) then
+    FUpdatedItems.Capacity:=AValue;
+end;  
+
+procedure TCustomSqliteDataset.SetExpectedDeletes(AValue:Integer);
+begin
+  if Assigned(FDeletedItems) then
+    FDeletedItems.Capacity:=AValue;
+end;  
+
+procedure TCustomSqliteDataset.SetFieldData(Field: TField; Buffer: Pointer);
+var
+  TempStr:String;
+  ActiveItem:PDataRecord;
+begin
+  ActiveItem:=PPDataRecord(ActiveBuffer)^;
+  if (ActiveItem <> FCacheItem) and (FUpdatedItems.IndexOf(ActiveItem) = -1) and (FAddedItems.IndexOf(ActiveItem) = -1) then
+    FUpdatedItems.Add(ActiveItem);
+    
+  StrDispose(ActiveItem^.Row[Pred(Field.FieldNo)]);
+  if Buffer <> nil then
+  begin
+    case Field.Datatype of
+    ftString:
+      begin            
+        ActiveItem^.Row[Pred(Field.FieldNo)]:=StrNew(PChar(Buffer));
+      end;
+    ftInteger,ftBoolean,ftWord:
+      begin          
+        Str(LongInt(Buffer^),TempStr);  
+        ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
+        StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);
+      end;
+    ftFloat,ftDateTime,ftDate,ftTime:
+      begin
+        Str(Double(Buffer^),TempStr);  
+        ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
+        StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);
+      end;    
+    end;// case
+  end//if
+  else
+    ActiveItem^.Row[Pred(Field.FieldNo)]:=nil;        
+end;
+
+procedure TCustomSqliteDataset.SetRecNo(Value: Integer);
+var
+  Counter:Integer;
+  TempItem:PDataRecord;
+begin
+  if (Value >= FRecordCount) or (Value < 0) then
+    DatabaseError('SqliteDs - Record Number Out Of Range');
+  TempItem:=FBeginItem;
+  for Counter := 0 to Value do
+    TempItem:=TempItem^.Next;
+  PPDataRecord(ActiveBuffer)^:=TempItem;   
+end;
+
+// Specific functions 
+
+procedure TCustomSqliteDataset.MasterChanged(Sender: TObject);
+  function GetSqlStr(AField:TField):String;
+  begin
+    case AField.DataType of
+      ftString,ftMemo: Result:='"'+AField.AsString+'"';//todo: handle " caracter properly
+      ftDateTime,ftDate,ftTime:Str(AField.AsDateTime,Result);
+    else
+      Result:=AField.AsString;  
+    end;//case
+  end;//function
+
+var
+  AFilter:String;
+  i:Integer;
+begin
+  AFilter:=' where ';
+  for i:= 0 to FMasterLink.Fields.Count - 1 do
+  begin
+    AFilter:=AFilter + IndexFields[i].FieldName +' = '+ GetSqlStr(TField(FMasterLink.Fields[i]));
+    if i <> FMasterLink.Fields.Count - 1 then
+      AFilter:= AFilter + ' and ';
+  end;
+  FSql:='Select * from '+FTableName+AFilter;
+  {$ifdef DEBUG}
+  writeln('Sql used to filter detail dataset:');
+  writeln(FSql);
+  {$endif}
+  RefetchData;
+end;
+
+procedure TCustomSqliteDataset.MasterDisabled(Sender: TObject);
+begin
+  FSql:='Select * from '+FTableName+';'; 
+  RefetchData;
+end;
+
+procedure TCustomSqliteDataset.SetMasterFields(Value: String);
+begin
+  if Active then
+    DatabaseError('It''s not allowed to set MasterFields property in a open dataset');
+  FMasterLink.FieldNames:=Value;
+end;
+
+function TCustomSqliteDataset.GetMasterFields: String;
+begin
+  Result:=FMasterLink.FieldNames;
+end;
+
+
+procedure TCustomSqliteDataset.UpdateIndexFields;
+begin
+  if FIndexFieldNames <> '' then
+  begin
+    FIndexFieldList.Clear;
+    try
+      GetFieldList(FIndexFieldList, FIndexFieldNames);
+    except
+      FIndexFieldList.Clear;
+      raise;
+    end;
+  end;
+end;
+
+function TCustomSqliteDataset.GetMasterSource: TDataSource;
+begin
+  Result := FMasterLink.DataSource;
+end;
+
+procedure TCustomSqliteDataset.SetMasterSource(Value: TDataSource);
+begin
+  FMasterLink.DataSource := Value;
+end;
+
+
+function TCustomSqliteDataset.ExecSQL(const ASql:String):Integer;
+var
+  AHandle: Pointer;
+begin
+  Result:=0;
+  //Todo check if Filename exists
+  if FSqliteHandle <> nil then
+    AHandle:=FSqliteHandle
+  else 
+    if FFileName <> '' then  
+      AHandle := GetSqliteHandle
+    else
+      DatabaseError ('ExecSql - FileName not set');    
+  FSqliteReturnId:= SqliteExec(AHandle,PChar(ASql));
+  //todo: add a way to get the num of changes
+  //Result:=sqlite_changes(AHandle);
+  if AHandle <> FSqliteHandle then
+    SqliteClose(AHandle);
+end;    
+
+function TCustomSqliteDataset.ExecSQL:Integer;
+begin
+  Result:=ExecSQL(FSql);  
+end;
+
+function TCustomSqliteDataset.ApplyUpdates:Boolean;
+var
+  CounterFields,CounterItems,StatementsCounter:Integer;
+  SqlTemp,KeyName,ASqlLine,TemplateStr:String;
+begin
+  Result:=False;
+  if (FPrimaryKeyNo <> -1) and not FComplexSql then
+  begin
+    StatementsCounter:=0;
+    KeyName:=Fields[FPrimaryKeyNo].FieldName;
+    {$ifdef DEBUG}
+    WriteLn('ApplyUpdates called');
+    if FPrimaryKeyNo = FAutoIncFieldNo then
+      WriteLn('Using an AutoInc field as primary key');
+    WriteLn('PrimaryKey: ',KeyName);
+    WriteLn('PrimaryKeyNo: ',FPrimaryKeyNo);
+    {$endif}
+    SqlTemp:='BEGIN TRANSACTION;';
+    // In some situations (LCL apps) FBeginItems is inserted in FUpdatedItems
+    FUpdatedItems.Remove(FBeginItem);
+    // Update changed records
+    if FUpdatedItems.Count > 0 then
+      TemplateStr:='UPDATE '+FTableName+' SET ';
+    for CounterItems:= 0 to FUpdatedItems.Count - 1 do  
+    begin
+      ASqlLine:=TemplateStr;
+      for CounterFields:= 0 to Fields.Count - 1 do
+      begin
+        if PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields] <> nil then
+        begin
+          ASqlLine:=ASqlLine + Fields[CounterFields].FieldName +' = ';
+          if not (Fields[CounterFields].DataType in [ftString,ftMemo]) then
+            ASqlLine:=ASqlLine+StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields])+ ','
+          else
+            ASqlLine:=ASqlLine+''''+
+              AnsiReplaceStr(StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields]),'''','''''')+''',';  
+        end
+        else
+          ASqlLine:=ASqlLine + Fields[CounterFields].FieldName +' = NULL,';  
+      end;
+      //Todo: see if system.delete trunks AnsiString
+      system.delete(ASqlLine,Length(ASqlLine),1);
+      SqlTemp:=SqlTemp + ASqlLine+' WHERE '+KeyName+' = '+StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[FPrimaryKeyNo])+';';
+      inc(StatementsCounter);
+      //ApplyUpdates each 400 statements
+      if StatementsCounter = 400 then
+      begin
+        SqlTemp:=SqlTemp+'END TRANSACTION;';
+        FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
+        StatementsCounter:=0;
+        SqlTemp:='BEGIN TRANSACTION;';
+      end;  
+    end;
+    // Add new records
+    // Build TemplateStr
+    if FAddedItems.Count > 0 then
+    begin
+      TemplateStr:='INSERT INTO '+FTableName+ ' (';
+      for CounterFields:= 0 to Fields.Count - 1 do
+      begin
+        TemplateStr:=TemplateStr + Fields[CounterFields].FieldName; 
+        if CounterFields <> Fields.Count - 1 then
+          TemplateStr:=TemplateStr+',';
+      end; 
+      TemplateStr:=TemplateStr+') VALUES (';
+    end;  
+    for CounterItems:= 0 to FAddedItems.Count - 1 do  
+    begin
+      ASqlLine:=TemplateStr;
+      for CounterFields:= 0 to Fields.Count - 1 do
+      begin
+        if PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields] <> nil then
+        begin
+          if not (Fields[CounterFields].DataType in [ftString,ftMemo]) then
+            ASqlLine:=ASqlLine+StrPas(PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields])
+          else
+            ASqlLine:=ASqlLine+''''+
+              AnsiReplaceStr(StrPas(PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields]),'''','''''')+'''';  
+        end
+        else
+          ASqlLine:=ASqlLine + 'NULL';
+        //Todo: see if delete ASqline is faster
+        if CounterFields <> Fields.Count - 1 then
+          ASqlLine:=ASqlLine+',';
+      end;
+      SqlTemp:=SqlTemp+ASqlLine+');';    
+      inc(StatementsCounter);
+      //ApplyUpdates each 400 statements
+      if StatementsCounter = 400 then
+      begin
+        SqlTemp:=SqlTemp+'END TRANSACTION;';
+        FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
+        StatementsCounter:=0;
+        SqlTemp:='BEGIN TRANSACTION;';
+      end;  
+    end;  
+    // Delete Items
+    if FDeletedItems.Count > 0 then
+      TemplateStr:='DELETE FROM '+FTableName+ ' WHERE '+KeyName+' = ';
+    for CounterItems:= 0 to FDeletedItems.Count - 1 do  
+    begin
+      SqlTemp:=SqlTemp+TemplateStr+
+        StrPas(PDataRecord(FDeletedItems[CounterItems])^.Row[FPrimaryKeyNo])+';';    
+      inc(StatementsCounter);
+      //ApplyUpdates each 400 statements
+      if StatementsCounter = 400 then
+      begin
+        SqlTemp:=SqlTemp+'END TRANSACTION;';
+        FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
+        StatementsCounter:=0;
+        SqlTemp:='BEGIN TRANSACTION;';
+      end;    
+    end;
+    SqlTemp:=SqlTemp+'END TRANSACTION;';
+    {$ifdef DEBUG}
+    writeln('ApplyUpdates Sql: ',SqlTemp);
+    {$endif}  
+   FAddedItems.Clear;
+   FUpdatedItems.Clear;
+   FDeletedItems.Clear;   
+   FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
+   Result:= FSqliteReturnId = SQLITE_OK;
+  end;  
+  {$ifdef DEBUG}
+    writeln('ApplyUpdates Result: ',Result);
+  {$endif}   
+end;    
+
+function TCustomSqliteDataset.CreateTable: Boolean;
+var
+  SqlTemp:String;
+  Counter:Integer;
+begin
+  {$ifdef DEBUG}
+  if FTableName = '' then
+    WriteLn('CreateTable : TableName Not Set');
+  if FieldDefs.Count = 0 then 
+    WriteLn('CreateTable : FieldDefs Not Initialized');
+  {$endif}
+  if (FTableName <> '') and (FieldDefs.Count > 0) then
+  begin
+    FSqliteHandle:= GetSqliteHandle;
+    SqlTemp:='CREATE TABLE '+FTableName+' (';
+    for Counter := 0 to FieldDefs.Count-1 do
+    begin
+      SqlTemp:=SqlTemp + FieldDefs[Counter].Name;
+      case FieldDefs[Counter].DataType of
+        ftInteger:
+          SqlTemp:=SqlTemp + ' INTEGER';
+        ftString:
+          SqlTemp:=SqlTemp + ' VARCHAR';
+        ftBoolean:
+          SqlTemp:=SqlTemp + ' BOOLEAN';
+        ftFloat:
+          SqlTemp:=SqlTemp + ' FLOAT';  
+        ftWord:
+          SqlTemp:=SqlTemp + ' WORD';
+        ftDateTime:
+          SqlTemp:=SqlTemp + ' DATETIME';
+        ftDate:
+          SqlTemp:=SqlTemp + ' DATE';
+        ftTime:
+          SqlTemp:=SqlTemp + ' TIME';                
+        ftAutoInc:
+          SqlTemp:=SqlTemp + ' AUTOINC'; 
+        ftMemo:
+          SqlTemp:=SqlTemp + ' MEMO';   
+      else
+        SqlTemp:=SqlTemp + ' VARCHAR';    
+      end;
+      if Counter <>  FieldDefs.Count - 1 then
+        SqlTemp:=SqlTemp+ ' , ';   
+    end;
+    SqlTemp:=SqlTemp+');';
+    {$ifdef DEBUG}
+    writeln('CreateTable Sql: ',SqlTemp);
+    {$endif}  
+    FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
+    Result:= FSqliteReturnId = SQLITE_OK;
+    SqliteClose(FSqliteHandle);
+    FSqliteHandle:=nil;
+  end
+  else
+    Result:=False;  
+end; 
+
+procedure TCustomSqliteDataset.RefetchData;
+var
+  i:Integer;
+begin
+  //Close
+  if FSaveOnRefetch then
+    ApplyUpdates;
+  if FDataAllocated then
+    DisposeLinkedList;  
+  FAddedItems.Clear;
+  FUpdatedItems.Clear;
+  FDeletedItems.Clear;
+  FOrphanItems.Clear;
+  //Reopen
+  BuildLinkedList;               
+  FCurrentItem:=FBeginItem;
+  for i := 0 to BufferCount - 1 do
+    PPDataRecord(Buffers[i])^:=FBeginItem;
+  Resync([]);
+end;  
+
+
+function TCustomSqliteDataset.UpdatesPending: Boolean;
+begin
+  Result:= (FDeletedItems.Count > 0) or
+    (FAddedItems.Count > 0) or (FUpdatedItems.Count > 0);
+end;
+
+{$ifdef DEBUGACTIVEBUFFER}
+procedure TCustomSqliteDataset.SetCurrentItem(Value:PDataRecord);
+var
+ ANo:Integer;
+
+  function GetItemPos:Integer;
+  var
+    TempItem:PDataRecord;
+  begin
+    Result:= -1;
+    TempItem:=FBeginItem;
+    if Value = FCacheItem then
+       Result:=-2
+    else
+    while Value <> TempItem do
+    begin
+     if TempItem^.Next <> nil then
+     begin
+       inc(Result);
+       TempItem:=TempItem^.Next;
+     end
+     else
+     begin
+      Result:=-1;
+      break;
+     end;
+    end;
+  end;
+
+begin
+  if Value = FBeginItem then
+  begin
+    writeln('FCurrentItem set to FBeginItem: ',IntToHex(Integer(Value),0));
+    FFCurrentItem:=Value;
+  end
+  else
+    if Value = FEndItem then
+    begin
+      writeln('FCurrentItem set to FEndItem: ',IntToHex(Integer(Value),0));
+      FFCurrentItem:=Value;
+    end
+    else
+      if Value = FCacheItem then
+      begin
+        writeln('FCurrentItem set to FCacheItem: ',IntToHex(Integer(Value),0));
+        FFCurrentItem:=Value;
+      end
+      else
+      begin
+        writeln('FCurrentItem set from ',IntToHex(Integer(FFCurrentItem),0),' to ',IntToHex(Integer(Value),0));
+        Ano:=GetItemPos;
+        writeln('Item position is ',ANo);
+        FFCurrentItem:=Value;
+      end;
+end;
+{$endif}
+
+end.

+ 290 - 0
fcl/db/sqlite/sqlite3ds.pas

@@ -0,0 +1,290 @@
+unit sqlite3ds;
+
+{
+    This is TSqlite3Dataset, 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
+  { TSqlite3Dataset }
+
+  TSqlite3Dataset = class (TCustomSqliteDataset)
+  private
+    function SqliteExec(AHandle: Pointer; ASql:PChar):Integer;override;
+    function GetSqliteHandle: Pointer; override;
+    procedure SqliteClose(AHandle: Pointer);override;
+    procedure BuildLinkedList; override;
+  protected
+    procedure InternalInitFieldDefs; override;
+  public
+    function SqliteReturnString: String; override;
+    function TableExists: Boolean;override;
+  end;
+
+implementation
+
+uses
+  sqlite3,db;
+
+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;
+
+{ TSqlite3Dataset }
+
+function TSqlite3Dataset.SqliteExec(AHandle: Pointer; ASql: PChar): Integer;
+begin
+  Result:=sqlite3_exec(AHandle, ASql, nil, nil, nil);
+end;
+
+procedure TSqlite3Dataset.SqliteClose(AHandle: Pointer);
+begin
+  sqlite3_close(AHandle);
+  //todo:handle return data
+end;
+
+
+function TSqlite3Dataset.GetSqliteHandle: Pointer;
+begin
+  FSqliteReturnId:=sqlite3_open(PChar(FFileName),@Result);
+end;
+
+procedure TSqlite3Dataset.InternalInitFieldDefs;
+var
+  vm:Pointer;
+  ColumnStr:String;
+  Counter,FieldSize:Integer;
+  AType:TFieldType;
+begin
+  FieldDefs.Clear;
+  sqlite3_prepare(FSqliteHandle,PChar(FSql),-1,@vm,nil);
+	sqlite3_step(vm);
+	for Counter:= 0 to sqlite3_column_count(vm) - 1 do
+	begin
+   ColumnStr:= UpperCase(StrPas(sqlite3_column_decltype(vm,Counter)));
+   if (ColumnStr = 'INTEGER') then
+   begin
+     AType:= ftInteger;
+     FieldSize:=SizeOf(Integer);
+   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 = 'MEMO') then
+   begin
+     AType:= ftMemo;
+     FieldSize:=10;//??
+   end else if (ColumnStr = 'AUTOINC') then
+   begin
+     AType:= ftAutoInc;
+     FieldSize:=SizeOf(Integer);
+     if FAutoIncFieldNo = -1 then
+       FAutoIncFieldNo:= Counter;
+   end else
+   begin
+     AType:= ftString;
+     FieldSize:=10; //??
+   end;
+   FieldDefs.Add(StrPas(sqlite3_column_name(vm,Counter)), AType, FieldSize, False);
+   {$ifdef DEBUG}
+   writeln('Field Name: ',sqlite3_column_name(vm,Counter));
+   writeln('Field Type: ',sqlite3_column_decltype(vm,Counter));
+   {$endif}
+  end;
+	sqlite3_finalize(vm);
+  FRowBufferSize:=(SizeOf(PPChar)*FieldDefs.Count);
+  {$ifdef DEBUG}
+  writeln('FieldDefs.Count: ',FieldDefs.Count);
+  {$endif}
+end;
+
+procedure TSqlite3Dataset.BuildLinkedList;
+var
+  TempItem:PDataRecord;
+  vm:Pointer;
+  Counter:Integer;
+begin
+  //Get AutoInc Field initial value
+  if FAutoIncFieldNo <> -1 then
+    sqlite3_exec(FSqliteHandle,PChar('Select Max('+Fields[FAutoIncFieldNo].FieldName+') from ' + FTableName),
+      @GetAutoIncValue,@FNextAutoInc,nil);
+
+  FSqliteReturnId:=sqlite3_prepare(FSqliteHandle,Pchar(FSql),-1,@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;
+  FRowCount:=sqlite3_column_count(vm);
+  FSqliteReturnId:=sqlite3_step(vm);
+  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(sqlite3_column_text(vm,Counter));
+    FSqliteReturnId:=sqlite3_step(vm);
+  end;
+  sqlite3_finalize(vm);
+
+  // 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 TSqlite3Dataset.TableExists: Boolean;
+var
+  AHandle,vm:Pointer;
+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:=sqlite3_prepare(AHandle,
+    Pchar('SELECT name FROM SQLITE_MASTER WHERE type = ''table'' AND name LIKE '''+ FTableName+ ''';'),
+      -1,@vm,nil);
+    {$ifdef DEBUG}
+    WriteLn('TableExists.sqlite3_prepare - SqliteReturnString:',SqliteReturnString);
+    {$endif}
+    FSqliteReturnId:=sqlite3_step(vm);
+    {$ifdef DEBUG}
+    WriteLn('TableExists.sqlite3_step - SqliteReturnString:',SqliteReturnString);
+    {$endif}
+    Result:=FSqliteReturnId = SQLITE_ROW;
+    sqlite3_finalize(vm);
+    if (FSqliteHandle = nil) then
+      sqlite3_close(AHandle);
+  end;
+  {$ifdef DEBUG}
+  WriteLn('TableExists ('+FTableName+') Result:',Result);
+  {$endif}
+end;
+
+function TSqlite3Dataset.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_NOTADB       : Result := 'SQLITE_NOTADB      ';
+      SQLITE_DONE         : Result := 'SQLITE_DONE        ';
+  else
+    Result:='Unknow Return Value';
+ end;
+end;
+
+
+end.
+

文件差异内容过多而无法显示
+ 71 - 1034
fcl/db/sqlite/sqliteds.pas


部分文件因为文件数量过多而无法显示