|
@@ -23,8 +23,8 @@ unit customsqliteds;
|
|
|
|
|
|
{$Mode ObjFpc}
|
|
|
{$H+}
|
|
|
-{ $Define DEBUG}
|
|
|
-{ $Define DEBUGACTIVEBUFFER}
|
|
|
+{.$Define DEBUG_SQLITEDS}
|
|
|
+{.$Define DEBUGACTIVEBUFFER}
|
|
|
|
|
|
interface
|
|
|
|
|
@@ -81,7 +81,6 @@ type
|
|
|
{$endif}
|
|
|
FInternalActiveBuffer: PDataRecord;
|
|
|
FInsertBookmark: PDataRecord;
|
|
|
- FBufferSize: Integer;
|
|
|
FExpectedAppends: Integer;
|
|
|
FExpectedDeletes: Integer;
|
|
|
FExpectedUpdates: Integer;
|
|
@@ -109,7 +108,6 @@ type
|
|
|
FUpdatedItems: TFPList;
|
|
|
FAddedItems: TFPList;
|
|
|
FDeletedItems: TFPList;
|
|
|
- FOrphanItems: TFPList;
|
|
|
FReturnCode: Integer;
|
|
|
FSqliteHandle: Pointer;
|
|
|
FDataAllocated: Boolean;
|
|
@@ -126,6 +124,7 @@ type
|
|
|
procedure GetSqliteHandle;
|
|
|
function GetSqliteVersion: String; virtual; abstract;
|
|
|
procedure BuildLinkedList; virtual; abstract;
|
|
|
+ procedure FreeItem(AItem: PDataRecord);
|
|
|
procedure DisposeLinkedList;
|
|
|
procedure SetDetailFilter;
|
|
|
procedure MasterChanged(Sender: TObject);
|
|
@@ -176,9 +175,9 @@ type
|
|
|
function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint; 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}
|
|
|
+ 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;override;
|
|
|
// Additional procedures
|
|
|
function ApplyUpdates: Boolean;
|
|
|
function CreateTable: Boolean;
|
|
@@ -242,8 +241,12 @@ type
|
|
|
property AfterDelete;
|
|
|
property BeforeScroll;
|
|
|
property AfterScroll;
|
|
|
+ property BeforeRefresh;
|
|
|
+ property AfterRefresh;
|
|
|
property OnDeleteError;
|
|
|
property OnEditError;
|
|
|
+ property OnNewRecord;
|
|
|
+ property OnPostError;
|
|
|
end;
|
|
|
|
|
|
function Num2SqlStr(APChar: PChar): String;
|
|
@@ -334,7 +337,7 @@ begin
|
|
|
Move(Buffer,(NewRow+FRowSize)^,Count);
|
|
|
FActiveItem^.Row[FFieldIndex]:=NewRow;
|
|
|
StrDispose(FFieldRow);
|
|
|
- {$ifdef DEBUG}
|
|
|
+ {$ifdef DEBUG_SQLITEDS}
|
|
|
WriteLn('##TDSStream.Write##');
|
|
|
WriteLn(' FPosition(Before): ',FPosition);
|
|
|
WriteLn(' FRowSize(Before): ',FRowSize);
|
|
@@ -358,7 +361,7 @@ begin
|
|
|
Move((FFieldRow+FPosition)^,Buffer,BytesToMove);
|
|
|
Inc(FPosition,BytesToMove);
|
|
|
Result:=BytesToMove;
|
|
|
- {$ifdef DEBUG}
|
|
|
+ {$ifdef DEBUG_SQLITEDS}
|
|
|
WriteLn('##TDSStream.Read##');
|
|
|
WriteLn(' Bytes requested: ',Count);
|
|
|
WriteLn(' Bytes moved: ',BytesToMove);
|
|
@@ -370,12 +373,9 @@ end;
|
|
|
// TCustomSqliteDataset override methods
|
|
|
|
|
|
function TCustomSqliteDataset.AllocRecordBuffer: PChar;
|
|
|
-var
|
|
|
- APointer:Pointer;
|
|
|
begin
|
|
|
- APointer := AllocMem(FBufferSize);
|
|
|
- PDataRecord(APointer^):=FBeginItem;
|
|
|
- Result:=APointer;
|
|
|
+ Result := AllocMem(SizeOf(PPDataRecord));
|
|
|
+ PDataRecord(Pointer(Result)^):=FBeginItem;
|
|
|
end;
|
|
|
|
|
|
constructor TCustomSqliteDataset.Create(AOwner: TComponent);
|
|
@@ -396,10 +396,8 @@ begin
|
|
|
FMasterLink.OnMasterDisable:=@MasterDisabled;
|
|
|
FIndexFieldList:=TList.Create;
|
|
|
BookmarkSize := SizeOf(Pointer);
|
|
|
- FBufferSize := SizeOf(PPDataRecord);
|
|
|
FUpdatedItems:= TFPList.Create;
|
|
|
FAddedItems:= TFPList.Create;
|
|
|
- FOrphanItems:= TFPList.Create;
|
|
|
FDeletedItems:= TFPList.Create;
|
|
|
FSqlList:=TStringList.Create;
|
|
|
inherited Create(AOwner);
|
|
@@ -444,7 +442,6 @@ begin
|
|
|
FUpdatedItems.Destroy;
|
|
|
FAddedItems.Destroy;
|
|
|
FDeletedItems.Destroy;
|
|
|
- FOrphanItems.Destroy;
|
|
|
FMasterLink.Destroy;
|
|
|
FIndexFieldList.Destroy;
|
|
|
FSqlList.Destroy;
|
|
@@ -519,41 +516,30 @@ end;
|
|
|
procedure TCustomSqliteDataset.DisposeLinkedList;
|
|
|
var
|
|
|
TempItem:PDataRecord;
|
|
|
- Counter,I:Integer;
|
|
|
+ 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);
|
|
|
- TempItem:=TempItem^.Next;
|
|
|
- Dispose(TempItem^.Previous);
|
|
|
- end;
|
|
|
-
|
|
|
+ while TempItem^.Next <> nil do
|
|
|
+ begin
|
|
|
+ TempItem:=TempItem^.Next;
|
|
|
+ FreeItem(TempItem^.Previous);
|
|
|
+ end;
|
|
|
+
|
|
|
+ //Dispose Deleted Items
|
|
|
+ //Directly access list pointer since the index check is already done in the loop
|
|
|
+ for i:= 0 to FDeletedItems.Count - 1 do
|
|
|
+ FreeItem(PDataRecord(FDeletedItems.List^[i]));
|
|
|
+
|
|
|
//Dispose FBeginItem.Row
|
|
|
FreeMem(FBeginItem^.Row,FRowBufferSize);
|
|
|
|
|
|
- //Dispose cache item
|
|
|
- for Counter:= 0 to FRowCount - 1 do
|
|
|
- StrDispose(FCacheItem^.Row[Counter]);
|
|
|
+ //Dispose cache item row
|
|
|
+ for i:= 0 to FRowCount - 1 do
|
|
|
+ StrDispose(FCacheItem^.Row[i]);
|
|
|
FreeMem(FCacheItem^.Row,FRowBufferSize);
|
|
|
-
|
|
|
- //Dispose OrphanItems
|
|
|
- for Counter:= 0 to FOrphanItems.Count - 1 do
|
|
|
- begin
|
|
|
- TempItem:=PDataRecord(FOrphanItems[Counter]);
|
|
|
- for I:= 0 to FRowCount - 1 do
|
|
|
- StrDispose(TempItem^.Row[I]);
|
|
|
- FreeMem(TempItem^.Row,FRowBufferSize);
|
|
|
- Dispose(TempItem);
|
|
|
- end;
|
|
|
end;
|
|
|
|
|
|
procedure TCustomSqliteDataset.FreeRecordBuffer(var Buffer: PChar);
|
|
@@ -683,14 +669,14 @@ end;
|
|
|
|
|
|
function TCustomSqliteDataset.GetRecordSize: Word;
|
|
|
begin
|
|
|
- Result := FBufferSize; //??
|
|
|
+ Result := SizeOf(PPDataRecord); //??
|
|
|
end;
|
|
|
|
|
|
procedure TCustomSqliteDataset.InternalAddRecord(Buffer: Pointer; DoAppend: Boolean);
|
|
|
var
|
|
|
NewItem: PDataRecord;
|
|
|
begin
|
|
|
- {$ifdef DEBUG}
|
|
|
+ {$ifdef DEBUG_SQLITEDS}
|
|
|
if PPDataRecord(Buffer)^ <> FCacheItem then
|
|
|
DatabaseError('PPDataRecord(Buffer) <> FCacheItem - Problem',Self);
|
|
|
{$endif}
|
|
@@ -727,7 +713,6 @@ begin
|
|
|
FAddedItems.Clear;
|
|
|
FUpdatedItems.Clear;
|
|
|
FDeletedItems.Clear;
|
|
|
- FOrphanItems.Clear;
|
|
|
FRecordCount:=0;
|
|
|
end;
|
|
|
|
|
@@ -749,18 +734,10 @@ var
|
|
|
TempItem:PDataRecord;
|
|
|
ValError,TempInteger:Integer;
|
|
|
begin
|
|
|
- If FRecordCount = 0 then
|
|
|
+ if FRecordCount = 0 then
|
|
|
Exit;
|
|
|
Dec(FRecordCount);
|
|
|
TempItem:=PPDataRecord(ActiveBuffer)^;
|
|
|
- // Remove from changed list
|
|
|
- FUpdatedItems.Remove(TempItem);
|
|
|
- if FAddedItems.Remove(TempItem) = -1 then
|
|
|
- FDeletedItems.Add(TempItem);
|
|
|
- //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;
|
|
|
if FCurrentItem = TempItem then
|
|
@@ -777,6 +754,12 @@ begin
|
|
|
if (ValError = 0) and (TempInteger = (FNextAutoInc - 1)) then
|
|
|
Dec(FNextAutoInc);
|
|
|
end;
|
|
|
+ // Update item lists
|
|
|
+ FUpdatedItems.Remove(TempItem);
|
|
|
+ if FAddedItems.Remove(TempItem) = -1 then
|
|
|
+ FDeletedItems.Add(TempItem)
|
|
|
+ else
|
|
|
+ FreeItem(TempItem);
|
|
|
end;
|
|
|
|
|
|
procedure TCustomSqliteDataset.InternalEdit;
|
|
@@ -909,6 +892,7 @@ var
|
|
|
TempItem:PDataRecord;
|
|
|
begin
|
|
|
Result:=nil;
|
|
|
+ CheckBrowseMode;
|
|
|
// Currently ignore options
|
|
|
AFieldList:=TList.Create;
|
|
|
try
|
|
@@ -953,7 +937,7 @@ begin
|
|
|
finally
|
|
|
AFieldList.Destroy;
|
|
|
end;
|
|
|
- {$ifdef DEBUG}
|
|
|
+ {$ifdef DEBUG_SQLITEDS}
|
|
|
writeln('##TCustomSqliteDataset.FindRecordItem##');
|
|
|
writeln(' KeyFields: ',keyfields);
|
|
|
writeln(' KeyValues: ',keyvalues);
|
|
@@ -996,12 +980,22 @@ begin
|
|
|
FSqliteHandle:=InternalGetHandle;
|
|
|
end;
|
|
|
|
|
|
-function TCustomSqliteDataset.Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean;
|
|
|
+procedure TCustomSqliteDataset.FreeItem(AItem: PDataRecord);
|
|
|
+var
|
|
|
+ i: Integer;
|
|
|
+begin
|
|
|
+ for i:= 0 to FRowCount - 1 do
|
|
|
+ StrDispose(AItem^.Row[i]);
|
|
|
+ FreeMem(AItem^.Row,FRowBufferSize);
|
|
|
+ Dispose(AItem);
|
|
|
+end;
|
|
|
+
|
|
|
+function TCustomSqliteDataset.Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : Boolean;
|
|
|
begin
|
|
|
Result:=FindRecordItem(FBeginItem^.Next,KeyFields,KeyValues,Options,True) <> nil;
|
|
|
end;
|
|
|
|
|
|
-function TCustomSqliteDataset.LocateNext(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean;
|
|
|
+function TCustomSqliteDataset.LocateNext(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : Boolean;
|
|
|
begin
|
|
|
Result:=FindRecordItem(PPDataRecord(ActiveBuffer)^^.Next,KeyFields,KeyValues,Options,True) <> nil;
|
|
|
end;
|
|
@@ -1040,7 +1034,6 @@ end;
|
|
|
procedure TCustomSqliteDataset.SetExpectedDeletes(AValue:Integer);
|
|
|
begin
|
|
|
FDeletedItems.Capacity:=AValue;
|
|
|
- FOrphanItems.Capacity:=AValue;
|
|
|
end;
|
|
|
|
|
|
procedure TCustomSqliteDataset.SetFieldData(Field: TField; Buffer: Pointer;
|
|
@@ -1155,7 +1148,7 @@ end;
|
|
|
procedure TCustomSqliteDataset.MasterChanged(Sender: TObject);
|
|
|
begin
|
|
|
SetDetailFilter;
|
|
|
- {$ifdef DEBUG}
|
|
|
+ {$ifdef DEBUG_SQLITEDS}
|
|
|
writeln('##TCustomSqliteDataset.MasterChanged##');
|
|
|
writeln(' SQL used to filter detail dataset:');
|
|
|
writeln(' ',FSql);
|
|
@@ -1244,7 +1237,9 @@ function TCustomSqliteDataset.ApplyUpdates:Boolean;
|
|
|
var
|
|
|
iFields,iItems,StatementsCounter:Integer;
|
|
|
SqlTemp,WhereKeyNameEqual,ASqlLine,TemplateStr:String;
|
|
|
+ TempItem: PDataRecord;
|
|
|
begin
|
|
|
+ CheckBrowseMode;
|
|
|
if not UpdatesPending then
|
|
|
begin
|
|
|
Result:=True;
|
|
@@ -1255,7 +1250,7 @@ begin
|
|
|
begin
|
|
|
StatementsCounter:=0;
|
|
|
WhereKeyNameEqual:=' WHERE '+Fields[FPrimaryKeyNo].FieldName+' = ';
|
|
|
- {$ifdef DEBUG}
|
|
|
+ {$ifdef DEBUG_SQLITEDS}
|
|
|
WriteLn('##TCustomSqliteDataset.ApplyUpdates##');
|
|
|
if FPrimaryKeyNo = FAutoIncFieldNo then
|
|
|
WriteLn(' Using an AutoInc field as primary key');
|
|
@@ -1268,8 +1263,10 @@ begin
|
|
|
TemplateStr:='DELETE FROM '+FTableName+WhereKeyNameEqual;
|
|
|
for iItems:= 0 to FDeletedItems.Count - 1 do
|
|
|
begin
|
|
|
+ TempItem:=PDataRecord(FDeletedItems.List^[iItems]);
|
|
|
SqlTemp:=SqlTemp+(TemplateStr+
|
|
|
- StrPas(PDataRecord(FDeletedItems[iItems])^.Row[FPrimaryKeyNo])+';');
|
|
|
+ StrPas(TempItem^.Row[FPrimaryKeyNo])+';');
|
|
|
+ FreeItem(TempItem);
|
|
|
inc(StatementsCounter);
|
|
|
//ApplyUpdates each 400 statements
|
|
|
if StatementsCounter = 400 then
|
|
@@ -1312,9 +1309,7 @@ begin
|
|
|
begin
|
|
|
TemplateStr:='INSERT INTO '+FTableName+ ' (';
|
|
|
for iFields:= 0 to Fields.Count - 2 do
|
|
|
- begin
|
|
|
TemplateStr:=TemplateStr + Fields[iFields].FieldName+',';
|
|
|
- end;
|
|
|
TemplateStr:= TemplateStr+Fields[Fields.Count - 1].FieldName+') VALUES (';
|
|
|
end;
|
|
|
for iItems:= 0 to FAddedItems.Count - 1 do
|
|
@@ -1339,7 +1334,7 @@ begin
|
|
|
end;
|
|
|
end;
|
|
|
SqlTemp:=SqlTemp+'COMMIT;';
|
|
|
- {$ifdef DEBUG}
|
|
|
+ {$ifdef DEBUG_SQLITEDS}
|
|
|
writeln(' SQL: ',SqlTemp);
|
|
|
{$endif}
|
|
|
FAddedItems.Clear;
|
|
@@ -1348,7 +1343,7 @@ begin
|
|
|
FReturnCode:=SqliteExec(PChar(SqlTemp),nil,nil);
|
|
|
Result:= FReturnCode = SQLITE_OK;
|
|
|
end;
|
|
|
- {$ifdef DEBUG}
|
|
|
+ {$ifdef DEBUG_SQLITEDS}
|
|
|
writeln(' Result: ',Result);
|
|
|
{$endif}
|
|
|
end;
|
|
@@ -1363,7 +1358,7 @@ var
|
|
|
SqlTemp:String;
|
|
|
i:Integer;
|
|
|
begin
|
|
|
- {$ifdef DEBUG}
|
|
|
+ {$ifdef DEBUG_SQLITEDS}
|
|
|
writeln('##TCustomSqliteDataset.CreateTable##');
|
|
|
if ATableName = '' then
|
|
|
WriteLn(' TableName Not Set');
|
|
@@ -1411,7 +1406,7 @@ begin
|
|
|
SqlTemp:=SqlTemp+ ' , ';
|
|
|
end;
|
|
|
SqlTemp:=SqlTemp+');';
|
|
|
- {$ifdef DEBUG}
|
|
|
+ {$ifdef DEBUG_SQLITEDS}
|
|
|
writeln(' SQL: ',SqlTemp);
|
|
|
{$endif}
|
|
|
ExecSQL(SqlTemp);
|
|
@@ -1443,6 +1438,7 @@ var
|
|
|
begin
|
|
|
if not Assigned(Callback) then
|
|
|
DatabaseError('Callback parameter not set',Self);
|
|
|
+ CheckBrowseMode;
|
|
|
if rsDeleted in RecordStates then
|
|
|
with FDeletedItems do
|
|
|
for i:= 0 to Count - 1 do
|
|
@@ -1476,7 +1472,6 @@ begin
|
|
|
FAddedItems.Clear;
|
|
|
FUpdatedItems.Clear;
|
|
|
FDeletedItems.Clear;
|
|
|
- FOrphanItems.Clear;
|
|
|
//Reopen
|
|
|
BuildLinkedList;
|
|
|
FCurrentItem:=FBeginItem;
|