Browse Source

* Patch from Luiz Americo:
- Allow more than one field to be used in Locate (fix bug #7843)
- Properly implement Edit, Insert, Cancel
- Simplified TableExists
- Make RecNo follows Delphi/sqldb (1 based count)

git-svn-id: trunk@5711 -

michael 18 years ago
parent
commit
edc1015a49
3 changed files with 200 additions and 295 deletions
  1. 200 98
      fcl/db/sqlite/customsqliteds.pas
  2. 0 77
      fcl/db/sqlite/sqlite3ds.pas
  3. 0 120
      fcl/db/sqlite/sqliteds.pas

+ 200 - 98
fcl/db/sqlite/customsqliteds.pas

@@ -69,11 +69,11 @@ type
     {$else}
     {$else}
     FCurrentItem: PDataRecord;
     FCurrentItem: PDataRecord;
     {$endif}
     {$endif}
+    FInternalActiveBuffer: PDataRecord;
     FBufferSize: Integer;
     FBufferSize: Integer;
     FExpectedAppends: Integer;
     FExpectedAppends: Integer;
     FExpectedDeletes: Integer;
     FExpectedDeletes: Integer;
     FExpectedUpdates: Integer;
     FExpectedUpdates: Integer;
-    //FPersistentHandle: Boolean;
     FSaveOnClose: Boolean;
     FSaveOnClose: Boolean;
     FSaveOnRefetch: Boolean;
     FSaveOnRefetch: Boolean;
     FAutoIncrementKey: Boolean;
     FAutoIncrementKey: Boolean;
@@ -81,6 +81,7 @@ type
     FIndexFieldNames: String;
     FIndexFieldNames: String;
     FIndexFieldList: TList;
     FIndexFieldList: TList;
     FSqlList:TStrings;
     FSqlList:TStrings;
+    procedure CopyCacheToItem(AItem: PDataRecord);
     function GetIndexFields(Value: Integer): TField;
     function GetIndexFields(Value: Integer): TField;
     procedure UpdateIndexFields;
     procedure UpdateIndexFields;
     function FindRecordItem(StartItem: PDataRecord; const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions; DoResync:Boolean):PDataRecord;
     function FindRecordItem(StartItem: PDataRecord; const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions; DoResync:Boolean):PDataRecord;
@@ -135,7 +136,9 @@ type
     function GetRecordSize: Word; override; 
     function GetRecordSize: Word; override; 
     procedure InternalAddRecord(Buffer: Pointer; DoAppend: Boolean); override;
     procedure InternalAddRecord(Buffer: Pointer; DoAppend: Boolean); override;
     procedure InternalClose; override;
     procedure InternalClose; override;
+    procedure InternalCancel; override;
     procedure InternalDelete; override;
     procedure InternalDelete; override;
+    procedure InternalEdit; override;
     procedure InternalFirst; override;
     procedure InternalFirst; override;
     procedure InternalGotoBookmark(ABookmark: Pointer); override;
     procedure InternalGotoBookmark(ABookmark: Pointer); override;
     procedure InternalInitRecord(Buffer: PChar); override;
     procedure InternalInitRecord(Buffer: PChar); override;
@@ -173,8 +176,8 @@ type
     function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;virtual;abstract;overload;
     function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;virtual;abstract;overload;
     procedure RefetchData;
     procedure RefetchData;
     function SqliteReturnString: String; virtual;abstract;
     function SqliteReturnString: String; virtual;abstract;
-    function TableExists: Boolean;overload;
-    function TableExists(const ATableName:String):Boolean;virtual;abstract;overload;
+    function TableExists: Boolean;
+    function TableExists(const ATableName:String):Boolean;
     function UpdatesPending: Boolean;
     function UpdatesPending: Boolean;
     {$ifdef DEBUGACTIVEBUFFER}
     {$ifdef DEBUGACTIVEBUFFER}
     procedure SetCurrentItem(Value:PDataRecord);
     procedure SetCurrentItem(Value:PDataRecord);
@@ -192,7 +195,6 @@ type
     property ExpectedDeletes: Integer read FExpectedDeletes write SetExpectedDeletes;
     property ExpectedDeletes: Integer read FExpectedDeletes write SetExpectedDeletes;
     property IndexFields[Value: Integer]: TField read GetIndexFields;
     property IndexFields[Value: Integer]: TField read GetIndexFields;
     property RowsAffected: Integer read GetRowsAffected;
     property RowsAffected: Integer read GetRowsAffected;
-    //property PersistentHandle: boolean read FPersistentHandle write FPersistentHandle;
     property SqliteReturnId: Integer read FSqliteReturnId;
     property SqliteReturnId: Integer read FSqliteReturnId;
     property SqliteHandle: Pointer read FSqliteHandle;
     property SqliteHandle: Pointer read FSqliteHandle;
     property SqliteVersion: String read GetSqliteVersion;
     property SqliteVersion: String read GetSqliteVersion;
@@ -239,11 +241,13 @@ type
 implementation
 implementation
 
 
 uses
 uses
-  strutils, variants;
+  strutils, variants, dbconst;
 
 
 const
 const
-  SQLITE_OK = 0;//sqlite2.x.x and sqlite3.x.x defines this equal
-
+  //sqlite2.x.x and sqlite3.x.x define these constants equally
+  SQLITE_OK = 0;
+  SQLITE_ROW = 100;
+  
 function Num2SqlStr(APChar: PChar): String;
 function Num2SqlStr(APChar: PChar): String;
 begin
 begin
   if APChar = nil then
   if APChar = nil then
@@ -364,7 +368,6 @@ begin
   FEndItem^.Next:=nil;
   FEndItem^.Next:=nil;
   
   
   FBeginItem^.BookMarkFlag:=bfBOF;
   FBeginItem^.BookMarkFlag:=bfBOF;
-  FCacheItem^.BookMarkFlag:=bfEOF;
   FEndItem^.BookMarkFlag:=bfEOF;
   FEndItem^.BookMarkFlag:=bfEOF;
   
   
   FMasterLink:=TMasterDataLink.Create(Self);
   FMasterLink:=TMasterDataLink.Create(Self);
@@ -382,19 +385,19 @@ begin
 end;
 end;
 
 
 function TCustomSqliteDataset.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
 function TCustomSqliteDataset.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
-var
-  ActiveItem:PDataRecord;
 begin
 begin
   if Mode = bmWrite then
   if Mode = bmWrite then
   begin
   begin
-    ActiveItem:=PPDataRecord(ActiveBuffer)^;
-    if (ActiveItem <> FCacheItem) and (FUpdatedItems.IndexOf(ActiveItem) = -1) and (FAddedItems.IndexOf(ActiveItem) = -1) then
-      FUpdatedItems.Add(ActiveItem);
-    StrDispose(ActiveItem^.Row[Field.FieldNo - 1]);
-    ActiveItem^.Row[Field.FieldNo - 1]:=nil;
+    if not (State in [dsEdit, dsInsert]) then
+    begin
+      DatabaseErrorFmt(SNotInEditState,[Name],Self);
+      Exit;
+    end;
+    StrDispose(FCacheItem^.Row[Field.FieldNo - 1]);
+    FCacheItem^.Row[Field.FieldNo - 1]:=nil;
   end;
   end;
   Result:= TDSStream.Create(PPDataRecord(ActiveBuffer)^,Field.FieldNo - 1);
   Result:= TDSStream.Create(PPDataRecord(ActiveBuffer)^,Field.FieldNo - 1);
-end;  
+end;
 
 
 destructor TCustomSqliteDataset.Destroy;
 destructor TCustomSqliteDataset.Destroy;
 begin
 begin
@@ -414,6 +417,19 @@ begin
   Dispose(FEndItem);
   Dispose(FEndItem);
 end;
 end;
 
 
+procedure TCustomSqliteDataset.CopyCacheToItem(AItem: PDataRecord);
+var
+  i:Integer;
+begin
+  for i := 0 to FRowCount - 1 do
+  begin
+    StrDispose(AItem^.Row[i]);
+    AItem^.Row[i]:=FCacheItem^.Row[i];
+    FCacheItem^.Row[i]:=nil;
+  end;
+  AItem^.BookmarkFlag:=FCacheItem^.BookmarkFlag;
+end;
+
 function TCustomSqliteDataset.GetIndexFields(Value: Integer): TField;
 function TCustomSqliteDataset.GetIndexFields(Value: Integer): TField;
 begin
 begin
   if (Value < 0) or (Value > FIndexFieldList.Count - 1) then
   if (Value < 0) or (Value > FIndexFieldList.Count - 1) then
@@ -560,27 +576,30 @@ var
   TempItem,TempActive:PDataRecord;
   TempItem,TempActive:PDataRecord;
 begin
 begin
   Result:= -1;
   Result:= -1;
-  if FRecordCount = 0 then
+  if (FRecordCount = 0) or (State = dsInsert) then
     Exit;  
     Exit;  
   TempItem:=FBeginItem;
   TempItem:=FBeginItem;
   TempActive:=PPDataRecord(ActiveBuffer)^;
   TempActive:=PPDataRecord(ActiveBuffer)^;
-  if TempActive = FCacheItem then // Record not posted yet
-    Result:=FRecordCount 
-  else
-    while TempActive <> TempItem do
+  if TempActive = FCacheItem then // Record is being edited
+  begin
+    TempActive:=FInternalActiveBuffer;
+  end;
+  //RecNo is 1 based
+  inc(Result);
+  while TempActive <> TempItem do
+  begin
+    if TempItem^.Next <> nil then
     begin
     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;  
+      inc(Result);
+      TempItem:=TempItem^.Next;
+    end  
+    else
+    begin
+      Result:=-1;
+      DatabaseError('GetRecNo - ActiveItem Not Found',Self);
+      break;    
+    end;      
+  end;  
 end;
 end;
 
 
 function TCustomSqliteDataset.GetRecordSize: Word;
 function TCustomSqliteDataset.GetRecordSize: Word;
@@ -591,7 +610,6 @@ end;
 procedure TCustomSqliteDataset.InternalAddRecord(Buffer: Pointer; DoAppend: Boolean);
 procedure TCustomSqliteDataset.InternalAddRecord(Buffer: Pointer; DoAppend: Boolean);
 var
 var
   NewItem: PDataRecord;
   NewItem: PDataRecord;
-  Counter:Integer;
 begin
 begin
   {$ifdef DEBUG}
   {$ifdef DEBUG}
   if PPDataRecord(Buffer)^ <> FCacheItem then
   if PPDataRecord(Buffer)^ <> FCacheItem then
@@ -599,12 +617,20 @@ begin
   {$endif}
   {$endif}
   New(NewItem);
   New(NewItem);
   GetMem(NewItem^.Row,FRowBufferSize);
   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;
+  //necessary to nullify the Row before copy the cache
+  FillChar(NewItem^.Row^,FRowBufferSize,#0);
+  CopyCacheToItem(NewItem);
+
+  //insert in the linked list
+  FCurrentItem^.Previous^.Next:=NewItem;
+  NewItem^.Next:=FCurrentItem;
+  NewItem^.Previous:=FCurrentItem^.Previous;
+  FCurrentItem^.Previous:=NewItem;
+  
+  //update the cursor in case of a insert
+  if FCurrentItem <> FEndItem then
+    FCurrentItem:=NewItem;
+  
   Inc(FRecordCount);
   Inc(FRecordCount);
   if FAutoIncFieldNo <> - 1 then
   if FAutoIncFieldNo <> - 1 then
     Inc(FNextAutoInc);
     Inc(FNextAutoInc);
@@ -620,13 +646,6 @@ begin
     DestroyFields;
     DestroyFields;
   if FDataAllocated then
   if FDataAllocated then
     DisposeLinkedList;  
     DisposeLinkedList;  
-  {
-  if (FSqliteHandle <> nil) and not FPersistentHandle then
-  begin
-    InternalCloseHandle;
-    FSqliteHandle := nil;
-  end;
-  }
   FAddedItems.Clear;
   FAddedItems.Clear;
   FUpdatedItems.Clear;
   FUpdatedItems.Clear;
   FDeletedItems.Clear;
   FDeletedItems.Clear;
@@ -634,6 +653,19 @@ begin
   FRecordCount:=0;
   FRecordCount:=0;
 end;
 end;
 
 
+procedure TCustomSqliteDataset.InternalCancel;
+var
+  i: Integer;
+begin
+  PPDataRecord(ActiveBuffer)^:=FInternalActiveBuffer;
+  //free the cache
+  for i:= 0 to FRowCount - 1 do
+  begin
+    StrDispose(FCacheItem^.Row[i]);
+    FCacheItem^.Row[i]:=nil;
+  end;
+end;
+
 procedure TCustomSqliteDataset.InternalDelete;
 procedure TCustomSqliteDataset.InternalDelete;
 var
 var
   TempItem:PDataRecord;
   TempItem:PDataRecord;
@@ -669,6 +701,19 @@ begin
   end;    
   end;    
 end;
 end;
 
 
+procedure TCustomSqliteDataset.InternalEdit;
+var
+  i: Integer;
+begin
+  FInternalActiveBuffer:=PPDataRecord(ActiveBuffer)^;
+  //copy active item to cache
+  for i:= 0 to FRowCount - 1 do
+    FCacheItem^.Row[i]:=StrNew(FInternalActiveBuffer^.Row[i]);
+  FCacheItem^.BookmarkFlag:=FInternalActiveBuffer^.BookmarkFlag;
+  //now active buffer is the cache item
+  PPDataRecord(ActiveBuffer)^:=FCacheItem;
+end;
+
 procedure TCustomSqliteDataset.InternalFirst;
 procedure TCustomSqliteDataset.InternalFirst;
 begin
 begin
   FCurrentItem := FBeginItem;
   FCurrentItem := FBeginItem;
@@ -681,14 +726,8 @@ end;
 
 
 procedure TCustomSqliteDataset.InternalInitRecord(Buffer: PChar);
 procedure TCustomSqliteDataset.InternalInitRecord(Buffer: PChar);
 var
 var
-  Counter:Integer;
   TempStr:String;  
   TempStr:String;  
 begin
 begin
-  for Counter:= 0 to FRowCount - 1 do
-  begin
-    StrDispose(FCacheItem^.Row[Counter]);
-    FCacheItem^.Row[Counter]:=nil;
-  end;
   if FAutoIncFieldNo <> - 1 then
   if FAutoIncFieldNo <> - 1 then
   begin
   begin
     Str(FNextAutoInc,TempStr);
     Str(FNextAutoInc,TempStr);
@@ -696,6 +735,7 @@ begin
     StrPCopy(FCacheItem^.Row[FAutoIncFieldNo],TempStr);
     StrPCopy(FCacheItem^.Row[FAutoIncFieldNo],TempStr);
   end;  
   end;  
   PPDataRecord(Buffer)^:=FCacheItem;    
   PPDataRecord(Buffer)^:=FCacheItem;    
+  FCacheItem^.BookmarkFlag:=bfInserted;
 end;
 end;
 
 
 procedure TCustomSqliteDataset.InternalLast;
 procedure TCustomSqliteDataset.InternalLast;
@@ -732,7 +772,6 @@ begin
     FSelectSqlStr:=FSelectSqlStr+FieldDefs[i].Name+',';
     FSelectSqlStr:=FSelectSqlStr+FieldDefs[i].Name+',';
   FSelectSqlStr:=FSelectSqlStr+FieldDefs[FieldDefs.Count - 1].Name+
   FSelectSqlStr:=FSelectSqlStr+FieldDefs[FieldDefs.Count - 1].Name+
     ' FROM '+FTableName;
     ' FROM '+FTableName;
-  //writeln(FSelectSqlStr);
 
 
   if DefaultFields then 
   if DefaultFields then 
     CreateFields;
     CreateFields;
@@ -759,8 +798,16 @@ end;
 
 
 procedure TCustomSqliteDataset.InternalPost;
 procedure TCustomSqliteDataset.InternalPost;
 begin
 begin
-  if (State<>dsEdit) then 
-    InternalAddRecord(ActiveBuffer,True);
+  if State <> dsEdit then
+    InternalAddRecord(ActiveBuffer,True)
+  else
+  begin
+    CopyCacheToItem(FInternalActiveBuffer);
+    PPDataRecord(ActiveBuffer)^:=FInternalActiveBuffer;
+    if (FUpdatedItems.IndexOf(FInternalActiveBuffer) = -1) and
+      (FAddedItems.IndexOf(FInternalActiveBuffer) = -1) then
+      FUpdatedItems.Add(FInternalActiveBuffer);
+  end;
 end;
 end;
 
 
 procedure TCustomSqliteDataset.InternalSetToRecord(Buffer: PChar);
 procedure TCustomSqliteDataset.InternalSetToRecord(Buffer: PChar);
@@ -775,23 +822,59 @@ end;
 
 
 function TCustomSqliteDataset.FindRecordItem(StartItem: PDataRecord; const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions; DoResync:Boolean):PDataRecord;
 function TCustomSqliteDataset.FindRecordItem(StartItem: PDataRecord; const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions; DoResync:Boolean):PDataRecord;
 var
 var
+  ValueArray: array of string;
+  IndexArray: array of integer;
+  AFieldList: TList;
+  i,AFieldCount:Integer;
+  MatchRecord: Boolean;
   AValue:String;
   AValue:String;
-  AField:TField;
-  AFieldIndex:Integer;
   TempItem:PDataRecord;
   TempItem:PDataRecord;
 begin
 begin
   Result:=nil;
   Result:=nil;
-  // Now, it allows to search only one field and ignores options 
-  AField:=Fields.FieldByName(KeyFields); //FieldByName raises an exception if field not found
-  AFieldIndex:=AField.FieldNo - 1;  
-  //get float types in appropriate format
-  if not (AField.DataType in [ftFloat,ftDateTime,ftTime,ftDate]) then
-    AValue:=keyvalues
-  else
-  begin
-    Str(VarToDateTime(keyvalues),AValue);
-    AValue:=Trim(AValue);
-  end;  
+  // Currently ignore options
+  AFieldList:=TList.Create;
+  try
+    GetFieldList(AFieldList,KeyFields);
+    AFieldCount:=AFieldList.Count;
+    if AFieldCount > 1 then
+    begin
+      if VarIsArray(KeyValues) then
+      begin
+        if Succ(VarArrayHighBound(KeyValues,1)) <> AFieldCount then
+          DatabaseError('Number of fields does not correspond to number of values',Self);
+      end
+      else
+        DatabaseError('Wrong number of values specified: expected an array of variants got a variant',Self);
+    end;
+    
+    //set the array of values and indexes
+
+    SetLength(ValueArray,AFieldCount);
+    SetLength(IndexArray,AFieldCount);
+    for i:= 0 to AFieldCount - 1 do
+      with TField(AFieldList[i]) do
+      begin
+        //get float types in appropriate format
+        if not (DataType in [ftFloat,ftDateTime,ftTime,ftDate]) then
+        begin
+          if VarIsArray(KeyValues) then
+            ValueArray[i]:=keyvalues[i]
+          else
+            ValueArray[i]:=keyvalues;
+        end
+        else
+        begin
+          if VarIsArray(KeyValues) then
+            Str(VarToDateTime(keyvalues[i]),AValue)
+          else
+            Str(VarToDateTime(keyvalues),AValue);
+          ValueArray[i]:=Trim(AValue);
+        end;
+        IndexArray[i]:=FieldNo - 1;
+      end;
+  finally
+    AFieldList.Destroy;
+  end;
   {$ifdef DEBUG}
   {$ifdef DEBUG}
   writeln('##TCustomSqliteDataset.FindRecordItem##');
   writeln('##TCustomSqliteDataset.FindRecordItem##');
   writeln('  KeyFields: ',keyfields);
   writeln('  KeyFields: ',keyfields);
@@ -802,19 +885,27 @@ begin
   TempItem:=StartItem;
   TempItem:=StartItem;
   while TempItem <> FEndItem do
   while TempItem <> FEndItem do
   begin
   begin
-    if TempItem^.Row[AFieldIndex] <> nil then
+    MatchRecord:=True;
+    for i:= 0 to AFieldCount - 1 do
     begin
     begin
-      if StrComp(TempItem^.Row[AFieldIndex],PChar(AValue)) = 0 then
+      //todo: handle null values??
+      if (TempItem^.Row[IndexArray[i]] = nil) or
+        (StrComp(TempItem^.Row[IndexArray[i]],PChar(ValueArray[i])) <> 0) then
       begin
       begin
-        Result:=TempItem;
-        if DoResync then
-        begin
-          FCurrentItem:=TempItem;
-          Resync([]);
-        end;  
-        Break;
+        MatchRecord:= False;
+        Break;//for
       end;
       end;
-    end;    
+    end;
+    if MatchRecord then
+    begin
+      Result:=TempItem;
+      if DoResync then
+      begin
+        FCurrentItem:=TempItem;
+        Resync([]);
+      end;
+      Break;//while
+    end;
     TempItem:=TempItem^.Next;
     TempItem:=TempItem^.Next;
   end;      
   end;      
 end;
 end;
@@ -850,7 +941,7 @@ end;
 
 
 procedure TCustomSqliteDataset.SetBookmarkData(Buffer: PChar; Data: Pointer);
 procedure TCustomSqliteDataset.SetBookmarkData(Buffer: PChar; Data: Pointer);
 begin
 begin
-  //The BookMarkData is the Buffer itself;
+  //The BookMarkData is the Buffer itself: no need to set nothing;
 end;
 end;
 
 
 procedure TCustomSqliteDataset.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
 procedure TCustomSqliteDataset.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
@@ -878,50 +969,50 @@ procedure TCustomSqliteDataset.SetFieldData(Field: TField; Buffer: Pointer;
   NativeFormat: Boolean);
   NativeFormat: Boolean);
 var
 var
   TempStr:String;
   TempStr:String;
-  ActiveItem:PDataRecord;
 begin
 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 not (State in [dsEdit, dsInsert]) then
+  begin
+    DatabaseErrorFmt(SNotInEditState,[Name],Self);
+    Exit;
+  end;
+  StrDispose(FCacheItem^.Row[Pred(Field.FieldNo)]);
   if Buffer <> nil then
   if Buffer <> nil then
   begin
   begin
     case Field.Datatype of
     case Field.Datatype of
     ftString:
     ftString:
       begin            
       begin            
-        ActiveItem^.Row[Pred(Field.FieldNo)]:=StrNew(PChar(Buffer));
+        FCacheItem^.Row[Pred(Field.FieldNo)]:=StrNew(PChar(Buffer));
       end;
       end;
     ftInteger:
     ftInteger:
       begin          
       begin          
         Str(LongInt(Buffer^),TempStr);  
         Str(LongInt(Buffer^),TempStr);  
-        ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
-        Move(PChar(TempStr)^,(ActiveItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr)+1);
+        FCacheItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
+        Move(PChar(TempStr)^,(FCacheItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr)+1);
       end;
       end;
     ftBoolean,ftWord:
     ftBoolean,ftWord:
       begin
       begin
         Str(Word(Buffer^),TempStr);  
         Str(Word(Buffer^),TempStr);  
-        ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
-        Move(PChar(TempStr)^,(ActiveItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr)+1);
+        FCacheItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
+        Move(PChar(TempStr)^,(FCacheItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr)+1);
       end;  
       end;  
     ftFloat,ftDateTime,ftDate,ftTime,ftCurrency:
     ftFloat,ftDateTime,ftDate,ftTime,ftCurrency:
       begin
       begin
         Str(Double(Buffer^),TempStr);  
         Str(Double(Buffer^),TempStr);  
-        ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr));
+        FCacheItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr));
         //Skips the first space that str returns
         //Skips the first space that str returns
         //todo: make a custom Str?
         //todo: make a custom Str?
-        Move((PChar(TempStr)+1)^,(ActiveItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr));
+        Move((PChar(TempStr)+1)^,(FCacheItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr));
       end;
       end;
     ftLargeInt:
     ftLargeInt:
       begin
       begin
         Str(Int64(Buffer^),TempStr);  
         Str(Int64(Buffer^),TempStr);  
-        ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
-        Move(PChar(TempStr)^,(ActiveItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr)+1);
+        FCacheItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
+        Move(PChar(TempStr)^,(FCacheItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr)+1);
       end;        
       end;        
     end;// case
     end;// case
   end//if
   end//if
   else
   else
-    ActiveItem^.Row[Pred(Field.FieldNo)]:=nil;        
+    FCacheItem^.Row[Pred(Field.FieldNo)]:=nil;
   if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
   if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
     DataEvent(deFieldChange, Ptrint(Field));  
     DataEvent(deFieldChange, Ptrint(Field));  
 end;
 end;
@@ -936,10 +1027,11 @@ var
   Counter:Integer;
   Counter:Integer;
   TempItem:PDataRecord;
   TempItem:PDataRecord;
 begin
 begin
-  if (Value >= FRecordCount) or (Value < 0) then
+  if (Value > FRecordCount) or (Value <= 0) then
     DatabaseError('Record Number Out Of Range',Self);
     DatabaseError('Record Number Out Of Range',Self);
+  CheckBrowseMode;
   TempItem:=FBeginItem;
   TempItem:=FBeginItem;
-  for Counter := 0 to Value do
+  for Counter := 1 to Value do
     TempItem:=TempItem^.Next;
     TempItem:=TempItem^.Next;
   FCurrentItem:=TempItem;
   FCurrentItem:=TempItem;
   Resync([]);
   Resync([]);
@@ -1280,6 +1372,16 @@ begin
   Result:=TableExists(FTableName);
   Result:=TableExists(FTableName);
 end;
 end;
 
 
+function TCustomSqliteDataset.TableExists(const ATableName: String): Boolean;
+begin
+  Result:=False;
+  if not (ATableName = '') and FileExists(FFileName) then
+  begin
+    ExecSql('SELECT name FROM SQLITE_MASTER WHERE type = ''table'' AND name LIKE '''+ ATableName+ ''';');
+    Result:=FSqliteReturnId = SQLITE_ROW;
+  end;
+end;
+
 function TCustomSqliteDataset.UpdatesPending: Boolean;
 function TCustomSqliteDataset.UpdatesPending: Boolean;
 begin
 begin
   //Sometimes FBeginItem is inserted in FUpdatedItems
   //Sometimes FBeginItem is inserted in FUpdatedItems

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

@@ -41,13 +41,11 @@ type
     procedure InternalCloseHandle;override;
     procedure InternalCloseHandle;override;
     procedure BuildLinkedList; override;
     procedure BuildLinkedList; override;
   protected
   protected
-    procedure InternalCancel;override;
     procedure InternalInitFieldDefs; override;
     procedure InternalInitFieldDefs; override;
     function GetRowsAffected:Integer; override;
     function GetRowsAffected:Integer; override;
   public
   public
     procedure ExecuteDirect(const ASql: String);override;
     procedure ExecuteDirect(const ASql: String);override;
     function SqliteReturnString: String; override;
     function SqliteReturnString: String; override;
-    function TableExists(const ATableName:String): Boolean;override;
     function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;override;
     function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;override;
   end;
   end;
 
 
@@ -256,81 +254,6 @@ begin
     FBeginItem^.Row[Counter]:=nil;
     FBeginItem^.Row[Counter]:=nil;
 end;
 end;
 
 
-procedure TSqlite3Dataset.InternalCancel;
-{
-var
-  vm:Pointer;
-  i:Integer;
-  ActiveItem:PDataRecord;
-  ASql:String;
-}
-begin
-{
-  //WriteLn('InternalCancel called');
-  if FPrimaryKeyNo <> - 1 then //requires a primarykey
-  begin
-    ActiveItem:=PPDataRecord(ActiveBuffer)^;
-    if ActiveItem = FBeginItem then //Dataset is empty
-      Exit;
-    for i:= 0 to FRowCount -1 do
-      StrDispose(ActiveItem^.Row[i]);
-
-    if FAddedItems.IndexOf(ActiveItem) <> -1 then //the record is not in the database
-    begin
-      for i:= 0 to FRowCount - 1 do
-      begin
-        ActiveItem^.Row[i]:=nil;
-        //DataEvent(deFieldChange, Ptrint(Fields[i]));
-      end;
-      Exit;
-    end;
-    ASql:=FSelectSqlStr+' Where '+Fields[FPrimaryKeyNo].FieldName+
-      ' = '+StrPas(ActiveItem^.Row[FPrimaryKeyNo]);
-    //writeln(Asql);
-    sqlite3_prepare(FSqliteHandle,PChar(ASql),-1,@vm,nil);
-    if sqlite3_step(vm) = SQLITE_ROW then
-    begin
-      for i:= 0 to FRowCount - 1 do
-      begin
-        ActiveItem^.Row[i]:=StrNew(sqlite3_column_text(vm,i));
-        //DataEvent(deFieldChange, Ptrint(Fields[i]));
-      end;
-    end;
-    sqlite3_finalize(vm);
-  end;
-}
-end;
-
-function TSqlite3Dataset.TableExists(const ATableName:String): Boolean;
-var
-  vm:Pointer;
-begin
-  {$ifdef DEBUG}
-  writeln('##TSqlite3Dataset.TableExists##');
-  {$endif}
-  Result:=False;
-  if not (ATableName = '') and FileExists(FFileName) then
-  begin
-    if FSqliteHandle = nil then
-      GetSqliteHandle;
-    FSqliteReturnId:=sqlite3_prepare(FSqliteHandle,
-    Pchar('SELECT name FROM SQLITE_MASTER WHERE type = ''table'' AND name LIKE '''+ ATableName+ ''';'),
-      -1,@vm,nil);
-    {$ifdef DEBUG}
-    WriteLn('  sqlite3_prepare - SqliteReturnString:',SqliteReturnString);
-    {$endif}
-    FSqliteReturnId:=sqlite3_step(vm);
-    {$ifdef DEBUG}
-    WriteLn('  sqlite3_step - SqliteReturnString:',SqliteReturnString);
-    {$endif}
-    Result:=FSqliteReturnId = SQLITE_ROW;
-    sqlite3_finalize(vm);
-  end;
-  {$ifdef DEBUG}
-  WriteLn('  Table '+ATableName+' exists: ',Result);
-  {$endif}
-end;
-
 function TSqlite3Dataset.SqliteReturnString: String;
 function TSqlite3Dataset.SqliteReturnString: String;
 begin
 begin
  case FSqliteReturnId of
  case FSqliteReturnId of

+ 0 - 120
fcl/db/sqlite/sqliteds.pas

@@ -47,7 +47,6 @@ type
   public
   public
     procedure ExecuteDirect(const ASql: String);override;
     procedure ExecuteDirect(const ASql: String);override;
     function SqliteReturnString: String; override;
     function SqliteReturnString: String; override;
-    function TableExists(const ATableName:String): Boolean;override;
     function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;override;
     function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;override;
     property SqliteEncoding: String read GetSqliteEncoding;
     property SqliteEncoding: String read GetSqliteEncoding;
   end;
   end;
@@ -74,93 +73,6 @@ begin
   Result:=1;
   Result:=1;
 end;
 end;
 
 
-{
-function GetFieldDefs(TheDataset: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): integer; cdecl;
-var
-  FieldSize:Word;
-  i:Integer;
-  AType:TFieldType;
-  ColumnStr:String;
-begin
- //Prepare the array of pchar2sql functions
- SetLength(TCustomSqliteDataset(TheDataset).FGetSqlStr,Columns);
- // 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, may have problems
- for i:= 0 to Columns - 1 do
- begin
-   ColumnStr:= UpperCase(StrPas(ColumnNames[i + Columns]));
-   if (ColumnStr = 'INTEGER') or (ColumnStr = 'INT') then
-   begin
-     if TCustomSqliteDataset(TheDataset).AutoIncrementKey and
-          (UpperCase(StrPas(ColumnNames[i])) = UpperCase(TCustomSqliteDataset(TheDataset).PrimaryKey)) then
-     begin
-       AType:= ftAutoInc;
-       DummyAutoIncFieldNo:=i;
-     end
-     else
-       AType:= ftInteger;
-     FieldSize:=SizeOf(LongInt);
-   end else if Pos('VARCHAR',ColumnStr) = 1 then
-   begin
-     AType:= ftString;
-     FieldSize:=0;
-   end else if Pos('BOOL',ColumnStr) = 1 then
-   begin
-     AType:= ftBoolean;
-     FieldSize:=SizeOf(WordBool);
-   end else if Pos('AUTOINC',ColumnStr) = 1 then
-   begin
-     AType:= ftAutoInc;
-     FieldSize:=SizeOf(LongInt);
-     if DummyAutoIncFieldNo = -1 then
-       DummyAutoIncFieldNo:= i;
-   end else if (Pos('FLOAT',ColumnStr)=1) or (Pos('NUMERIC',ColumnStr)=1) then
-   begin
-     AType:= ftFloat;
-     FieldSize:=SizeOf(Double);
-   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 = 'TEXT') then
-   begin
-     AType:= ftMemo;
-     FieldSize:=0;
-   end else if (ColumnStr = 'CURRENCY') then
-   begin
-     AType:= ftCurrency;
-     FieldSize:=SizeOf(Double);
-   end else if (ColumnStr = 'WORD') then
-   begin
-     AType:= ftWord;
-     FieldSize:=SizeOf(Word);
-   end else
-   begin
-     AType:=ftString;
-     FieldSize:=0;
-   end;
-   TDataset(TheDataset).FieldDefs.Add(StrPas(ColumnNames[i]), AType, FieldSize, False);
-   //Set
- end;
- Result:=-1;
-end;
-}
-
 { TSqliteDataset }
 { TSqliteDataset }
 
 
 function TSqliteDataset.SqliteExec(AHandle: Pointer; ASql: PChar): Integer;
 function TSqliteDataset.SqliteExec(AHandle: Pointer; ASql: PChar): Integer;
@@ -349,38 +261,6 @@ begin
     FBeginItem^.Row[Counter]:=nil;
     FBeginItem^.Row[Counter]:=nil;
 end;
 end;
 
 
-function TSqliteDataset.TableExists(const ATableName:String): Boolean;
-var
-  vm:Pointer;
-  ColumnNames,ColumnValues:PPChar;
-  AInt:Integer;
-begin
-  {$ifdef DEBUG}
-  WriteLn('##TSqliteDataset.TableExists##');
-  {$endif}
-  Result:=False;
-  if not (ATableName = '') and FileExists(FFileName) then
-  begin
-    if FSqliteHandle = nil then
-      GetSqliteHandle;
-    FSqliteReturnId:=sqlite_compile(FSqliteHandle,
-      Pchar('SELECT name FROM SQLITE_MASTER WHERE type = ''table'' AND name LIKE '''+ ATableName+ ''';'),
-      nil,@vm,nil);
-    {$ifdef DEBUG}
-    WriteLn('  sqlite_compile - SqliteReturnString:',SqliteReturnString);
-    {$endif}
-    FSqliteReturnId:=sqlite_step(vm,@AInt,@ColumnValues,@ColumnNames);
-    {$ifdef DEBUG}
-    WriteLn('  sqlite_step - SqliteReturnString:',SqliteReturnString);
-    {$endif}
-    Result:=FSqliteReturnId = SQLITE_ROW;
-    sqlite_finalize(vm, nil);
-  end;
-  {$ifdef DEBUG}
-  WriteLn('  Table '+ATableName+' exists:',Result);
-  {$endif}
-end;
-
 function TSqliteDataset.SqliteReturnString: String;
 function TSqliteDataset.SqliteReturnString: String;
 begin
 begin
  case FSqliteReturnId of
  case FSqliteReturnId of