Browse Source

+ updated to latest version from Luiz Camara

git-svn-id: trunk@3061 -
joost 19 years ago
parent
commit
9b7d7cb21f
3 changed files with 537 additions and 403 deletions
  1. 259 183
      fcl/db/sqlite/customsqliteds.pas
  2. 163 112
      fcl/db/sqlite/sqlite3ds.pas
  3. 115 108
      fcl/db/sqlite/sqliteds.pas

+ 259 - 183
fcl/db/sqlite/customsqliteds.pas

@@ -62,9 +62,6 @@ type
 
   TCustomSqliteDataset = class(TDataSet)
   private
-    FPrimaryKey: String;
-    FPrimaryKeyNo: Integer;
-
     {$ifdef DEBUGACTIVEBUFFER}
     FFCurrentItem: PDataRecord;
     {$else}
@@ -74,25 +71,30 @@ type
     FExpectedAppends: Integer;
     FExpectedDeletes: Integer;
     FExpectedUpdates: Integer;
+    //FPersistentHandle: Boolean;
     FSaveOnClose: Boolean;
     FSaveOnRefetch: Boolean;
-    FSqlMode: Boolean;
-    FUpdatedItems: TFPList;
-    FAddedItems: TFPList;
-    FDeletedItems: TFPList;
-    FOrphanItems: TFPList;
+    FAutoIncrementKey: Boolean;
     FMasterLink: TMasterDataLink;
     FIndexFieldNames: String;
     FIndexFieldList: TList;
+    FSqlList:TStrings;
     function GetIndexFields(Value: Integer): TField;
     procedure UpdateIndexFields;
     function FindRecordItem(StartItem: PDataRecord; const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions; DoResync:Boolean):PDataRecord;
   protected
+    FPrimaryKey: String;
+    FPrimaryKeyNo: Integer;
     FFileName: String;
     FSql: String;
     FTableName: String;
+    FSelectSqlStr: String;
     FAutoIncFieldNo: Integer;
     FNextAutoInc:Integer;
+    FUpdatedItems: TFPList;
+    FAddedItems: TFPList;
+    FDeletedItems: TFPList;
+    FOrphanItems: TFPList;
     FSqliteReturnId: Integer;
     FSqliteHandle: Pointer;
     FDataAllocated: Boolean;
@@ -103,8 +105,9 @@ type
     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 InternalCloseHandle;virtual;abstract;
+    function InternalGetHandle: Pointer; virtual; abstract;
+    procedure GetSqliteHandle;
     function GetSqliteVersion: String; virtual; abstract;
     procedure BuildLinkedList; virtual; abstract;
     procedure DisposeLinkedList;
@@ -115,6 +118,8 @@ type
     function GetMasterFields:String;
     procedure SetMasterSource(Value: TDataSource);
     function GetMasterSource:TDataSource;
+    procedure SetFileName(Value: String);
+    function GetRowsAffected:Integer; virtual;abstract;
     //TDataSet overrides
     function AllocRecordBuffer: PChar; override;
     function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
@@ -141,26 +146,32 @@ type
     procedure SetExpectedAppends(AValue:Integer);
     procedure SetExpectedUpdates(AValue:Integer);
     procedure SetExpectedDeletes(AValue:Integer);
-    procedure SetFieldData(Field: TField; Buffer: Pointer); override;  
+    procedure SetFieldData(Field: TField; Buffer: Pointer); override;
+    procedure SetFieldData(Field: TField; Buffer: Pointer; NativeFormat: Boolean); override;
     procedure SetRecNo(Value: Integer); override;
   public
     constructor Create(AOwner: TComponent); override;
     destructor Destroy; override;
     function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
+    function GetFieldData(Field: TField; Buffer: Pointer; NativeFormat: Boolean): Boolean; override;
     function Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean; override;   
     function LocateNext(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean;
     function Lookup(const KeyFields: string; const KeyValues: Variant; const ResultFields: string): Variant;{$ifndef ver2_0_0}override;{$endif}
     // Additional procedures
-    function ApplyUpdates: Boolean; virtual;
-    function CreateTable: Boolean; virtual;
-    function ExecSQL:Integer;
-    function ExecSQL(const ASql:String):Integer;
+    function ApplyUpdates: Boolean;
+    function CreateTable: Boolean;
+    function CreateTable(const ATableName: String): Boolean;
+    procedure ExecSQL;
+    procedure ExecSQL(const ASql:String);
+    procedure ExecSQLList;
+    procedure ExecuteDirect(const ASql: String);virtual;abstract;
     function QuickQuery(const ASql:String):String;overload;
     function QuickQuery(const ASql:String;const AStrList: TStrings):String;overload;
     function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;virtual;abstract;overload;
     procedure RefetchData;
     function SqliteReturnString: String; virtual;abstract;
-    function TableExists: Boolean;virtual;abstract;
+    function TableExists: Boolean;overload;
+    function TableExists(const ATableName:String):Boolean;virtual;abstract;overload;
     function UpdatesPending: Boolean;
     {$ifdef DEBUGACTIVEBUFFER}
     procedure SetCurrentItem(Value:PDataRecord);
@@ -177,17 +188,20 @@ type
     property ExpectedUpdates: Integer read FExpectedUpdates write SetExpectedUpdates;
     property ExpectedDeletes: Integer read FExpectedDeletes write SetExpectedDeletes;
     property IndexFields[Value: Integer]: TField read GetIndexFields;
+    property RowsAffected: Integer read GetRowsAffected;
+    //property PersistentHandle: boolean read FPersistentHandle write FPersistentHandle;
     property SqliteReturnId: Integer read FSqliteReturnId;
     property SqliteHandle: Pointer read FSqliteHandle;
     property SqliteVersion: String read GetSqliteVersion;
+    property SQLList:TStrings read FSqlList;
    published
+    property AutoIncrementKey: Boolean read FAutoIncrementKey write FAutoIncrementKey;
     property IndexFieldNames: string read FIndexFieldNames write FIndexFieldNames;
-    property FileName: String read FFileName write FFileName;
+    property FileName: String read FFileName write SetFileName;
     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 SqlMode: Boolean read FSqlMode write FSqlMode;
     property TableName: String read FTableName write FTableName;   
     property MasterSource: TDataSource read GetMasterSource write SetMasterSource;
     property MasterFields: string read GetMasterFields write SetMasterFields;
@@ -255,31 +269,26 @@ 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;
+  //FRowSize is always 0 when FPosition = 0,
+  //so there's no need to check FPosition
+  NewRow:=StrAlloc(FRowSize+Count+1);
+  (NewRow+Count+FRowSize)^:=#0;
+  if FRowSize > 0 then
     Move(FFieldRow^,NewRow^,FRowSize);
-    Move(Buffer,(NewRow+FRowSize)^,Count);
-  end;
+  Move(Buffer,(NewRow+FRowSize)^,Count);
   FActiveItem^.Row[FFieldIndex]:=NewRow;    
   StrDispose(FFieldRow);
+  {$ifdef DEBUG}
+  WriteLn('##TDSStream.Write##');
+  WriteLn('  FPosition(Before): ',FPosition);
+  WriteLn('  FRowSize(Before): ',FRowSize);
+  WriteLn('  FPosition(After): ',FPosition+Count);
+  WriteLn('  FRowSize(After): ',StrLen(NewRow));
+  //WriteLn('  Stream Value: ',NewRow);
+  {$endif}
   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; 
@@ -294,11 +303,11 @@ begin
   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);
+  WriteLn('##TDSStream.Read##');
+  WriteLn('  Bytes requested: ',Count);
+  WriteLn('  Bytes moved: ',BytesToMove);
+  WriteLn('  Stream.Size: ',FRowSize);
+  //WriteLn('  Stream Value: ',FFieldRow);
   {$endif}
 end; 
  
@@ -334,30 +343,40 @@ begin
   BookmarkSize := SizeOf(Pointer);
   FBufferSize := SizeOf(PPDataRecord);
   FUpdatedItems:= TFPList.Create;
-  FUpdatedItems.Capacity:=20;
   FAddedItems:= TFPList.Create;
-  FAddedItems.Capacity:=20;
   FOrphanItems:= TFPList.Create;
-  FOrphanItems.Capacity:=20;
   FDeletedItems:= TFPList.Create;
-  FDeletedItems.Capacity:=20;
+  FSqlList:=TStringList.Create;
   inherited Create(AOwner);
 end;
 
 function TCustomSqliteDataset.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
+var
+  ActiveItem:PDataRecord;
 begin
+  if Mode = bmWrite then
+  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;
+  end;
   Result:= TDSStream.Create(PPDataRecord(ActiveBuffer)^,Field.FieldNo - 1);
 end;  
 
 destructor TCustomSqliteDataset.Destroy;
 begin
   inherited Destroy;
+  if FSqliteHandle <> nil then
+    InternalCloseHandle;
   FUpdatedItems.Destroy;
   FAddedItems.Destroy;
   FDeletedItems.Destroy;
   FOrphanItems.Destroy;
   FMasterLink.Destroy;
   FIndexFieldList.Destroy;
+  FSqlList.Destroy;
   // dispose special items
   Dispose(FBeginItem);
   Dispose(FCacheItem);
@@ -377,11 +396,14 @@ var
   Counter,I:Integer;
 begin
   //Todo: insert debug info
+  //Todo: see if FDataAllocated is still necessary
   FDataAllocated:=False;
   TempItem:=FBeginItem^.Next;
+  //Todo: see if is necessary to check if TempItem is nil (aparently is not)
   if TempItem <> nil then
     while TempItem^.Next <> nil do
     begin
+      //Todo: Add procedure to Dispose and Free a Row ?
       for Counter:= 0 to FRowCount - 1 do
         StrDispose(TempItem^.Row[Counter]);  
       FreeMem(TempItem^.Row,FRowBufferSize);
@@ -423,7 +445,8 @@ begin
   Result := PPDataRecord(Buffer)^^.BookmarkFlag;
 end;
 
-function TCustomSqliteDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
+function TCustomSqliteDataset.GetFieldData(Field: TField; Buffer: Pointer;
+  NativeFormat: Boolean): Boolean;
 var
   ValError:Word;
   FieldRow:PChar;
@@ -461,6 +484,11 @@ begin
   end;        
 end;
 
+function TCustomSqliteDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
+begin
+  Result:=GetFieldData(Field, Buffer, False);
+end;
+
 function TCustomSqliteDataset.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
 begin
   Result := grOk;
@@ -561,11 +589,13 @@ begin
     DestroyFields;
   if FDataAllocated then
     DisposeLinkedList;  
-  if FSqliteHandle <> nil then
+  {
+  if (FSqliteHandle <> nil) and not FPersistentHandle then
   begin
-    SqliteClose(FSqliteHandle);
+    InternalCloseHandle;
     FSqliteHandle := nil;
   end;
+  }
   FAddedItems.Clear;
   FUpdatedItems.Clear;
   FDeletedItems.Clear;
@@ -586,6 +616,9 @@ begin
   FUpdatedItems.Remove(TempItem);
   if FAddedItems.Remove(TempItem) = -1 then
     FDeletedItems.Add(TempItem);
+  //Todo: see if FOrphanItems is necessary:
+  //  in ApplyUpdates a check could be done
+  //  to avoid "delete" the AddedItems
   FOrphanItems.Add(TempItem);
   TempItem^.Next^.Previous:=TempItem^.Previous;
   TempItem^.Previous^.Next:=TempItem^.Next;
@@ -640,28 +673,38 @@ begin
 end;
 
 procedure TCustomSqliteDataset.InternalOpen;
+var
+  i:Integer;
 begin
-  FAutoIncFieldNo:=-1;
-  if not FileExists(FFileName) then
-    DatabaseError('File "'+ExpandFileName(FFileName)+'" not found',Self);
-  if (FTablename = '') and not (FSqlMode) then
-    DatabaseError('Tablename not set',Self);
-
   if MasterSource <> nil then
   begin
+    //todo: retrieve only necessary fields
     FSql := 'Select * from '+FTableName+';'; // forced to obtain all fields
     FMasterLink.FieldNames:=FMasterLink.FieldNames; //workaround to fill MasterLinks.Fields
     //if FMasterLink.Fields.Count = 0 MasterChanged will not be called anyway so ignore it
   end;
-  
-  FSqliteHandle:=GetSqliteHandle;
+
   if FSql = '' then
+  begin
+    if FTablename = '' then
+      DatabaseError('Tablename not set',Self);
     FSql := 'Select * from '+FTableName+';';
+  end;
+
+  if FSqliteHandle = nil then
+    GetSqliteHandle;
+    
   InternalInitFieldDefs;
+  //todo: move this to InitFieldDefs
+  FSelectSqlStr:='SELECT ';
+  for i:= 0 to FieldDefs.Count - 2 do
+    FSelectSqlStr:=FSelectSqlStr+FieldDefs[i].Name+',';
+  FSelectSqlStr:=FSelectSqlStr+FieldDefs[FieldDefs.Count - 1].Name+
+    ' FROM '+FTableName;
+  //writeln(FSelectSqlStr);
 
   if DefaultFields then 
     CreateFields;
-
   BindFields(True);
 
   UpdateIndexFields;
@@ -708,7 +751,7 @@ var
 begin
   Result:=nil;
   // Now, it allows to search only one field and ignores options 
-  AField:=Fields.FieldByName(KeyFields); //FieldByName raises an exeception if field not found
+  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
@@ -719,10 +762,10 @@ begin
     AValue:=Trim(AValue);
   end;  
   {$ifdef DEBUG}
-  writeln('=FindRecord=');
-  writeln('keyfields: ',keyfields);
-  writeln('keyvalues: ',keyvalues);
-  writeln('AValue: ',AValue);
+  writeln('##TCustomSqliteDataset.FindRecordItem##');
+  writeln('  KeyFields: ',keyfields);
+  writeln('  KeyValues: ',keyvalues);
+  writeln('  AValue: ',AValue);
   {$endif}        
   //Search the list
   TempItem:=StartItem;
@@ -745,6 +788,14 @@ begin
   end;      
 end;
 
+procedure TCustomSqliteDataset.GetSqliteHandle;
+begin
+  if FFileName = '' then
+    DatabaseError ('Filename not set',Self);
+  //todo:Handle opening non db files
+  FSqliteHandle:=InternalGetHandle;
+end;
+
 function TCustomSqliteDataset.Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean;
 begin
   Result:=FindRecordItem(FBeginItem^.Next,KeyFields,KeyValues,Options,True) <> nil;  
@@ -792,7 +843,8 @@ begin
   FOrphanItems.Capacity:=AValue;  
 end;  
 
-procedure TCustomSqliteDataset.SetFieldData(Field: TField; Buffer: Pointer);
+procedure TCustomSqliteDataset.SetFieldData(Field: TField; Buffer: Pointer;
+  NativeFormat: Boolean);
 var
   TempStr:String;
   ActiveItem:PDataRecord;
@@ -813,25 +865,27 @@ begin
       begin          
         Str(LongInt(Buffer^),TempStr);  
         ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
-        StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);
+        Move(PChar(TempStr)^,(ActiveItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr)+1);
       end;
     ftBoolean,ftWord:
       begin
         Str(Word(Buffer^),TempStr);  
         ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
-        StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);        
+        Move(PChar(TempStr)^,(ActiveItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr)+1);
       end;  
     ftFloat,ftDateTime,ftDate,ftTime,ftCurrency:
       begin
         Str(Double(Buffer^),TempStr);  
-        ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
-        StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);
+        ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr));
+        //Skips the first space that str returns
+        //todo: make a custom Str?
+        Move((PChar(TempStr)+1)^,(ActiveItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr));
       end;
     ftLargeInt:
       begin
         Str(Int64(Buffer^),TempStr);  
         ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
-        StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);
+        Move(PChar(TempStr)^,(ActiveItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr)+1);
       end;        
     end;// case
   end//if
@@ -841,6 +895,11 @@ begin
     DataEvent(deFieldChange, Ptrint(Field));  
 end;
 
+procedure TCustomSqliteDataset.SetFieldData(Field: TField; Buffer: Pointer);
+begin
+  SetFieldData(Field, Buffer, False);
+end;
+
 procedure TCustomSqliteDataset.SetRecNo(Value: Integer);
 var
   Counter:Integer;
@@ -858,7 +917,7 @@ end;
 // Specific functions 
 
 procedure TCustomSqliteDataset.SetDetailFilter;
-  function GetSqlStr(AField:TField):String;
+  function FieldToSqlStr(AField:TField):String;
   begin
     case AField.DataType of
       ftString,ftMemo: Result:='"'+AField.AsString+'"';//todo: handle " caracter properly
@@ -879,7 +938,7 @@ begin
     AFilter:=' where ';
     for i:= 0 to FMasterLink.Fields.Count - 1 do
     begin
-      AFilter:=AFilter + IndexFields[i].FieldName +' = '+ GetSqlStr(TField(FMasterLink.Fields[i]));
+      AFilter:=AFilter + IndexFields[i].FieldName +' = '+ FieldToSqlStr(TField(FMasterLink.Fields[i]));
       if i <> FMasterLink.Fields.Count - 1 then
         AFilter:= AFilter + ' and ';
     end;
@@ -891,8 +950,9 @@ procedure TCustomSqliteDataset.MasterChanged(Sender: TObject);
 begin
   SetDetailFilter;
   {$ifdef DEBUG}
-  writeln('Sql used to filter detail dataset:');
-  writeln(FSql);
+  writeln('##TCustomSqliteDataset.MasterChanged##');
+  writeln('  SQL used to filter detail dataset:');
+  writeln('  ',FSql);
   {$endif}
   RefetchData;
 end;
@@ -919,7 +979,6 @@ begin
   Result:=FMasterLink.FieldNames;
 end;
 
-
 procedure TCustomSqliteDataset.UpdateIndexFields;
 begin
   FIndexFieldList.Clear;
@@ -939,41 +998,62 @@ begin
   Result := FMasterLink.DataSource;
 end;
 
+procedure TCustomSqliteDataset.SetFileName(Value: String);
+begin
+  if Value <> FFileName then
+  begin
+    if Active then
+      DatabaseError('It''s not allowed to change Filename in an open dataset',Self);
+    if FSqliteHandle <> nil then
+      InternalCloseHandle;
+    FFileName:=Value;
+  end;
+end;
+
 procedure TCustomSqliteDataset.SetMasterSource(Value: TDataSource);
 begin
   FMasterLink.DataSource := Value;
 end;
 
+procedure TCustomSqliteDataset.ExecSQL(const ASql:String);
+begin
+  if FSqliteHandle = nil then
+    GetSqliteHandle;
+  ExecuteDirect(ASql);
+end;
 
-function TCustomSqliteDataset.ExecSQL(const ASql:String):Integer;
-var
-  AHandle: Pointer;
+procedure TCustomSqliteDataset.ExecSQLList;
 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',Self);    
-  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;    
+  if FSqliteHandle = nil then
+    GetSqliteHandle;
+  SqliteExec(FSqliteHandle,PChar(FSqlList.Text));
+end;
 
-function TCustomSqliteDataset.ExecSQL:Integer;
+procedure TCustomSqliteDataset.ExecSQL;
 begin
-  Result:=ExecSQL(FSql);  
+  ExecSQL(FSql);
 end;
 
+function GetSqlStr(IsString: boolean; APChar: PChar): String;
+begin
+  if APChar = nil then
+  begin
+    Result:='NULL';
+    Exit;
+  end;  
+  Result:=StrPas(APChar);
+  if IsString then
+  begin
+    if Pos('''',Result) > 0 then
+      Result:=AnsiReplaceStr(Result,'''','''''');
+    Result:=''''+Result+'''';    
+  end;  
+end;  
+
 function TCustomSqliteDataset.ApplyUpdates:Boolean;
 var
   CounterFields,CounterItems,StatementsCounter:Integer;
-  SqlTemp,KeyName,ASqlLine,TemplateStr:String;
+  SqlTemp,WhereKeyNameEqual,ASqlLine,TemplateStr:String;
 begin
   if not UpdatesPending then
   begin
@@ -981,49 +1061,59 @@ begin
     Exit;
   end;
   Result:=False;
-  if (FPrimaryKeyNo <> -1) and not FSqlMode then
+  if FPrimaryKeyNo <> -1 then
   begin
     StatementsCounter:=0;
-    KeyName:=Fields[FPrimaryKeyNo].FieldName;
+    WhereKeyNameEqual:=' WHERE '+Fields[FPrimaryKeyNo].FieldName+' = ';
     {$ifdef DEBUG}
-    WriteLn('ApplyUpdates called');
+    WriteLn('##TCustomSqliteDataset.ApplyUpdates##');
     if FPrimaryKeyNo = FAutoIncFieldNo then
-      WriteLn('Using an AutoInc field as primary key');
-    WriteLn('PrimaryKey: ',KeyName);
-    WriteLn('PrimaryKeyNo: ',FPrimaryKeyNo);
+      WriteLn('  Using an AutoInc field as primary key');
+    WriteLn('  PrimaryKey: ',WhereKeyNameEqual);
+    WriteLn('  PrimaryKeyNo: ',FPrimaryKeyNo);
     {$endif}
-    SqlTemp:='BEGIN TRANSACTION;';
+    SqlTemp:='BEGIN;';
+    // Delete Records
+    if FDeletedItems.Count > 0 then
+      TemplateStr:='DELETE FROM '+FTableName+WhereKeyNameEqual;
+    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+'COMMIT;';
+        FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
+        StatementsCounter:=0;
+        SqlTemp:='BEGIN;';
+      end;    
+    end;
     // 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
+      for CounterFields:= 0 to Fields.Count - 2 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,';  
+        ASqlLine:=ASqlLine + (Fields[CounterFields].FieldName +' = '+
+          GetSqlStr((Fields[CounterFields].DataType in [ftString,ftMemo]),
+            PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields])+',');        
       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])+';';
+        ASqlLine:=ASqlLine + (Fields[Fields.Count - 1].FieldName +' = '+
+          GetSqlStr((Fields[Fields.Count - 1].DataType in [ftString,ftMemo]),PDataRecord(FUpdatedItems[CounterItems])^.Row[Fields.Count - 1])+
+          WhereKeyNameEqual+StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[FPrimaryKeyNo])+';');
+      SqlTemp:=SqlTemp + ASqlLine;
       inc(StatementsCounter);
       //ApplyUpdates each 400 statements
       if StatementsCounter = 400 then
       begin
-        SqlTemp:=SqlTemp+'END TRANSACTION;';
+        SqlTemp:=SqlTemp+'COMMIT;';
         FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
         StatementsCounter:=0;
-        SqlTemp:='BEGIN TRANSACTION;';
+        SqlTemp:='BEGIN;';
       end;  
     end;
     // Add new records
@@ -1042,53 +1132,27 @@ begin
     for CounterItems:= 0 to FAddedItems.Count - 1 do  
     begin
       ASqlLine:=TemplateStr;
-      for CounterFields:= 0 to Fields.Count - 1 do
+      for CounterFields:= 0 to Fields.Count - 2 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+',';
+        ASqlLine:=ASqlLine + (GetSqlStr((Fields[CounterFields].DataType in [ftString,ftMemo]),
+          PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields])+',');        
       end;
-      SqlTemp:=SqlTemp+ASqlLine+');';    
+      ASqlLine:=ASqlLine + (GetSqlStr((Fields[Fields.Count -1].DataType in [ftString,ftMemo]),
+        PDataRecord(FAddedItems[CounterItems])^.Row[Fields.Count - 1])+');');
+      SqlTemp:=SqlTemp + ASqlLine;    
       inc(StatementsCounter);
       //ApplyUpdates each 400 statements
       if StatementsCounter = 400 then
       begin
-        SqlTemp:=SqlTemp+'END TRANSACTION;';
+        SqlTemp:=SqlTemp+'COMMIT;';
         FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
         StatementsCounter:=0;
-        SqlTemp:='BEGIN TRANSACTION;';
+        SqlTemp:='BEGIN;';
       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;';
+    SqlTemp:=SqlTemp+'COMMIT;';
     {$ifdef DEBUG}
-    writeln('ApplyUpdates Sql: ',SqlTemp);
+    writeln('  SQL: ',SqlTemp);
     {$endif}  
    FAddedItems.Clear;
    FUpdatedItems.Clear;
@@ -1097,37 +1161,45 @@ begin
    Result:= FSqliteReturnId = SQLITE_OK;
   end;  
   {$ifdef DEBUG}
-    writeln('ApplyUpdates Result: ',Result);
+    writeln('  Result: ',Result);
   {$endif}   
 end;    
 
 function TCustomSqliteDataset.CreateTable: Boolean;
+begin
+  Result:=CreateTable(FTableName);
+end;
+
+function TCustomSqliteDataset.CreateTable(const ATableName: String): Boolean;
 var
   SqlTemp:String;
-  Counter:Integer;
+  i:Integer;
 begin
   {$ifdef DEBUG}
-  if FTableName = '' then
-    WriteLn('CreateTable : TableName Not Set');
-  if FieldDefs.Count = 0 then 
-    WriteLn('CreateTable : FieldDefs Not Initialized');
+  writeln('##TCustomSqliteDataset.CreateTable##');
+  if ATableName = '' then
+    WriteLn('  TableName Not Set');
+  if FieldDefs.Count = 0 then
+    WriteLn('  FieldDefs Not Initialized');
   {$endif}
-  if (FTableName <> '') and (FieldDefs.Count > 0) then
+  if (ATableName <> '') and (FieldDefs.Count > 0) then
   begin
-    FSqliteHandle:= GetSqliteHandle;
-    SqlTemp:='CREATE TABLE '+FTableName+' (';
-    for Counter := 0 to FieldDefs.Count-1 do
+    if FSqliteHandle = nil then
+      GetSqliteHandle;
+    SqlTemp:='CREATE TABLE '+ATableName+' (';
+    for i := 0 to FieldDefs.Count-1 do
     begin
-      SqlTemp:=SqlTemp + FieldDefs[Counter].Name;
-      case FieldDefs[Counter].DataType of
+      //todo: add index to autoinc field
+      SqlTemp:=SqlTemp + FieldDefs[i].Name;
+      case FieldDefs[i].DataType of
         ftInteger:
           SqlTemp:=SqlTemp + ' INTEGER';
         ftString:
           SqlTemp:=SqlTemp + ' VARCHAR';
         ftBoolean:
-          SqlTemp:=SqlTemp + ' BOOLEAN';
+          SqlTemp:=SqlTemp + ' BOOL_INT';
         ftFloat:
-          SqlTemp:=SqlTemp + ' FLOAT';  
+          SqlTemp:=SqlTemp + ' FLOAT';
         ftWord:
           SqlTemp:=SqlTemp + ' WORD';
         ftDateTime:
@@ -1135,33 +1207,33 @@ begin
         ftDate:
           SqlTemp:=SqlTemp + ' DATE';
         ftTime:
-          SqlTemp:=SqlTemp + ' TIME'; 
+          SqlTemp:=SqlTemp + ' TIME';
         ftLargeInt:
           SqlTemp:=SqlTemp + ' LARGEINT';
         ftCurrency:
-          SqlTemp:=SqlTemp + ' CURRENCY';                     
+          SqlTemp:=SqlTemp + ' CURRENCY';
         ftAutoInc:
-          SqlTemp:=SqlTemp + ' AUTOINC'; 
+          SqlTemp:=SqlTemp + ' AUTOINC_INT';
         ftMemo:
-          SqlTemp:=SqlTemp + ' MEMO';   
+          SqlTemp:=SqlTemp + ' TEXT';
       else
-        DatabaseError('Field type "'+FieldTypeNames[FieldDefs[Counter].DataType]+'" not supported',Self);    
+        DatabaseError('Field type "'+FieldTypeNames[FieldDefs[i].DataType]+'" not supported',Self);
       end;
-      if Counter <>  FieldDefs.Count - 1 then
-        SqlTemp:=SqlTemp+ ' , ';   
+      if UpperCase(FieldDefs[i].Name) = UpperCase(FPrimaryKey) then
+        SqlTemp:=SqlTemp + ' PRIMARY KEY';
+      if i <> FieldDefs.Count - 1 then
+        SqlTemp:=SqlTemp+ ' , ';
     end;
     SqlTemp:=SqlTemp+');';
     {$ifdef DEBUG}
-    writeln('CreateTable Sql: ',SqlTemp);
-    {$endif}  
+    writeln('  SQL: ',SqlTemp);
+    {$endif}
     FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
     Result:= FSqliteReturnId = SQLITE_OK;
-    SqliteClose(FSqliteHandle);
-    FSqliteHandle:=nil;
   end
   else
-    Result:=False;  
-end; 
+    Result:=False;
+end;
 
 procedure TCustomSqliteDataset.RefetchData;
 var
@@ -1182,8 +1254,12 @@ begin
   for i := 0 to BufferCount - 1 do
     PPDataRecord(Buffers[i])^:=FBeginItem;
   Resync([]);
-end;  
+end;
 
+function TCustomSqliteDataset.TableExists: Boolean;
+begin
+  Result:=TableExists(FTableName);
+end;
 
 function TCustomSqliteDataset.UpdatesPending: Boolean;
 begin

+ 163 - 112
fcl/db/sqlite/sqlite3ds.pas

@@ -36,15 +36,18 @@ type
   TSqlite3Dataset = class (TCustomSqliteDataset)
   private
     function SqliteExec(AHandle: Pointer; ASql:PChar):Integer;override;
-    function GetSqliteHandle: Pointer; override;
+    function InternalGetHandle: Pointer; override;
     function GetSqliteVersion: String; override;
-    procedure SqliteClose(AHandle: Pointer);override;
+    procedure InternalCloseHandle;override;
     procedure BuildLinkedList; override;
   protected
+    procedure InternalCancel;override;
     procedure InternalInitFieldDefs; override;
+    function GetRowsAffected:Integer; override;
   public
+    procedure ExecuteDirect(const ASql: String);override;
     function SqliteReturnString: String; override;
-    function TableExists: Boolean;override;
+    function TableExists(const ATableName:String): Boolean;override;
     function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;override;
   end;
 
@@ -75,14 +78,15 @@ begin
   Result:=sqlite3_exec(AHandle, ASql, nil, nil, nil);
 end;
 
-procedure TSqlite3Dataset.SqliteClose(AHandle: Pointer);
+procedure TSqlite3Dataset.InternalCloseHandle;
 begin
-  sqlite3_close(AHandle);
+  sqlite3_close(FSqliteHandle);
+  FSqliteHandle:=nil;
   //todo:handle return data
 end;
 
 
-function TSqlite3Dataset.GetSqliteHandle: Pointer;
+function TSqlite3Dataset.InternalGetHandle: Pointer;
 begin
   FSqliteReturnId:=sqlite3_open(PChar(FFileName),@Result);
 end;
@@ -91,35 +95,47 @@ procedure TSqlite3Dataset.InternalInitFieldDefs;
 var
   vm:Pointer;
   ColumnStr:String;
-  Counter,FieldSize:Integer;
+  i,FieldSize:Integer;
   AType:TFieldType;
 begin
+  {$ifdef DEBUG}
+  WriteLn('##TSqlite3Dataset.InternalInitFieldDefs##');
+  {$endif}
+  FAutoIncFieldNo:=-1;
   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
+  sqlite3_step(vm);
+  for i:= 0 to sqlite3_column_count(vm) - 1 do
+  begin
+   ColumnStr:= UpperCase(StrPas(sqlite3_column_decltype(vm,i)));
+   if (ColumnStr = 'INTEGER') or (ColumnStr = 'INT') then
    begin
-     AType:= ftInteger;
+     if AutoIncrementKey and (UpperCase(StrPas(sqlite3_column_name(vm,i))) = UpperCase(PrimaryKey)) then
+     begin
+       AType:= ftAutoInc;
+       FAutoIncFieldNo:=i;
+     end
+     else
+       AType:= ftInteger;
      FieldSize:=SizeOf(LongInt);
-   end else if (ColumnStr = 'VARCHAR') then
+   end else if Pos('VARCHAR',ColumnStr) = 1 then
    begin
      AType:= ftString;
-     FieldSize:=10;//??
-   end else if (ColumnStr = 'BOOLEAN') then
+     FieldSize:=0;
+   end else if Pos('BOOL',ColumnStr) = 1 then
    begin
      AType:= ftBoolean;
-     FieldSize:=SizeOf(Boolean);
-   end else if (ColumnStr = 'FLOAT') then
+     FieldSize:=SizeOf(WordBool);
+   end else if Pos('AUTOINC',ColumnStr) = 1 then
+   begin
+     AType:= ftAutoInc;
+     FieldSize:=SizeOf(LongInt);
+     if FAutoIncFieldNo = -1 then
+       FAutoIncFieldNo:= i;
+   end else if (Pos('FLOAT',ColumnStr)=1) or (Pos('NUMERIC',ColumnStr)=1) 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;
@@ -132,41 +148,55 @@ begin
    begin
      AType:= ftLargeInt;
      FieldSize:=SizeOf(Int64);
-   end else if (ColumnStr = 'CURRENCY') then
-   begin
-     AType:= ftCurrency;
-     FieldSize:=SizeOf(Double);
    end else if (ColumnStr = 'TIME') then
    begin
      AType:= ftTime;
      FieldSize:=SizeOf(TDateTime);
-   end else if (ColumnStr = 'MEMO') then
+   end else if (ColumnStr = 'TEXT') then
    begin
      AType:= ftMemo;
-     FieldSize:=10;//??
-   end else if (ColumnStr = 'AUTOINC') then
+     FieldSize:=0;
+   end else if (ColumnStr = 'CURRENCY') then
    begin
-     AType:= ftAutoInc;
-     FieldSize:=SizeOf(Integer);
-     if FAutoIncFieldNo = -1 then
-       FAutoIncFieldNo:= Counter;
+     AType:= ftCurrency;
+     FieldSize:=SizeOf(Double);
+   end else if (ColumnStr = 'WORD') then
+   begin
+     AType:= ftWord;
+     FieldSize:=SizeOf(Word);
    end else
    begin
      DatabaseError('Field type "'+ColumnStr+'" not recognized',Self);
    end;
-   FieldDefs.Add(StrPas(sqlite3_column_name(vm,Counter)), AType, FieldSize, False);
+   FieldDefs.Add(StrPas(sqlite3_column_name(vm,i)), AType, FieldSize, False);
    {$ifdef DEBUG}
-   writeln('Field Name: ',sqlite3_column_name(vm,Counter));
-   writeln('Field Type: ',sqlite3_column_decltype(vm,Counter));
+   writeln('  Field[',i,'] Name: ',sqlite3_column_name(vm,i));
+   writeln('  Field[',i,'] Type: ',sqlite3_column_decltype(vm,i));
    {$endif}
   end;
-	sqlite3_finalize(vm);
+  sqlite3_finalize(vm);
   FRowBufferSize:=(SizeOf(PPChar)*FieldDefs.Count);
   {$ifdef DEBUG}
-  writeln('FieldDefs.Count: ',FieldDefs.Count);
+  writeln('  FieldDefs.Count: ',FieldDefs.Count);
   {$endif}
 end;
 
+function TSqlite3Dataset.GetRowsAffected: Integer;
+begin
+  Result:=sqlite3_changes(FSqliteHandle);
+end;
+
+procedure TSqlite3Dataset.ExecuteDirect(const ASql: String);
+var
+  vm:Pointer;
+begin
+  FSqliteReturnId:=sqlite3_prepare(FSqliteHandle,Pchar(ASql),-1,@vm,nil);
+  if FSqliteReturnId <> SQLITE_OK then
+    DatabaseError(SqliteReturnString,Self);
+  FSqliteReturnId:=sqlite3_step(vm);
+  sqlite3_finalize(vm);
+end;
+
 procedure TSqlite3Dataset.BuildLinkedList;
 var
   TempItem:PDataRecord;
@@ -180,12 +210,7 @@ begin
 
   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;
+    DatabaseError(SqliteReturnString,Self);
 
   FDataAllocated:=True;
 
@@ -200,7 +225,7 @@ begin
     TempItem^.Next^.Previous:=TempItem;
     TempItem:=TempItem^.Next;
     GetMem(TempItem^.Row,FRowBufferSize);
-    For Counter := 0 to FRowCount - 1 do
+    for Counter := 0 to FRowCount - 1 do
       TempItem^.Row[Counter]:=StrNew(sqlite3_column_text(vm,Counter));
     FSqliteReturnId:=sqlite3_step(vm);
   end;
@@ -210,92 +235,128 @@ begin
   TempItem^.Next:=FEndItem;
   FEndItem^.Previous:=TempItem;
 
-  // Alloc item used in append/insert
+  // Alloc temporary 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);
+  //Todo: see if is better to nullif using FillDWord
   for Counter := 0 to FRowCount - 1 do
     FBeginItem^.Row[Counter]:=nil;
 end;
 
-function TSqlite3Dataset.TableExists: Boolean;
+procedure TSqlite3Dataset.InternalCancel;
+{
 var
-  AHandle,vm:Pointer;
+  vm:Pointer;
+  i:Integer;
+  ActiveItem:PDataRecord;
+  ASql:String;
+}
 begin
-  Result:=False;
-  if not (FTableName = '') and FileExists(FFileName) then
+{
+  //WriteLn('InternalCancel called');
+  if FPrimaryKeyNo <> - 1 then //requires a primarykey
   begin
-    if FSqliteHandle = nil then
+    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
-      {$ifdef DEBUG}
-      writeln('TableExists - FSqliteHandle=nil : Opening a file');
-      {$endif}
-      AHandle:=GetSqliteHandle;
-    end
-    else
+      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
-      {$ifdef DEBUG}
-      writeln('TableExists - FSqliteHandle<>nil : Using FSqliteHandle');
-      {$endif}
-      AHandle:=FSqliteHandle;
+      for i:= 0 to FRowCount - 1 do
+      begin
+        ActiveItem^.Row[i]:=StrNew(sqlite3_column_text(vm,i));
+        //DataEvent(deFieldChange, Ptrint(Fields[i]));
+      end;
     end;
-    FSqliteReturnId:=sqlite3_prepare(AHandle,
-    Pchar('SELECT name FROM SQLITE_MASTER WHERE type = ''table'' AND name LIKE '''+ FTableName+ ''';'),
+    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('TableExists.sqlite3_prepare - SqliteReturnString:',SqliteReturnString);
+    WriteLn('  sqlite3_prepare - SqliteReturnString:',SqliteReturnString);
     {$endif}
     FSqliteReturnId:=sqlite3_step(vm);
     {$ifdef DEBUG}
-    WriteLn('TableExists.sqlite3_step - SqliteReturnString:',SqliteReturnString);
+    WriteLn('  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);
+  WriteLn('  Table '+ATableName+' exists: ',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        ';
+      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;
+ Result:=Result+' - '+sqlite3_errmsg(FSqliteHandle);
 end;
 
 function TSqlite3Dataset.GetSqliteVersion: String;
@@ -305,7 +366,7 @@ end;
 
 function TSqlite3Dataset.QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;
 var
-  vm,AHandle:Pointer;
+  vm:Pointer;
     
   procedure FillStrings;
   begin
@@ -324,20 +385,12 @@ var
     end;
   end;    
 begin
-  if FSqliteHandle <> nil then
-    AHandle:=FSqliteHandle
-  else
-    if FileExists(FFileName) then
-      AHandle:=GetSqliteHandle
-    else
-      DatabaseError('File "'+FFileName+'" not Exists',Self);    
+  if FSqliteHandle = nil then
+    GetSqliteHandle;
   Result:='';
-  // It's up to the caller clear or not the list
-  //if AStrList <> nil then
-  //  AStrList.Clear;
-  FSqliteReturnId:=sqlite3_prepare(AHandle,Pchar(ASql),-1,@vm,nil);
+  FSqliteReturnId:=sqlite3_prepare(FSqliteHandle,Pchar(ASql),-1,@vm,nil);
   if FSqliteReturnId <> SQLITE_OK then
-    DatabaseError('Error returned by sqlite in QuickQuery: '+SqliteReturnString,Self);
+    DatabaseError(SqliteReturnString,Self);
     
   FSqliteReturnId:=sqlite3_step(vm);
   if (FSqliteReturnId = SQLITE_ROW) and (sqlite3_column_count(vm) > 0) then
@@ -352,8 +405,6 @@ begin
     end;          
   end;  
   sqlite3_finalize(vm); 
-  if FSqliteHandle = nil then
-    sqlite3_close(AHandle);
 end;
 
 end.

+ 115 - 108
fcl/db/sqlite/sqliteds.pas

@@ -36,16 +36,18 @@ type
   TSqliteDataset = class (TCustomSqliteDataset)
   private
     function SqliteExec(AHandle: Pointer; ASql:PChar):Integer;override;
-    function GetSqliteHandle: Pointer; override;
+    function InternalGetHandle: Pointer; override;
     function GetSqliteEncoding: String;
     function GetSqliteVersion: String; override;
-    procedure SqliteClose(AHandle: Pointer);override;
+    procedure InternalCloseHandle;override;
     procedure BuildLinkedList; override;
   protected
     procedure InternalInitFieldDefs; override;
+    function GetRowsAffected:Integer; override;
   public
+    procedure ExecuteDirect(const ASql: String);override;
     function SqliteReturnString: String; override;
-    function TableExists: Boolean;override;
+    function TableExists(const ATableName:String): Boolean;override;
     function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;override;
     property SqliteEncoding: String read GetSqliteEncoding;
   end;
@@ -58,6 +60,8 @@ uses
 var
   DummyAutoIncFieldNo:Integer;
 
+//function sqlite_last_statement_changes(dbhandle:Pointer):longint;cdecl;external 'sqlite' name 'sqlite_last_statement_changes';
+
 function GetAutoIncValue(NextValue: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): integer; cdecl;
 var
   CodeError, TempInt: Integer;
@@ -76,7 +80,7 @@ end;
 function GetFieldDefs(TheDataset: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): integer; cdecl;
 var
   FieldSize:Word;
-  Counter:Integer;
+  i:Integer;
   AType:TFieldType;
   ColumnStr:String;
 begin
@@ -84,30 +88,39 @@ begin
  // 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
+ // If the field contains another type, may have problems
+ for i:= 0 to Columns - 1 do
  begin
-   ColumnStr:= UpperCase(StrPas(ColumnNames[Counter + Columns]));
-   if (ColumnStr = 'INTEGER') then
+   ColumnStr:= UpperCase(StrPas(ColumnNames[i + Columns]));
+   if (ColumnStr = 'INTEGER') or (ColumnStr = 'INT') then
    begin
-     AType:= ftInteger;
+     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 (ColumnStr = 'VARCHAR') then
+   end else if Pos('VARCHAR',ColumnStr) = 1 then
    begin
      AType:= ftString;
-     FieldSize:=10;//??
-   end else if (ColumnStr = 'BOOLEAN') then
+     FieldSize:=0;
+   end else if Pos('BOOL',ColumnStr) = 1 then
    begin
      AType:= ftBoolean;
-     FieldSize:=SizeOf(Boolean);
-   end else if (ColumnStr = 'FLOAT') then
+     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 = 'WORD') then
-   begin
-     AType:= ftWord;
-     FieldSize:=SizeOf(Word);
    end else if (ColumnStr = 'DATETIME') then
    begin
      AType:= ftDateTime;
@@ -124,27 +137,26 @@ begin
    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 = 'MEMO') then
-   begin
-     AType:= ftMemo;
-     FieldSize:=10;//??
-   end else if (ColumnStr = 'AUTOINC') then
+   end else if (ColumnStr = 'WORD') then
    begin
-     AType:= ftAutoInc;
-     FieldSize:=SizeOf(Integer);
-     if DummyAutoIncFieldNo = -1 then
-       DummyAutoIncFieldNo:= Counter;
+     AType:= ftWord;
+     FieldSize:=SizeOf(Word);
    end else
    begin
-     DatabaseError('Field type "'+ColumnStr+'" not recognized',TDataset(TheDataset));
+     AType:=ftString;
+     FieldSize:=0;
    end;
-   TDataset(TheDataset).FieldDefs.Add(StrPas(ColumnNames[Counter]), AType, FieldSize, False);
+   TDataset(TheDataset).FieldDefs.Add(StrPas(ColumnNames[i]), AType, FieldSize, False);
  end;
- result:=-1;
+ Result:=-1;
 end;
 
 
@@ -155,20 +167,19 @@ begin
   Result:=sqlite_exec(AHandle, ASql, nil, nil, nil);
 end;
 
-procedure TSqliteDataset.SqliteClose(AHandle: Pointer);
+procedure TSqliteDataset.InternalCloseHandle;
 begin
-  sqlite_close(AHandle);
+  sqlite_close(FSqliteHandle);
+  FSqliteHandle:=nil;
 end;
 
-
-function TSqliteDataset.GetSqliteHandle: Pointer;
+function TSqliteDataset.InternalGetHandle: 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;
@@ -181,6 +192,27 @@ begin
   FRowBufferSize:=(SizeOf(PPChar)*FieldDefs.Count);
 end;
 
+function TSqliteDataset.GetRowsAffected: Integer;
+begin
+  Result:=sqlite_changes(FSqliteHandle);
+  //Result:=sqlite_last_statement_changes(FSqliteHandle);
+end;
+
+procedure TSqliteDataset.ExecuteDirect(const ASql: String);
+var
+  vm:Pointer;
+  ColumnNames,ColumnValues:PPChar;
+  ColCount:Integer;
+begin
+  FSqliteReturnId:=sqlite_compile(FSqliteHandle,Pchar(ASql),nil,@vm,nil);
+  if FSqliteReturnId <> SQLITE_OK then
+    DatabaseError(SqliteReturnString,Self);
+
+  FSqliteReturnId:=sqlite_step(vm,@ColCount,@ColumnValues,@ColumnNames);
+
+  sqlite_finalize(vm, nil);
+end;
+
 procedure TSqliteDataset.BuildLinkedList;
 var
   TempItem:PDataRecord;
@@ -195,12 +227,7 @@ begin
 
   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;
+    DatabaseError(SqliteReturnString,Self);
 
   FDataAllocated:=True;
 
@@ -214,7 +241,7 @@ begin
     TempItem^.Next^.Previous:=TempItem;
     TempItem:=TempItem^.Next;
     GetMem(TempItem^.Row,FRowBufferSize);
-    For Counter := 0 to FRowCount - 1 do
+    for Counter := 0 to FRowCount - 1 do
       TempItem^.Row[Counter]:=StrNew(ColumnValues[Counter]);
     FSqliteReturnId:=sqlite_step(vm,@FRowCount,@ColumnValues,@ColumnNames);
   end;
@@ -234,83 +261,73 @@ begin
     FBeginItem^.Row[Counter]:=nil;
 end;
 
-function TSqliteDataset.TableExists: Boolean;
+function TSqliteDataset.TableExists(const ATableName:String): Boolean;
 var
-  AHandle,vm:Pointer;
+  vm:Pointer;
   ColumnNames,ColumnValues:PPChar;
   AInt:Integer;
 begin
+  {$ifdef DEBUG}
+  WriteLn('##TSqliteDataset.TableExists##');
+  {$endif}
   Result:=False;
-  if not (FTableName = '') and FileExists(FFileName) then
+  if not (ATableName = '') 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+ ''';'),
+      GetSqliteHandle;
+    FSqliteReturnId:=sqlite_compile(FSqliteHandle,
+      Pchar('SELECT name FROM SQLITE_MASTER WHERE type = ''table'' AND name LIKE '''+ ATableName+ ''';'),
       nil,@vm,nil);
     {$ifdef DEBUG}
-    WriteLn('TableExists.sqlite_compile - SqliteReturnString:',SqliteReturnString);
+    WriteLn('  sqlite_compile - SqliteReturnString:',SqliteReturnString);
     {$endif}
     FSqliteReturnId:=sqlite_step(vm,@AInt,@ColumnValues,@ColumnNames);
     {$ifdef DEBUG}
-    WriteLn('TableExists.sqlite_step - SqliteReturnString:',SqliteReturnString);
+    WriteLn('  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);
+  WriteLn('  Table '+ATableName+' exists:',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        ';
+      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          : begin Result := 'SQLITE_ROW - not an error'; Exit; end;
+      SQLITE_DONE         : begin Result := 'SQLITE_DONE - not an error'; Exit; end;
   else
     Result:='Unknow Return Value';
  end;
+ Result:=Result+' - '+sqlite_error_string(FSqliteReturnId);
 end;
 
 function TSqliteDataset.GetSqliteEncoding: String;
@@ -325,7 +342,7 @@ end;
 
 function TSqliteDataset.QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;
 var
-  vm,AHandle:Pointer;
+  vm:Pointer;
   ColumnNames,ColumnValues:PPChar;
   ColCount:Integer;
   
@@ -347,20 +364,12 @@ var
     end;
   end;    
 begin
-  if FSqliteHandle <> nil then
-    AHandle:=FSqliteHandle
-  else
-    if FileExists(FFileName) then
-      AHandle:=GetSqliteHandle
-    else
-      DatabaseError('File '+FFileName+' not Exists',Self);    
+  if FSqliteHandle = nil then
+    GetSqliteHandle;
   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);
+  FSqliteReturnId:=sqlite_compile(FSqliteHandle,Pchar(ASql),nil,@vm,nil);
   if FSqliteReturnId <> SQLITE_OK then
-    DatabaseError('Error returned by sqlite in QuickQuery: '+SqliteReturnString,Self);
+    DatabaseError(SqliteReturnString,Self);
     
   FSqliteReturnId:=sqlite_step(vm,@ColCount,@ColumnValues,@ColumnNames);
   if (FSqliteReturnId = SQLITE_ROW) and (ColCount > 0) then
@@ -375,8 +384,6 @@ begin
     end;          
   end;  
   sqlite_finalize(vm, nil); 
-  if FSqliteHandle = nil then
-    sqlite_close(AHandle);
 end;
     
 end.