* Patch from Luiz Américo:

- ExecSql now can be called in closed datasets
  - Changed name from IndexFieldName to PrimaryKey to avoid confunsion
    with TclientDataset.IndexFieldName (Delphi)
  - Small changes
This commit is contained in:
michael 2005-05-06 19:08:23 +00:00
parent d1e6934a5b
commit 11eefecc8c

View File

@ -25,6 +25,7 @@ unit sqliteds;
{$H+} {$H+}
{ $Define USE_SQLITEDS_INTERNALS} { $Define USE_SQLITEDS_INTERNALS}
{ $Define DEBUG} { $Define DEBUG}
{ $Define DEBUGACTIVEBUFFER}
interface interface
@ -66,11 +67,11 @@ type
FFileName: String; FFileName: String;
FSql: String; FSql: String;
FTableName: String; FTableName: String;
FIndexFieldName: String; FPrimaryKey: String;
FIndexFieldNo: Integer; FPrimaryKeyNo: Integer;
FAutoIncFieldNo: Integer; FAutoIncFieldNo: Integer;
FNextAutoInc:Integer; FNextAutoInc:Integer;
{$ifdef Debug} {$ifdef DEBUGACTIVEBUFFER}
FFCurrentItem: PDataRecord; FFCurrentItem: PDataRecord;
{$else} {$else}
FCurrentItem: PDataRecord; FCurrentItem: PDataRecord;
@ -141,7 +142,7 @@ type
procedure RefetchData; procedure RefetchData;
function SqliteReturnString: String; function SqliteReturnString: String;
function UpdatesPending: Boolean; function UpdatesPending: Boolean;
{$ifdef DEBUG} {$ifdef DEBUGACTIVEBUFFER}
procedure SetCurrentItem(Value:PDataRecord); procedure SetCurrentItem(Value:PDataRecord);
property FCurrentItem: PDataRecord read FFCurrentItem write SetCurrentItem; property FCurrentItem: PDataRecord read FFCurrentItem write SetCurrentItem;
{$endif} {$endif}
@ -159,7 +160,7 @@ type
property SqliteReturnId: Integer read FSqliteReturnId; property SqliteReturnId: Integer read FSqliteReturnId;
published published
property FileName: String read FFileName write FFileName; property FileName: String read FFileName write FFileName;
property IndexFieldName: String read FIndexFieldName write FIndexFieldName; property PrimaryKey: String read FPrimaryKey write FPrimaryKey;
property SaveOnClose: Boolean read FSaveOnClose write FSaveOnClose; property SaveOnClose: Boolean read FSaveOnClose write FSaveOnClose;
property SaveOnRefetch: Boolean read FSaveOnRefetch write FSaveOnRefetch; property SaveOnRefetch: Boolean read FSaveOnRefetch write FSaveOnRefetch;
property SQL: String read FSql write FSql; property SQL: String read FSql write FSql;
@ -378,9 +379,9 @@ begin
if FSqliteReturnId <> SQLITE_OK then if FSqliteReturnId <> SQLITE_OK then
case FSqliteReturnId of case FSqliteReturnId of
SQLITE_ERROR: SQLITE_ERROR:
DatabaseError('Invalid Sql',Self); DatabaseError('Invalid SQL',Self);
else else
DatabaseError('Unknow Error',Self); DatabaseError('Error returned by sqlite while retrieving data: '+SqliteReturnString,Self);
end; end;
FDataAllocated:=True; FDataAllocated:=True;
@ -417,11 +418,15 @@ begin
FBeginItem^.Next:=FEndItem; FBeginItem^.Next:=FEndItem;
end; end;
FEndItem^.Next:=nil; FEndItem^.Next:=nil;
// Alloc item used in append/insert // Alloc item used in append/insert
New(FCacheItem); New(FCacheItem);
GetMem(FCacheItem^.Row,FRowBufferSize); GetMem(FCacheItem^.Row,FRowBufferSize);
For Counter := 0 to FRowCount - 1 do For Counter := 0 to FRowCount - 1 do
FCacheItem^.Row[Counter]:=nil; 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; end;
constructor TSqliteDataset.Create(AOwner: TComponent); constructor TSqliteDataset.Create(AOwner: TComponent);
@ -460,25 +465,31 @@ var
begin begin
//Todo: insert debug info //Todo: insert debug info
FDataAllocated:=False; 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
FreeMem(FBeginItem^.Row,FRowBufferSize);
Dispose(FBeginItem);
//Dispose cache item //Dispose cache item
for Counter:= 0 to FRowCount - 1 do for Counter:= 0 to FRowCount - 1 do
StrDispose(FCacheItem^.Row[Counter]); StrDispose(FCacheItem^.Row[Counter]);
FreeMem(FCacheItem^.Row,FRowBufferSize); FreeMem(FCacheItem^.Row,FRowBufferSize);
Dispose(FCacheItem); Dispose(FCacheItem);
If FBeginItem^.Next = nil then //remove it??
exit; // Free last item (FEndItem)
TempItem:=FBeginItem^.Next;
Dispose(FBeginItem);
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;
// Free last item
Dispose(TempItem); Dispose(TempItem);
//Dispose OrphanItems
for Counter:= 0 to FOrphanItems.Count - 1 do for Counter:= 0 to FOrphanItems.Count - 1 do
begin begin
TempItem:=PDataRecord(FOrphanItems[Counter]); TempItem:=PDataRecord(FOrphanItems[Counter]);
@ -508,18 +519,7 @@ function TSqliteDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
var var
ValError:Word; ValError:Word;
FieldRow:PChar; FieldRow:PChar;
//FieldIndex:Integer;
begin begin
if FRecordCount = 0 then // avoid exception in empty datasets -Todo: see if still applys
begin
Result:=False;
Exit;
end;
//Small hack to allow reopening datasets with TDbEdit
//while not fix it in LCL (It seems that TDataLink doesnt update Field property
//after Closing and reopening datasets)
//FieldRow:=PPDataRecord(ActiveBuffer)^^.Row[Field.Index];
//FieldIndex:=Field.FieldNo - 1;
FieldRow:=PPDataRecord(ActiveBuffer)^^.Row[Field.FieldNo - 1]; FieldRow:=PPDataRecord(ActiveBuffer)^^.Row[Field.FieldNo - 1];
Result := FieldRow <> nil; Result := FieldRow <> nil;
if Result and (Buffer <> nil) then //supports GetIsNull if Result and (Buffer <> nil) then //supports GetIsNull
@ -756,11 +756,11 @@ begin
if DefaultFields then if DefaultFields then
CreateFields; CreateFields;
BindFields(True); BindFields(True);
// Get indexfieldno if available // Get PrimaryKeyNo if available
if FIndexFieldName <> '' then if Fields.FindField(FPrimaryKey) <> nil then
FIndexFieldNo:=FieldByName(FIndexFieldName).FieldNo - 1 FPrimaryKeyNo:=Fields.FindField(FPrimaryKey).FieldNo - 1
else else
FIndexFieldNo:=FAutoIncFieldNo; FPrimaryKeyNo:=FAutoIncFieldNo; // -1 if there's no AutoIncField
BuildLinkedList; BuildLinkedList;
FCurrentItem:=FBeginItem; FCurrentItem:=FBeginItem;
@ -862,13 +862,22 @@ end;
// Specific functions // Specific functions
function TSqliteDataset.ExecSQL(ASql:String):Integer; function TSqliteDataset.ExecSQL(ASql:String):Integer;
var
AHandle: Pointer;
begin begin
Result:=0; Result:=0;
//Todo check if Filename exists
if FSqliteHandle <> nil then if FSqliteHandle <> nil then
begin AHandle:=FSqliteHandle
FSqliteReturnId:= sqlite_exec(FSqliteHandle,PChar(ASql),nil,nil,nil); else
Result:=sqlite_changes(FSqliteHandle); if FFileName <> '' then
end; AHandle := sqlite_open(PChar(FFilename),0,nil)
else
DatabaseError ('ExecSql - FileName not set');
FSqliteReturnId:= sqlite_exec(AHandle,PChar(ASql),nil,nil,nil);
Result:=sqlite_changes(AHandle);
if AHandle <> FSqliteHandle then
sqlite_close(AHandle);
end; end;
function TSqliteDataset.ExecSQL:Integer; function TSqliteDataset.ExecSQL:Integer;
@ -882,16 +891,16 @@ var
SqlTemp,KeyName,ASqlLine,TemplateStr:String; SqlTemp,KeyName,ASqlLine,TemplateStr:String;
begin begin
Result:=False; Result:=False;
if (FIndexFieldNo <> -1) and not FComplexSql then if (FPrimaryKeyNo <> -1) and not FComplexSql then
begin begin
StatementsCounter:=0; StatementsCounter:=0;
KeyName:=Fields[FIndexFieldNo].FieldName; KeyName:=Fields[FPrimaryKeyNo].FieldName;
{$ifdef DEBUG} {$ifdef DEBUG}
WriteLn('ApplyUpdates called'); WriteLn('ApplyUpdates called');
if FIndexFieldNo = FAutoIncFieldNo then if FPrimaryKeyNo = FAutoIncFieldNo then
WriteLn('Using an AutoInc field as primary key'); WriteLn('Using an AutoInc field as primary key');
WriteLn('IndexFieldName: ',KeyName); WriteLn('PrimaryKey: ',KeyName);
WriteLn('IndexFieldNo: ',FIndexFieldNo); WriteLn('PrimaryKeyNo: ',FPrimaryKeyNo);
{$endif} {$endif}
SqlTemp:='BEGIN TRANSACTION;'; SqlTemp:='BEGIN TRANSACTION;';
// Update changed records // Update changed records
@ -916,7 +925,7 @@ begin
end; end;
//Todo: see if system.delete trunks AnsiString //Todo: see if system.delete trunks AnsiString
system.delete(ASqlLine,Length(ASqlLine),1); system.delete(ASqlLine,Length(ASqlLine),1);
SqlTemp:=SqlTemp + ASqlLine+' WHERE '+KeyName+' = '+StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[FIndexFieldNo])+';'; SqlTemp:=SqlTemp + ASqlLine+' WHERE '+KeyName+' = '+StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[FPrimaryKeyNo])+';';
inc(StatementsCounter); inc(StatementsCounter);
//ApplyUpdates each 400 statements //ApplyUpdates each 400 statements
if StatementsCounter = 400 then if StatementsCounter = 400 then
@ -976,7 +985,7 @@ begin
for CounterItems:= 0 to FDeletedItems.Count - 1 do for CounterItems:= 0 to FDeletedItems.Count - 1 do
begin begin
SqlTemp:=SqlTemp+TemplateStr+ SqlTemp:=SqlTemp+TemplateStr+
StrPas(PDataRecord(FDeletedItems[CounterItems])^.Row[FIndexFieldNo])+';'; StrPas(PDataRecord(FDeletedItems[CounterItems])^.Row[FPrimaryKeyNo])+';';
inc(StatementsCounter); inc(StatementsCounter);
//ApplyUpdates each 400 statements //ApplyUpdates each 400 statements
if StatementsCounter = 400 then if StatementsCounter = 400 then
@ -1167,7 +1176,7 @@ begin
RegisterComponents('Data Access', [TSqliteDataset]); RegisterComponents('Data Access', [TSqliteDataset]);
end; end;
{$ifdef DEBUG} {$ifdef DEBUGACTIVEBUFFER}
procedure TSqliteDataset.SetCurrentItem(Value:PDataRecord); procedure TSqliteDataset.SetCurrentItem(Value:PDataRecord);
var var
ANo:Integer; ANo:Integer;