mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-12-12 02:20:36 +01:00
* 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:
parent
d1e6934a5b
commit
11eefecc8c
@ -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;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user