mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-16 10:19:17 +02:00
--- Merging r33254 into '.':
U packages/fcl-db/src/sqldb/sqldb.pp U packages/fcl-db/src/sqldb/mysql/mysqlconn.inc --- Recording mergeinfo for merge of r33254 into '.': U . --- Merging r33420 into '.': U packages/fcl-db/tests/testdbbasics.pas U packages/fcl-db/src/base/bufdataset.pas --- Recording mergeinfo for merge of r33420 into '.': G . --- Merging r33421 into '.': G packages/fcl-db/src/base/bufdataset.pas --- Recording mergeinfo for merge of r33421 into '.': G . --- Merging r33422 into '.': G packages/fcl-db/src/base/bufdataset.pas --- Recording mergeinfo for merge of r33422 into '.': G . --- Merging r33427 into '.': G packages/fcl-db/src/base/bufdataset.pas --- Recording mergeinfo for merge of r33427 into '.': G . --- Merging r33570 into '.': G packages/fcl-db/src/sqldb/sqldb.pp --- Recording mergeinfo for merge of r33570 into '.': G . --- Merging r33666 into '.': U packages/fcl-db/tests/toolsunit.pas U packages/fcl-db/tests/sqldbtoolsunit.pas --- Recording mergeinfo for merge of r33666 into '.': G . --- Merging r33905 into '.': U packages/fcl-db/src/sqldb/interbase/fbadmin.pp --- Recording mergeinfo for merge of r33905 into '.': G . --- Merging r33911 into '.': G packages/fcl-db/src/sqldb/interbase/fbadmin.pp --- Recording mergeinfo for merge of r33911 into '.': G . --- Merging r33912 into '.': G packages/fcl-db/src/sqldb/interbase/fbadmin.pp --- Recording mergeinfo for merge of r33912 into '.': G . --- Merging r33913 into '.': U packages/fcl-db/src/sqldb/interbase/fbadmin.pp --- Recording mergeinfo for merge of r33913 into '.': G . --- Merging r34095 into '.': U packages/postgres/src/postgres3dyn.pp --- Recording mergeinfo for merge of r34095 into '.': G . # revisions: 33254,33420,33421,33422,33427,33570,33666,33905,33911,33912,33913,34095 git-svn-id: branches/fixes_3_0@35993 -
This commit is contained in:
parent
f34993a217
commit
c6ad797d22
@ -159,7 +159,7 @@ type
|
|||||||
procedure GotoBookmark(const ABookmark : PBufBookmark); virtual; abstract;
|
procedure GotoBookmark(const ABookmark : PBufBookmark); virtual; abstract;
|
||||||
function BookmarkValid(const ABookmark: PBufBookmark): boolean; virtual;
|
function BookmarkValid(const ABookmark: PBufBookmark): boolean; virtual;
|
||||||
function CompareBookmarks(const ABookmark1, ABookmark2 : PBufBookmark) : integer; virtual;
|
function CompareBookmarks(const ABookmark1, ABookmark2 : PBufBookmark) : integer; virtual;
|
||||||
function SameBookmarks(const ABookmark1, ABookmark2 : PBufBookmark) : boolean; inline;
|
function SameBookmarks(const ABookmark1, ABookmark2 : PBufBookmark) : boolean; virtual;
|
||||||
|
|
||||||
procedure InitialiseIndex; virtual; abstract;
|
procedure InitialiseIndex; virtual; abstract;
|
||||||
|
|
||||||
@ -228,6 +228,7 @@ type
|
|||||||
procedure StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark); override;
|
procedure StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark); override;
|
||||||
procedure GotoBookmark(const ABookmark : PBufBookmark); override;
|
procedure GotoBookmark(const ABookmark : PBufBookmark); override;
|
||||||
function CompareBookmarks(const ABookmark1, ABookmark2: PBufBookmark): integer; override;
|
function CompareBookmarks(const ABookmark1, ABookmark2: PBufBookmark): integer; override;
|
||||||
|
function SameBookmarks(const ABookmark1, ABookmark2 : PBufBookmark) : boolean; override;
|
||||||
procedure InitialiseIndex; override;
|
procedure InitialiseIndex; override;
|
||||||
|
|
||||||
procedure InitialiseSpareRecord(const ASpareRecord : TRecordBuffer); override;
|
procedure InitialiseSpareRecord(const ASpareRecord : TRecordBuffer); override;
|
||||||
@ -496,6 +497,7 @@ type
|
|||||||
function GetRecordUpdateBuffer(const ABookmark : TBufBookmark; IncludePrior : boolean = false; AFindNext : boolean = false) : boolean;
|
function GetRecordUpdateBuffer(const ABookmark : TBufBookmark; IncludePrior : boolean = false; AFindNext : boolean = false) : boolean;
|
||||||
function GetRecordUpdateBufferCached(const ABookmark : TBufBookmark; IncludePrior : boolean = false) : boolean;
|
function GetRecordUpdateBufferCached(const ABookmark : TBufBookmark; IncludePrior : boolean = false) : boolean;
|
||||||
function GetActiveRecordUpdateBuffer : boolean;
|
function GetActiveRecordUpdateBuffer : boolean;
|
||||||
|
procedure CancelRecordUpdateBuffer(AUpdateBufferIndex: integer; var ABookmark: TBufBookmark);
|
||||||
procedure ParseFilter(const AFilter: string);
|
procedure ParseFilter(const AFilter: string);
|
||||||
|
|
||||||
function GetIndexDefs : TIndexDefs;
|
function GetIndexDefs : TIndexDefs;
|
||||||
@ -575,6 +577,7 @@ type
|
|||||||
procedure ApplyUpdates; virtual; overload;
|
procedure ApplyUpdates; virtual; overload;
|
||||||
procedure ApplyUpdates(MaxErrors: Integer); virtual; overload;
|
procedure ApplyUpdates(MaxErrors: Integer); virtual; overload;
|
||||||
procedure MergeChangeLog;
|
procedure MergeChangeLog;
|
||||||
|
procedure RevertRecord;
|
||||||
procedure CancelUpdates; virtual;
|
procedure CancelUpdates; virtual;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
function Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean; override;
|
function Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean; override;
|
||||||
@ -1677,6 +1680,11 @@ begin
|
|||||||
Result := -Result;
|
Result := -Result;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TDoubleLinkedBufIndex.SameBookmarks(const ABookmark1, ABookmark2: PBufBookmark): boolean;
|
||||||
|
begin
|
||||||
|
Result := Assigned(ABookmark1) and Assigned(ABookmark2) and (ABookmark1^.BookmarkData = ABookmark2^.BookmarkData);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TDoubleLinkedBufIndex.InitialiseIndex;
|
procedure TDoubleLinkedBufIndex.InitialiseIndex;
|
||||||
begin
|
begin
|
||||||
// Do nothing
|
// Do nothing
|
||||||
@ -2401,90 +2409,106 @@ begin
|
|||||||
raise EDatabaseError.Create(SApplyRecNotSupported);
|
raise EDatabaseError.Create(SApplyRecNotSupported);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TCustomBufDataset.CancelUpdates;
|
procedure TCustomBufDataset.CancelRecordUpdateBuffer(AUpdateBufferIndex: integer; var ABookmark: TBufBookmark);
|
||||||
var StoreRecBM : TBufBookmark;
|
var
|
||||||
procedure CancelUpdBuffer(var AUpdBuffer : TRecUpdateBuffer);
|
ARecordBuffer: TRecordBuffer;
|
||||||
var
|
NBookmark : TBufBookmark;
|
||||||
TmpBuf : TRecordBuffer;
|
i : integer;
|
||||||
StoreUpdBuf : integer;
|
begin
|
||||||
Bm : TBufBookmark;
|
with FUpdateBuffer[AUpdateBufferIndex] do
|
||||||
begin
|
if Assigned(BookmarkData.BookmarkData) then // this is used to exclude buffers which are already handled
|
||||||
with AUpdBuffer do
|
|
||||||
begin
|
begin
|
||||||
if Not assigned(BookmarkData.BookmarkData) then
|
case UpdateKind of
|
||||||
exit;// this is used to exclude buffers which are already handled
|
ukModify:
|
||||||
Case UpdateKind of
|
|
||||||
ukModify:
|
|
||||||
begin
|
|
||||||
FCurrentIndex.GotoBookmark(@BookmarkData);
|
|
||||||
move(TRecordBuffer(OldValuesBuffer)^,TRecordBuffer(FCurrentIndex.CurrentBuffer)^,FRecordSize);
|
|
||||||
FreeRecordBuffer(OldValuesBuffer);
|
|
||||||
end;
|
|
||||||
ukDelete:
|
|
||||||
if (assigned(OldValuesBuffer)) then
|
|
||||||
begin
|
begin
|
||||||
FCurrentIndex.GotoBookmark(@NextBookmarkData);
|
FCurrentIndex.GotoBookmark(@BookmarkData);
|
||||||
FCurrentIndex.InsertRecordBeforeCurrentRecord(TRecordBuffer(BookmarkData.BookmarkData));
|
move(TRecordBuffer(OldValuesBuffer)^, TRecordBuffer(FCurrentIndex.CurrentBuffer)^, FRecordSize);
|
||||||
FCurrentIndex.ScrollBackward;
|
|
||||||
move(TRecordBuffer(OldValuesBuffer)^,TRecordBuffer(FCurrentIndex.CurrentBuffer)^,FRecordSize);
|
|
||||||
|
|
||||||
{for x := length(FUpdateBuffer)-1 downto 0 do
|
|
||||||
begin
|
|
||||||
if (FUpdateBuffer[x].UpdateKind=ukDelete) and FCurrentIndex.SameBookmarks(@FUpdateBuffer[x].NextBookmarkData,@BookmarkData) then
|
|
||||||
CancelUpdBuffer(FUpdateBuffer[x]);
|
|
||||||
end;}
|
|
||||||
FreeRecordBuffer(OldValuesBuffer);
|
FreeRecordBuffer(OldValuesBuffer);
|
||||||
inc(FBRecordCount);
|
|
||||||
end ;
|
|
||||||
ukInsert:
|
|
||||||
begin
|
|
||||||
// Process all update buffers linked to this record before this record is removed
|
|
||||||
StoreUpdBuf:=FCurrentUpdateBuffer;
|
|
||||||
Bm := BookmarkData;
|
|
||||||
BookmarkData.BookmarkData:=nil; // Avoid infinite recursion...
|
|
||||||
if GetRecordUpdateBuffer(Bm,True,False) then
|
|
||||||
begin
|
|
||||||
repeat
|
|
||||||
if (FCurrentUpdateBuffer<>StoreUpdBuf) then
|
|
||||||
CancelUpdBuffer(FUpdateBuffer[FCurrentUpdateBuffer]);
|
|
||||||
until not GetRecordUpdateBuffer(Bm,True,True);
|
|
||||||
end;
|
end;
|
||||||
FCurrentUpdateBuffer:=StoreUpdBuf;
|
ukDelete:
|
||||||
|
if (assigned(OldValuesBuffer)) then
|
||||||
|
begin
|
||||||
|
FCurrentIndex.GotoBookmark(@NextBookmarkData);
|
||||||
|
FCurrentIndex.InsertRecordBeforeCurrentRecord(TRecordBuffer(BookmarkData.BookmarkData));
|
||||||
|
FCurrentIndex.ScrollBackward;
|
||||||
|
move(TRecordBuffer(OldValuesBuffer)^, TRecordBuffer(FCurrentIndex.CurrentBuffer)^, FRecordSize);
|
||||||
|
FreeRecordBuffer(OldValuesBuffer);
|
||||||
|
inc(FBRecordCount);
|
||||||
|
end;
|
||||||
|
ukInsert:
|
||||||
|
begin
|
||||||
|
FCurrentIndex.GotoBookmark(@BookmarkData);
|
||||||
|
ARecordBuffer := FCurrentIndex.CurrentRecord;
|
||||||
|
|
||||||
FCurrentIndex.GotoBookmark(@Bm);
|
// Find next record's bookmark
|
||||||
TmpBuf:=FCurrentIndex.CurrentRecord;
|
FCurrentIndex.DoScrollForward;
|
||||||
// resync won't work if the currentbuffer is freed...
|
FCurrentIndex.StoreCurrentRecIntoBookmark(@NBookmark);
|
||||||
if FCurrentIndex.SameBookmarks(@Bm,@StoreRecBM) then with FCurrentIndex do
|
// Process (re-link) all update buffers linked to this record before this record is removed
|
||||||
begin
|
// Modified record #1, which is later deleted can be linked to another inserted record #2. In this case deleted record #1 precedes inserted #2 in update buffer.
|
||||||
GotoBookmark(@StoreRecBM);
|
// Deleted records, which are deleted after this record is inserted are in update buffer after this record.
|
||||||
if ScrollForward = grEOF then
|
// if we need revert inserted record which is linked from another deleted records, then we must re-link these records
|
||||||
if ScrollBackward = grBOF then
|
for i:=0 to high(FUpdateBuffer) do
|
||||||
ScrollLast; // last record will be removed from index, so move to spare record
|
if (FUpdateBuffer[i].UpdateKind = ukDelete) and
|
||||||
StoreCurrentRecIntoBookmark(@StoreRecBM);
|
(FUpdateBuffer[i].NextBookmarkData.BookmarkData = BookmarkData.BookmarkData) then
|
||||||
|
FUpdateBuffer[i].NextBookmarkData := NBookmark;
|
||||||
|
|
||||||
|
// ReSync won't work if the CurrentBuffer is freed ... so in this case move to next/prior record
|
||||||
|
if FCurrentIndex.SameBookmarks(@BookmarkData,@ABookmark) then with FCurrentIndex do
|
||||||
|
begin
|
||||||
|
GotoBookmark(@ABookmark);
|
||||||
|
if ScrollForward = grEOF then
|
||||||
|
if ScrollBackward = grBOF then
|
||||||
|
ScrollLast; // last record will be removed from index, so move to spare record
|
||||||
|
StoreCurrentRecIntoBookmark(@ABookmark);
|
||||||
|
end;
|
||||||
|
|
||||||
|
RemoveRecordFromIndexes(BookmarkData);
|
||||||
|
FreeRecordBuffer(ARecordBuffer);
|
||||||
|
dec(FBRecordCount);
|
||||||
end;
|
end;
|
||||||
RemoveRecordFromIndexes(Bm);
|
|
||||||
FreeRecordBuffer(TmpBuf);
|
|
||||||
dec(FBRecordCount);
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
BookmarkData.BookmarkData:=nil;
|
BookmarkData.BookmarkData := nil;
|
||||||
end;
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCustomBufDataset.RevertRecord;
|
||||||
|
var
|
||||||
|
ABookmark : TBufBookmark;
|
||||||
|
begin
|
||||||
|
CheckBrowseMode;
|
||||||
|
|
||||||
|
if GetActiveRecordUpdateBuffer then
|
||||||
|
begin
|
||||||
|
FCurrentIndex.StoreCurrentRecIntoBookmark(@ABookmark);
|
||||||
|
|
||||||
|
CancelRecordUpdateBuffer(FCurrentUpdateBuffer, ABookmark);
|
||||||
|
|
||||||
|
// remove update record of current record from update-buffer array
|
||||||
|
Move(FUpdateBuffer[FCurrentUpdateBuffer+1], FUpdateBuffer[FCurrentUpdateBuffer], (High(FUpdateBuffer)-FCurrentUpdateBuffer)*SizeOf(TRecUpdateBuffer));
|
||||||
|
SetLength(FUpdateBuffer, High(FUpdateBuffer));
|
||||||
|
|
||||||
|
FCurrentIndex.GotoBookmark(@ABookmark);
|
||||||
|
|
||||||
|
Resync([]);
|
||||||
end;
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
var r : Integer;
|
procedure TCustomBufDataset.CancelUpdates;
|
||||||
|
var
|
||||||
|
ABookmark : TBufBookmark;
|
||||||
|
r : Integer;
|
||||||
begin
|
begin
|
||||||
CheckBrowseMode;
|
CheckBrowseMode;
|
||||||
|
|
||||||
if Length(FUpdateBuffer) > 0 then
|
if Length(FUpdateBuffer) > 0 then
|
||||||
begin
|
begin
|
||||||
FCurrentIndex.StoreCurrentRecIntoBookmark(@StoreRecBM);
|
FCurrentIndex.StoreCurrentRecIntoBookmark(@ABookmark);
|
||||||
for r := Length(FUpdateBuffer) - 1 downto 0 do
|
|
||||||
CancelUpdBuffer(FUpdateBuffer[r]);
|
|
||||||
|
|
||||||
SetLength(FUpdateBuffer,0);
|
for r := High(FUpdateBuffer) downto 0 do
|
||||||
|
CancelRecordUpdateBuffer(r, ABookmark);
|
||||||
|
SetLength(FUpdateBuffer, 0);
|
||||||
|
|
||||||
FCurrentIndex.GotoBookmark(@StoreRecBM);
|
FCurrentIndex.GotoBookmark(@ABookmark);
|
||||||
|
|
||||||
Resync([]);
|
Resync([]);
|
||||||
end;
|
end;
|
||||||
@ -2635,7 +2659,7 @@ begin
|
|||||||
FAutoIncField.AsInteger := FAutoIncValue;
|
FAutoIncField.AsInteger := FAutoIncValue;
|
||||||
inc(FAutoIncValue);
|
inc(FAutoIncValue);
|
||||||
end;
|
end;
|
||||||
// The active buffer is the newly created TDataset record,
|
// The active buffer is the newly created TDataSet record,
|
||||||
// from which the bookmark is set to the record where the new record should be
|
// from which the bookmark is set to the record where the new record should be
|
||||||
// inserted
|
// inserted
|
||||||
ABookmark := PBufBookmark(ActiveBuffer + FRecordSize);
|
ABookmark := PBufBookmark(ActiveBuffer + FRecordSize);
|
||||||
@ -2653,12 +2677,13 @@ begin
|
|||||||
// insert (before current record)
|
// insert (before current record)
|
||||||
FIndexes[i].GotoBookmark(ABookmark);
|
FIndexes[i].GotoBookmark(ABookmark);
|
||||||
|
|
||||||
|
// insert new record before current record
|
||||||
FIndexes[i].InsertRecordBeforeCurrentRecord(ABuff);
|
FIndexes[i].InsertRecordBeforeCurrentRecord(ABuff);
|
||||||
// newly inserted record becomes current record
|
// newly inserted record becomes current record
|
||||||
FIndexes[i].ScrollBackward;
|
FIndexes[i].ScrollBackward;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Link the newly created record buffer to the newly created TDataset record
|
// Link the newly created record buffer to the newly created TDataSet record
|
||||||
FCurrentIndex.StoreCurrentRecIntoBookmark(ABookmark);
|
FCurrentIndex.StoreCurrentRecIntoBookmark(ABookmark);
|
||||||
ABookmark^.BookmarkFlag := bfInserted;
|
ABookmark^.BookmarkFlag := bfInserted;
|
||||||
|
|
||||||
@ -2679,12 +2704,11 @@ begin
|
|||||||
|
|
||||||
if State = dsEdit then
|
if State = dsEdit then
|
||||||
begin
|
begin
|
||||||
// Create an oldvalues buffer with the old values of the record
|
// Create an OldValues buffer with the old values of the record
|
||||||
FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := IntAllocRecordBuffer;
|
|
||||||
with FCurrentIndex do
|
|
||||||
// Move only the real data
|
|
||||||
move(CurrentBuffer^,FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer^,FRecordSize);
|
|
||||||
FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind := ukModify;
|
FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind := ukModify;
|
||||||
|
FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer := IntAllocRecordBuffer;
|
||||||
|
// Move only the real data
|
||||||
|
move(FCurrentIndex.CurrentBuffer^, FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer^, FRecordSize);
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
@ -3018,12 +3042,10 @@ procedure TCustomBufDataset.GetDatasetPacket(AWriter: TDataPacketReader);
|
|||||||
begin
|
begin
|
||||||
AStoreUpdBuf:=FCurrentUpdateBuffer;
|
AStoreUpdBuf:=FCurrentUpdateBuffer;
|
||||||
if GetRecordUpdateBuffer(AUpdBuffer.BookmarkData,True,False) then
|
if GetRecordUpdateBuffer(AUpdBuffer.BookmarkData,True,False) then
|
||||||
begin
|
|
||||||
repeat
|
repeat
|
||||||
if FCurrentIndex.SameBookmarks(@FUpdateBuffer[FCurrentUpdateBuffer].NextBookmarkData, @AUpdBuffer.BookmarkData) then
|
if FCurrentIndex.SameBookmarks(@FUpdateBuffer[FCurrentUpdateBuffer].NextBookmarkData, @AUpdBuffer.BookmarkData) then
|
||||||
StoreUpdateBuffer(FUpdateBuffer[FCurrentUpdateBuffer], ARowState);
|
StoreUpdateBuffer(FUpdateBuffer[FCurrentUpdateBuffer], ARowState);
|
||||||
until not GetRecordUpdateBuffer(AUpdBuffer.BookmarkData,True,True)
|
until not GetRecordUpdateBuffer(AUpdBuffer.BookmarkData,True,True);
|
||||||
end;
|
|
||||||
FCurrentUpdateBuffer:=AStoreUpdBuf;
|
FCurrentUpdateBuffer:=AStoreUpdBuf;
|
||||||
AThisRowState := [rsvDeleted];
|
AThisRowState := [rsvDeleted];
|
||||||
end
|
end
|
||||||
@ -3036,16 +3058,16 @@ procedure TCustomBufDataset.GetDatasetPacket(AWriter: TDataPacketReader);
|
|||||||
FDatasetReader.StoreRecord(AThisRowState,FCurrentUpdateBuffer);
|
FDatasetReader.StoreRecord(AThisRowState,FCurrentUpdateBuffer);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure HandleUpdateBuffersFromRecord(AFirstCall : boolean;ARecBookmark : TBufBookmark; var ARowState: TRowState);
|
procedure HandleUpdateBuffersFromRecord(AFindNext : boolean; ARecBookmark : TBufBookmark; var ARowState: TRowState);
|
||||||
var StoreUpdBuf1,StoreUpdBuf2 : Integer;
|
var StoreUpdBuf1,StoreUpdBuf2 : Integer;
|
||||||
begin
|
begin
|
||||||
if AFirstCall then ARowState:=[];
|
if not AFindNext then ARowState:=[];
|
||||||
if GetRecordUpdateBuffer(ARecBookmark,True,not AFirstCall) then
|
if GetRecordUpdateBuffer(ARecBookmark,True,AFindNext) then
|
||||||
begin
|
begin
|
||||||
if FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind=ukDelete then
|
if FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind=ukDelete then
|
||||||
begin
|
begin
|
||||||
StoreUpdBuf1:=FCurrentUpdateBuffer;
|
StoreUpdBuf1:=FCurrentUpdateBuffer;
|
||||||
HandleUpdateBuffersFromRecord(False,ARecBookmark,ARowState);
|
HandleUpdateBuffersFromRecord(True,ARecBookmark,ARowState);
|
||||||
StoreUpdBuf2:=FCurrentUpdateBuffer;
|
StoreUpdBuf2:=FCurrentUpdateBuffer;
|
||||||
FCurrentUpdateBuffer:=StoreUpdBuf1;
|
FCurrentUpdateBuffer:=StoreUpdBuf1;
|
||||||
StoreUpdateBuffer(FUpdateBuffer[StoreUpdBuf1], ARowState);
|
StoreUpdateBuffer(FUpdateBuffer[StoreUpdBuf1], ARowState);
|
||||||
@ -3054,7 +3076,7 @@ procedure TCustomBufDataset.GetDatasetPacket(AWriter: TDataPacketReader);
|
|||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
StoreUpdateBuffer(FUpdateBuffer[FCurrentUpdateBuffer], ARowState);
|
StoreUpdateBuffer(FUpdateBuffer[FCurrentUpdateBuffer], ARowState);
|
||||||
HandleUpdateBuffersFromRecord(False,ARecBookmark,ARowState);
|
HandleUpdateBuffersFromRecord(True,ARecBookmark,ARowState);
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
@ -3078,7 +3100,9 @@ begin
|
|||||||
begin
|
begin
|
||||||
RowState:=[];
|
RowState:=[];
|
||||||
FCurrentIndex.StoreCurrentRecIntoBookmark(ABookmark);
|
FCurrentIndex.StoreCurrentRecIntoBookmark(ABookmark);
|
||||||
HandleUpdateBuffersFromRecord(True,ABookmark^,RowState);
|
// updates related to current record are stored first
|
||||||
|
HandleUpdateBuffersFromRecord(False,ABookmark^,RowState);
|
||||||
|
// now store current record
|
||||||
FFilterBuffer:=FCurrentIndex.CurrentBuffer;
|
FFilterBuffer:=FCurrentIndex.CurrentBuffer;
|
||||||
if RowState=[] then
|
if RowState=[] then
|
||||||
FDatasetReader.StoreRecord([])
|
FDatasetReader.StoreRecord([])
|
||||||
@ -3094,7 +3118,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
// There could be an update buffer linked to the last (spare) record
|
// There could be an update buffer linked to the last (spare) record
|
||||||
FCurrentIndex.StoreSpareRecIntoBookmark(ABookmark);
|
FCurrentIndex.StoreSpareRecIntoBookmark(ABookmark);
|
||||||
HandleUpdateBuffersFromRecord(True,ABookmark^,RowState);
|
HandleUpdateBuffersFromRecord(False,ABookmark^,RowState);
|
||||||
|
|
||||||
RestoreState(SavedState);
|
RestoreState(SavedState);
|
||||||
|
|
||||||
@ -3233,10 +3257,9 @@ end;
|
|||||||
procedure TCustomBufDataset.IntLoadRecordsFromFile;
|
procedure TCustomBufDataset.IntLoadRecordsFromFile;
|
||||||
|
|
||||||
var SavedState : TDataSetState;
|
var SavedState : TDataSetState;
|
||||||
AddRecordBuffer : boolean;
|
|
||||||
ARowState : TRowState;
|
ARowState : TRowState;
|
||||||
AUpdOrder : integer;
|
AUpdOrder : integer;
|
||||||
x : integer;
|
i : integer;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
CheckBiDirectional;
|
CheckBiDirectional;
|
||||||
@ -3274,9 +3297,6 @@ begin
|
|||||||
FDatasetReader.RestoreRecord;
|
FDatasetReader.RestoreRecord;
|
||||||
FIndexes[0].AddRecord;
|
FIndexes[0].AddRecord;
|
||||||
inc(FBRecordCount);
|
inc(FBRecordCount);
|
||||||
|
|
||||||
AddRecordBuffer:=False;
|
|
||||||
|
|
||||||
end
|
end
|
||||||
else if rsvDeleted in ARowState then
|
else if rsvDeleted in ARowState then
|
||||||
begin
|
begin
|
||||||
@ -3297,16 +3317,11 @@ begin
|
|||||||
FIndexes[0].RemoveRecordFromIndex(FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
|
FIndexes[0].RemoveRecordFromIndex(FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
|
||||||
FIndexes[0].StoreSpareRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].NextBookmarkData);
|
FIndexes[0].StoreSpareRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].NextBookmarkData);
|
||||||
|
|
||||||
for x := FCurrentUpdateBuffer+1 to length(FUpdateBuffer)-1 do
|
for i := FCurrentUpdateBuffer+1 to high(FUpdateBuffer) do
|
||||||
if FIndexes[0].SameBookmarks(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData,@FUpdateBuffer[x].NextBookmarkData) then
|
if FIndexes[0].SameBookmarks(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData, @FUpdateBuffer[i].NextBookmarkData) then
|
||||||
FIndexes[0].StoreSpareRecIntoBookmark(@FUpdateBuffer[x].NextBookmarkData);
|
FIndexes[0].StoreSpareRecIntoBookmark(@FUpdateBuffer[i].NextBookmarkData);
|
||||||
|
|
||||||
AddRecordBuffer:=False;
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
AddRecordBuffer:=True;
|
|
||||||
|
|
||||||
if AddRecordBuffer then
|
|
||||||
begin
|
begin
|
||||||
FFilterBuffer:=FIndexes[0].SpareBuffer;
|
FFilterBuffer:=FIndexes[0].SpareBuffer;
|
||||||
fillchar(FFilterBuffer^,FNullmaskSize,0);
|
fillchar(FFilterBuffer^,FNullmaskSize,0);
|
||||||
|
@ -47,11 +47,11 @@ uses
|
|||||||
|
|
||||||
type
|
type
|
||||||
TIBBackupOption=(IBBkpVerbose,IBBkpIgnoreChecksums,IBBkpIgnoreLimbo,IBBkpMetadataOnly,
|
TIBBackupOption=(IBBkpVerbose,IBBkpIgnoreChecksums,IBBkpIgnoreLimbo,IBBkpMetadataOnly,
|
||||||
IBBkpNoGarbageCollect,IBBkpOldDescriptions,IBBkpNonTransportable,IBBkpConvert);
|
IBBkpNoGarbageCollect,IBBkpOldDescriptions,IBBkpNonTransportable,IBBkpConvert,IBBkpWait);
|
||||||
TIBBackupOptions= set of TIBBackupOption;
|
TIBBackupOptions= set of TIBBackupOption;
|
||||||
TIBRestoreOption=(IBResVerbose,IBResDeactivateIdx,IBResNoShadow,IBResNoValidity,
|
TIBRestoreOption=(IBResVerbose,IBResDeactivateIdx,IBResNoShadow,IBResNoValidity,
|
||||||
IBResOneAtaTime,IBResReplace,IBResCreate,IBResUseAllSpace,IBResAMReadOnly,IBResAMReadWrite,
|
IBResOneAtaTime,IBResReplace,IBResCreate,IBResUseAllSpace,IBResAMReadOnly,IBResAMReadWrite,
|
||||||
IBFixFssData, IBFixFssMeta);
|
IBFixFssData, IBFixFssMeta,IBResWait);
|
||||||
TIBRestoreOptions= set of TIBRestoreOption;
|
TIBRestoreOptions= set of TIBRestoreOption;
|
||||||
TServiceProtocol=(IBSPLOCAL,IBSPTCPIP,IBSPNETBEUI,IBSPNAMEDPIPE);
|
TServiceProtocol=(IBSPLOCAL,IBSPTCPIP,IBSPNETBEUI,IBSPNAMEDPIPE);
|
||||||
TIBOnOutput= procedure(Sender: TObject; msg: string; IBAdminAction: string) of object;
|
TIBOnOutput= procedure(Sender: TObject; msg: string; IBAdminAction: string) of object;
|
||||||
@ -82,6 +82,7 @@ type
|
|||||||
FSvcHandle: isc_svc_handle;
|
FSvcHandle: isc_svc_handle;
|
||||||
FUseExceptions: boolean;
|
FUseExceptions: boolean;
|
||||||
FUser: string;
|
FUser: string;
|
||||||
|
FWaitInterval: Integer;
|
||||||
function CheckConnected(ProcName: string):boolean;
|
function CheckConnected(ProcName: string):boolean;
|
||||||
procedure CheckError(ProcName : string; Status : PISC_STATUS);
|
procedure CheckError(ProcName : string; Status : PISC_STATUS);
|
||||||
function GetDBInfo:boolean;
|
function GetDBInfo:boolean;
|
||||||
@ -94,7 +95,6 @@ type
|
|||||||
function IBSPBParamSerialize(isccode:byte;value:longint):string;
|
function IBSPBParamSerialize(isccode:byte;value:longint):string;
|
||||||
function MakeBackupOptions(options:TIBBackupOptions):longint;
|
function MakeBackupOptions(options:TIBBackupOptions):longint;
|
||||||
function MakeRestoreOptions(options:TIBRestoreOptions):longint;
|
function MakeRestoreOptions(options:TIBRestoreOptions):longint;
|
||||||
|
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TComponent); override;
|
constructor Create(AOwner: TComponent); override;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
@ -140,6 +140,12 @@ type
|
|||||||
function GetUsers(Users:TStrings):boolean;
|
function GetUsers(Users:TStrings):boolean;
|
||||||
//Get database server log file
|
//Get database server log file
|
||||||
function GetDatabaseLog:boolean;
|
function GetDatabaseLog:boolean;
|
||||||
|
// For Backup, Restore this will check if the service call is still running.
|
||||||
|
function ServiceRunning: Boolean;
|
||||||
|
// Wait till the service stops running, or until aTimeout (in milliseconds) is reached.
|
||||||
|
// Return true if the service stopped, false if timeout reached.
|
||||||
|
// WaitInterval is the interval (in milliseconds) between ServiceRunning calls.
|
||||||
|
function WaitForServiceCompletion(aTimeOut: Integer): Boolean;
|
||||||
//Get database statistics
|
//Get database statistics
|
||||||
function GetDatabaseStats(Database:string;Options:TIBStatOptions;TableNames:String = ''): boolean;
|
function GetDatabaseStats(Database:string;Options:TIBStatOptions;TableNames:String = ''): boolean;
|
||||||
//Database server version
|
//Database server version
|
||||||
@ -183,11 +189,15 @@ type
|
|||||||
//Event handler for Service output messages
|
//Event handler for Service output messages
|
||||||
//Used in Backup and Restore operations and GetLog
|
//Used in Backup and Restore operations and GetLog
|
||||||
property OnOutput: TIBOnOutput read FOnOutput write FOnOutput;
|
property OnOutput: TIBOnOutput read FOnOutput write FOnOutput;
|
||||||
|
// Interval (in milliseconds) to sleep while waiting for the service operation to end.
|
||||||
|
Property WaitInterval : Integer Read FWaitInterval Write FWaitInterval;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
|
uses dateutils;
|
||||||
|
|
||||||
resourcestring
|
resourcestring
|
||||||
SErrNotConnected = '%s : %s : Not connected.';
|
SErrNotConnected = '%s : %s : Not connected.';
|
||||||
SErrError = '%s : %s : %s';
|
SErrError = '%s : %s : %s';
|
||||||
@ -383,6 +393,7 @@ end;
|
|||||||
destructor TFBAdmin.Destroy;
|
destructor TFBAdmin.Destroy;
|
||||||
begin
|
begin
|
||||||
if FSvcHandle<>FB_API_NULLHANDLE then
|
if FSvcHandle<>FB_API_NULLHANDLE then
|
||||||
|
WaitInterval:=100;
|
||||||
DisConnect;
|
DisConnect;
|
||||||
FOutput.Destroy;
|
FOutput.Destroy;
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
@ -454,7 +465,9 @@ begin
|
|||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
if IBBkpVerbose in Options then
|
if IBBkpVerbose in Options then
|
||||||
result:=GetOutput('Backup');
|
result:=GetOutput('Backup')
|
||||||
|
else if (IBBkpWait in Options) then
|
||||||
|
WaitForServiceCompletion(0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TFBAdmin.BackupMultiFile(Database: string; Filenames: TStrings;
|
function TFBAdmin.BackupMultiFile(Database: string; Filenames: TStrings;
|
||||||
@ -483,9 +496,52 @@ begin
|
|||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
if IBBkpVerbose in Options then
|
if IBBkpVerbose in Options then
|
||||||
result:=GetOutput('BackupMultiFile');
|
result:=GetOutput('BackupMultiFile')
|
||||||
|
else if (IBBkpWait in Options) then
|
||||||
|
WaitForServiceCompletion(0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
Function TFBAdmin.ServiceRunning : Boolean;
|
||||||
|
|
||||||
|
const
|
||||||
|
BUFFERSIZE=1000;
|
||||||
|
|
||||||
|
var
|
||||||
|
res:integer;
|
||||||
|
buffer: string;
|
||||||
|
spb:string;
|
||||||
|
|
||||||
|
begin
|
||||||
|
FOutput.Clear;
|
||||||
|
spb:=chr(isc_info_svc_running);
|
||||||
|
setlength(buffer,BUFFERSIZE);
|
||||||
|
result:=isc_service_query(@FStatus[0], @FSvcHandle, nil, 0, nil, length(spb),
|
||||||
|
@spb[1],BUFFERSIZE,@buffer[1])=0;
|
||||||
|
if Not Result then
|
||||||
|
CheckError('ServiceRunning',FSTatus);
|
||||||
|
if (Buffer[1]=Char(isc_info_svc_running)) then
|
||||||
|
begin
|
||||||
|
res:=isc_vax_integer(@Buffer[2],4);
|
||||||
|
Result:=res=1;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
IBRaiseError(0,'%s: Service status detection returned wrong result',[self.Name]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Function TFBAdmin.WaitForServiceCompletion(aTimeOut : Integer) : Boolean;
|
||||||
|
|
||||||
|
Var
|
||||||
|
N : TDateTime;
|
||||||
|
|
||||||
|
begin
|
||||||
|
N:=Now;
|
||||||
|
Repeat
|
||||||
|
Sleep(WaitInterval);
|
||||||
|
Result:=not ServiceRunning;
|
||||||
|
until Result or ((aTimeOut<>0) and (MilliSecondsBetween(Now,N)>aTimeOut*WaitInterval));
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
function TFBAdmin.Restore(Database, Filename: string;
|
function TFBAdmin.Restore(Database, Filename: string;
|
||||||
Options: TIBRestoreOptions; RoleName: string): boolean;
|
Options: TIBRestoreOptions; RoleName: string): boolean;
|
||||||
var
|
var
|
||||||
@ -524,7 +580,9 @@ begin
|
|||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
if IBResVerbose in Options then
|
if IBResVerbose in Options then
|
||||||
result:=GetOutput('Restore');
|
result:=GetOutput('Restore')
|
||||||
|
else if IBResWait in Options then
|
||||||
|
WaitForServiceCompletion(0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
@ -154,8 +154,10 @@ Type
|
|||||||
function RefreshLastInsertID(Query : TCustomSQLQuery; Field : TField): Boolean; override;
|
function RefreshLastInsertID(Query : TCustomSQLQuery; Field : TField): Boolean; override;
|
||||||
Public
|
Public
|
||||||
constructor Create(AOwner : TComponent); override;
|
constructor Create(AOwner : TComponent); override;
|
||||||
|
{$IFNDEF MYSQL50_UP}
|
||||||
procedure GetFieldNames(const TableName : string; List : TStrings); override;
|
procedure GetFieldNames(const TableName : string; List : TStrings); override;
|
||||||
procedure GetTableNames(List : TStrings; SystemTables : Boolean = false); override;
|
procedure GetTableNames(List : TStrings; SystemTables : Boolean = false); override;
|
||||||
|
{$ENDIF}
|
||||||
function GetConnectionInfo(InfoType:TConnInfoType): string; override;
|
function GetConnectionInfo(InfoType:TConnInfoType): string; override;
|
||||||
Function GetInsertID: int64;
|
Function GetInsertID: int64;
|
||||||
procedure CreateDB; override;
|
procedure CreateDB; override;
|
||||||
@ -1199,6 +1201,7 @@ begin
|
|||||||
FMySQL := Nil;
|
FMySQL := Nil;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{$IFNDEF MYSQL50_UP}
|
||||||
procedure TConnectionName.GetFieldNames(const TableName: string; List: TStrings);
|
procedure TConnectionName.GetFieldNames(const TableName: string; List: TStrings);
|
||||||
begin
|
begin
|
||||||
GetDBInfo(stColumns,TableName,'field',List);
|
GetDBInfo(stColumns,TableName,'field',List);
|
||||||
@ -1208,6 +1211,7 @@ procedure TConnectionName.GetTableNames(List: TStrings; SystemTables: Boolean);
|
|||||||
begin
|
begin
|
||||||
GetDBInfo(stTables,'','tables_in_'+DatabaseName,List)
|
GetDBInfo(stTables,'','tables_in_'+DatabaseName,List)
|
||||||
end;
|
end;
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
function TConnectionName.GetConnectionInfo(InfoType: TConnInfoType): string;
|
function TConnectionName.GetConnectionInfo(InfoType: TConnInfoType): string;
|
||||||
begin
|
begin
|
||||||
@ -1294,13 +1298,19 @@ function TConnectionName.GetSchemaInfoSQL(SchemaType: TSchemaType;
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
case SchemaType of
|
case SchemaType of
|
||||||
|
{$IFDEF MYSQL50_UP}
|
||||||
|
stTables : result := 'SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=SCHEMA() AND TABLE_TYPE IN (''BASE TABLE'',''VIEW'')';
|
||||||
|
stColumns : result := 'SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=SCHEMA() AND TABLE_NAME='+QuotedStr(SchemaObjectName);
|
||||||
|
{$ELSE}
|
||||||
stTables : result := 'show tables';
|
stTables : result := 'show tables';
|
||||||
stColumns : result := 'show columns from ' + EscapeString(SchemaObjectName);
|
stColumns : result := 'show columns from ' + EscapeString(SchemaObjectName);
|
||||||
|
{$ENDIF}
|
||||||
else
|
else
|
||||||
DatabaseError(SMetadataUnavailable)
|
result := inherited;
|
||||||
end; {case}
|
end; {case}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TMySQLConnectionDef }
|
{ TMySQLConnectionDef }
|
||||||
|
|
||||||
class function TMySQLConnectionDef.TypeName: String;
|
class function TMySQLConnectionDef.TypeName: String;
|
||||||
|
@ -578,6 +578,8 @@ type
|
|||||||
property AfterCancel;
|
property AfterCancel;
|
||||||
property BeforeDelete;
|
property BeforeDelete;
|
||||||
property AfterDelete;
|
property AfterDelete;
|
||||||
|
property BeforeRefresh;
|
||||||
|
property AfterRefresh;
|
||||||
property BeforeScroll;
|
property BeforeScroll;
|
||||||
property AfterScroll;
|
property AfterScroll;
|
||||||
property OnCalcFields;
|
property OnCalcFields;
|
||||||
@ -630,6 +632,7 @@ type
|
|||||||
Property AfterInsert;
|
Property AfterInsert;
|
||||||
Property AfterOpen;
|
Property AfterOpen;
|
||||||
Property AfterPost;
|
Property AfterPost;
|
||||||
|
Property AfterRefresh;
|
||||||
Property AfterScroll;
|
Property AfterScroll;
|
||||||
Property BeforeCancel;
|
Property BeforeCancel;
|
||||||
Property BeforeClose;
|
Property BeforeClose;
|
||||||
@ -638,6 +641,7 @@ type
|
|||||||
Property BeforeInsert;
|
Property BeforeInsert;
|
||||||
Property BeforeOpen;
|
Property BeforeOpen;
|
||||||
Property BeforePost;
|
Property BeforePost;
|
||||||
|
Property BeforeRefresh;
|
||||||
Property BeforeScroll;
|
Property BeforeScroll;
|
||||||
Property OnCalcFields;
|
Property OnCalcFields;
|
||||||
Property OnDeleteError;
|
Property OnDeleteError;
|
||||||
@ -1984,6 +1988,8 @@ function TSQLConnection.GetSchemaInfoSQL( SchemaType : TSchemaType; SchemaObject
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
case SchemaType of
|
case SchemaType of
|
||||||
|
stTables : Result := 'SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE=''BASE TABLE''';
|
||||||
|
stColumns : Result := 'SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='+QuotedStr(SchemaObjectName);
|
||||||
stProcedures: Result := 'SELECT *, ROUTINE_NAME AS PROCEDURE_NAME FROM INFORMATION_SCHEMA.ROUTINES';
|
stProcedures: Result := 'SELECT *, ROUTINE_NAME AS PROCEDURE_NAME FROM INFORMATION_SCHEMA.ROUTINES';
|
||||||
stSchemata : Result := 'SELECT * FROM INFORMATION_SCHEMA.SCHEMATA';
|
stSchemata : Result := 'SELECT * FROM INFORMATION_SCHEMA.SCHEMATA';
|
||||||
stSequences : Result := 'SELECT * FROM INFORMATION_SCHEMA.SEQUENCES';
|
stSequences : Result := 'SELECT * FROM INFORMATION_SCHEMA.SEQUENCES';
|
||||||
|
@ -358,6 +358,11 @@ begin
|
|||||||
testStringValues[i] := TrimRight(testStringValues[i]);
|
testStringValues[i] := TrimRight(testStringValues[i]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
if SQLServerType in [ssMSSQL, ssSQLite, ssSybase] then
|
||||||
|
// Some DB's do not support sql compliant boolean data type.
|
||||||
|
for i := 0 to testValuesCount-1 do
|
||||||
|
testValues[ftBoolean, i] := BoolToStr(testBooleanValues[i], '1', '0');
|
||||||
|
|
||||||
if SQLServerType in [ssMySQL] then
|
if SQLServerType in [ssMySQL] then
|
||||||
begin
|
begin
|
||||||
// Some DB's do not support milliseconds in datetime and time fields.
|
// Some DB's do not support milliseconds in datetime and time fields.
|
||||||
@ -499,46 +504,35 @@ begin
|
|||||||
begin
|
begin
|
||||||
sql := sql + ',F' + Fieldtypenames[FType];
|
sql := sql + ',F' + Fieldtypenames[FType];
|
||||||
if testValues[FType,CountID] <> '' then
|
if testValues[FType,CountID] <> '' then
|
||||||
case FType of
|
if FType in [ftBoolean, ftCurrency] then
|
||||||
ftBlob, ftBytes, ftGraphic, ftVarBytes:
|
sql1 := sql1 + ',' + testValues[FType,CountID]
|
||||||
if SQLServerType in [ssOracle] then
|
else if (FType in [ftBlob, ftBytes, ftGraphic, ftVarBytes]) and
|
||||||
// Oracle does not accept string literals in blob insert statements
|
(SQLServerType = ssOracle) then
|
||||||
// convert 'DEADBEEF' hex literal to binary:
|
// Oracle does not accept string literals in blob insert statements
|
||||||
sql1 := sql1 + ', HEXTORAW(' + QuotedStr(String2Hex(testValues[FType,CountID])) + ') '
|
// convert 'DEADBEEF' hex literal to binary:
|
||||||
else // other dbs have no problems with the original string values
|
sql1 := sql1 + ', HEXTORAW(' + QuotedStr(String2Hex(testValues[FType,CountID])) + ') '
|
||||||
sql1 := sql1 + ',' + QuotedStr(testValues[FType,CountID]);
|
else if (FType = ftDate) and
|
||||||
ftCurrency:
|
(SQLServerType = ssOracle) then
|
||||||
sql1 := sql1 + ',' + testValues[FType,CountID];
|
// Oracle requires date conversion; otherwise
|
||||||
ftDate:
|
// ORA-01861: literal does not match format string
|
||||||
// Oracle requires date conversion; otherwise
|
// ANSI/ISO date literal:
|
||||||
// ORA-01861: literal does not match format string
|
sql1 := sql1 + ', DATE ' + QuotedStr(testValues[FType,CountID])
|
||||||
if SQLServerType in [ssOracle] then
|
else if (FType = ftDateTime) and
|
||||||
// ANSI/ISO date literal:
|
(SQLServerType = ssOracle) then begin
|
||||||
sql1 := sql1 + ', DATE ' + QuotedStr(testValues[FType,CountID])
|
// similar to ftDate handling
|
||||||
else
|
// Could be a real date+time or only date. Does not consider only time.
|
||||||
sql1 := sql1 + ',' + QuotedStr(testValues[FType,CountID]);
|
if pos(' ',testValues[FType,CountID])>0 then
|
||||||
ftDateTime:
|
sql1 := sql1 + ', TIMESTAMP ' + QuotedStr(testValues[FType,CountID])
|
||||||
// similar to ftDate handling
|
else
|
||||||
if SQLServerType in [ssOracle] then
|
sql1 := sql1 + ', DATE ' + QuotedStr(testValues[FType,CountID]);
|
||||||
begin
|
|
||||||
// Could be a real date+time or only date. Does not consider only time.
|
|
||||||
if pos(' ',testValues[FType,CountID])>0 then
|
|
||||||
sql1 := sql1 + ', TIMESTAMP ' + QuotedStr(testValues[FType,CountID])
|
|
||||||
else
|
|
||||||
sql1 := sql1 + ', DATE ' + QuotedStr(testValues[FType,CountID]);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
sql1 := sql1 + ',' + QuotedStr(testValues[FType,CountID]);
|
|
||||||
ftTime:
|
|
||||||
// similar to ftDate handling
|
|
||||||
if SQLServerType in [ssOracle] then
|
|
||||||
// More or less arbitrary default time; there is no time-only data type in Oracle.
|
|
||||||
sql1 := sql1 + ', TIMESTAMP ' + QuotedStr('0001-01-01 '+testValues[FType,CountID])
|
|
||||||
else
|
|
||||||
sql1 := sql1 + ',' + QuotedStr(testValues[FType,CountID]);
|
|
||||||
else
|
|
||||||
sql1 := sql1 + ',' + QuotedStr(testValues[FType,CountID])
|
|
||||||
end
|
end
|
||||||
|
else if (FType = ftTime) and
|
||||||
|
(SQLServerType = ssOracle) then
|
||||||
|
// similar to ftDate handling
|
||||||
|
// More or less arbitrary default time; there is no time-only data type in Oracle.
|
||||||
|
sql1 := sql1 + ', TIMESTAMP ' + QuotedStr('0001-01-01 '+testValues[FType,CountID])
|
||||||
|
else
|
||||||
|
sql1 := sql1 + ',' + QuotedStr(testValues[FType,CountID])
|
||||||
else
|
else
|
||||||
sql1 := sql1 + ',NULL';
|
sql1 := sql1 + ',NULL';
|
||||||
end;
|
end;
|
||||||
|
@ -88,6 +88,7 @@ type
|
|||||||
procedure TestMultipleDeleteUpdateBuffer;
|
procedure TestMultipleDeleteUpdateBuffer;
|
||||||
procedure TestDoubleDelete;
|
procedure TestDoubleDelete;
|
||||||
procedure TestMergeChangeLog;
|
procedure TestMergeChangeLog;
|
||||||
|
procedure TestRevertRecord;
|
||||||
// index tests
|
// index tests
|
||||||
procedure TestAddIndexInteger;
|
procedure TestAddIndexInteger;
|
||||||
procedure TestAddIndexSmallInt;
|
procedure TestAddIndexSmallInt;
|
||||||
@ -1231,6 +1232,7 @@ begin
|
|||||||
begin
|
begin
|
||||||
Open;
|
Open;
|
||||||
|
|
||||||
|
// modify records
|
||||||
for i := 0 to 16 do
|
for i := 0 to 16 do
|
||||||
begin
|
begin
|
||||||
if i mod 4=0 then
|
if i mod 4=0 then
|
||||||
@ -1242,19 +1244,21 @@ begin
|
|||||||
next;
|
next;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
for i := 17 to 20 do
|
// append new records
|
||||||
|
for i := 18 to 21 do
|
||||||
begin
|
begin
|
||||||
append;
|
append;
|
||||||
fieldbyname('id').AsInteger:=i+1;
|
fieldbyname('id').AsInteger:=i;
|
||||||
fieldbyname('name').AsString:='TestName'+inttostr(i+1);
|
fieldbyname('name').AsString:='TestName'+inttostr(i);
|
||||||
post;
|
post;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// delete records #1,5,9,13,17,21 which was modified or appended before
|
||||||
first;
|
first;
|
||||||
for i := 0 to 20 do if i mod 4=0 then
|
for i := 0 to 20 do if i mod 4=0 then
|
||||||
delete
|
delete
|
||||||
else
|
else
|
||||||
next;
|
next;
|
||||||
|
|
||||||
First;
|
First;
|
||||||
i := 0;
|
i := 0;
|
||||||
@ -1279,10 +1283,10 @@ begin
|
|||||||
CancelUpdates;
|
CancelUpdates;
|
||||||
|
|
||||||
First;
|
First;
|
||||||
for i := 0 to 16 do
|
for i := 1 to 17 do
|
||||||
begin
|
begin
|
||||||
CheckEquals(i+1,FieldByName('ID').AsInteger);
|
CheckEquals(i, FieldByName('ID').AsInteger);
|
||||||
CheckEquals('TestName'+inttostr(i+1),FieldByName('NAME').AsString);
|
CheckEquals('TestName'+inttostr(i), FieldByName('NAME').AsString);
|
||||||
next;
|
next;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -1785,6 +1789,77 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TTestBufDatasetDBBasics.TestRevertRecord;
|
||||||
|
begin
|
||||||
|
with DBConnector.GetNDataset(True,1) as TCustomBufDataset do
|
||||||
|
begin
|
||||||
|
Open;
|
||||||
|
// update value in one record and revert them
|
||||||
|
Edit;
|
||||||
|
FieldByName('ID').AsInteger := 100;
|
||||||
|
Post;
|
||||||
|
CheckEquals(100, FieldByName('ID').AsInteger);
|
||||||
|
RevertRecord;
|
||||||
|
CheckEquals(1, FieldByName('ID').AsInteger, 'Revert modified #1');
|
||||||
|
// append new record and delete prior and revert appended
|
||||||
|
AppendRecord([3,'']);
|
||||||
|
InsertRecord([2,'']);
|
||||||
|
Prior;
|
||||||
|
Delete; // 1st
|
||||||
|
Next;
|
||||||
|
RevertRecord; // 3rd
|
||||||
|
CheckEquals(2, FieldByName('ID').AsInteger, 'Revert inserted #1a');
|
||||||
|
RevertRecord; // 2nd
|
||||||
|
CheckTrue(Eof, 'Revert inserted #1b');
|
||||||
|
CancelUpdates; // restores 1st deleted record
|
||||||
|
CheckEquals(1, FieldByName('ID').AsInteger, 'CancelUpdates #1');
|
||||||
|
Close;
|
||||||
|
end;
|
||||||
|
|
||||||
|
with DBConnector.GetNDataset(False,0) as TCustomBufDataset do
|
||||||
|
begin
|
||||||
|
Open;
|
||||||
|
// insert one record and revert them
|
||||||
|
InsertRecord([1,'']);
|
||||||
|
RevertRecord;
|
||||||
|
CheckTrue(Eof);
|
||||||
|
CheckEquals(0, ChangeCount);
|
||||||
|
|
||||||
|
// insert two records and revert them in inverse order
|
||||||
|
AppendRecord([2,'']);
|
||||||
|
InsertRecord([1,'']); // this record in update-buffer is linked to 2
|
||||||
|
RevertRecord;
|
||||||
|
CheckEquals(2, FieldByName('ID').AsInteger);
|
||||||
|
CheckEquals(1, ChangeCount);
|
||||||
|
RevertRecord;
|
||||||
|
CheckTrue(Eof);
|
||||||
|
CheckEquals(0, ChangeCount);
|
||||||
|
|
||||||
|
// insert more records and some delete and some revert
|
||||||
|
AppendRecord([4,'']);
|
||||||
|
InsertRecord([3,'']);
|
||||||
|
InsertRecord([2,'']);
|
||||||
|
InsertRecord([1,'']);
|
||||||
|
CheckEquals(4, ChangeCount);
|
||||||
|
Delete; // 1
|
||||||
|
CheckEquals(4, ChangeCount);
|
||||||
|
Next; // 3
|
||||||
|
RevertRecord;
|
||||||
|
CheckEquals(4, FieldByName('ID').AsInteger);
|
||||||
|
CheckEquals(3, ChangeCount);
|
||||||
|
Prior; // 2
|
||||||
|
RevertRecord;
|
||||||
|
CheckEquals(4, FieldByName('ID').AsInteger);
|
||||||
|
CheckEquals(2, ChangeCount);
|
||||||
|
|
||||||
|
CancelUpdates;
|
||||||
|
CheckTrue(Eof);
|
||||||
|
CheckEquals(0, ChangeCount);
|
||||||
|
|
||||||
|
Close;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TTestBufDatasetDBBasics.FTestXMLDatasetDefinition(ADataset: TDataset);
|
procedure TTestBufDatasetDBBasics.FTestXMLDatasetDefinition(ADataset: TDataset);
|
||||||
var i : integer;
|
var i : integer;
|
||||||
begin
|
begin
|
||||||
|
@ -528,8 +528,6 @@ end;
|
|||||||
|
|
||||||
procedure InitialiseDBConnector;
|
procedure InitialiseDBConnector;
|
||||||
|
|
||||||
const B: array[boolean] of char=('0','1'); // should be exported from some main db unit, as SQL true/false?
|
|
||||||
|
|
||||||
var DBConnectorClass : TPersistentClass;
|
var DBConnectorClass : TPersistentClass;
|
||||||
i : integer;
|
i : integer;
|
||||||
FormatSettings : TFormatSettings;
|
FormatSettings : TFormatSettings;
|
||||||
@ -550,7 +548,7 @@ begin
|
|||||||
testValues[ftFMTBcd] := testFmtBCDValues;
|
testValues[ftFMTBcd] := testFmtBCDValues;
|
||||||
for i := 0 to testValuesCount-1 do
|
for i := 0 to testValuesCount-1 do
|
||||||
begin
|
begin
|
||||||
testValues[ftBoolean,i] := B[testBooleanValues[i]];
|
testValues[ftBoolean,i] := BoolToStr(testBooleanValues[i], True);
|
||||||
testValues[ftFloat,i] := FloatToStr(testFloatValues[i],FormatSettings);
|
testValues[ftFloat,i] := FloatToStr(testFloatValues[i],FormatSettings);
|
||||||
testValues[ftSmallint,i] := IntToStr(testSmallIntValues[i]);
|
testValues[ftSmallint,i] := IntToStr(testSmallIntValues[i]);
|
||||||
testValues[ftInteger,i] := IntToStr(testIntValues[i]);
|
testValues[ftInteger,i] := IntToStr(testIntValues[i]);
|
||||||
|
@ -233,6 +233,8 @@ var
|
|||||||
{ === in fe-auth.c === }
|
{ === in fe-auth.c === }
|
||||||
PQencryptPassword : function (passwd:Pcchar; user:Pcchar):Pcchar;cdecl;
|
PQencryptPassword : function (passwd:Pcchar; user:Pcchar):Pcchar;cdecl;
|
||||||
|
|
||||||
|
{ === in encnames.c === }
|
||||||
|
pg_encoding_to_char: function (encoding:cint):Pcchar;cdecl;
|
||||||
|
|
||||||
Function InitialisePostgres3(Const libpath : ansistring) : integer;
|
Function InitialisePostgres3(Const libpath : ansistring) : integer;
|
||||||
Procedure InitialisePostgres3;
|
Procedure InitialisePostgres3;
|
||||||
@ -398,6 +400,7 @@ begin
|
|||||||
pointer(PQmblen) := GetProcedureAddress(Postgres3LibraryHandle,'PQmblen');
|
pointer(PQmblen) := GetProcedureAddress(Postgres3LibraryHandle,'PQmblen');
|
||||||
pointer(PQenv2encoding) := GetProcedureAddress(Postgres3LibraryHandle,'PQenv2encoding');
|
pointer(PQenv2encoding) := GetProcedureAddress(Postgres3LibraryHandle,'PQenv2encoding');
|
||||||
pointer(PQencryptPassword) := GetProcedureAddress(Postgres3LibraryHandle,'PQencryptPassword');
|
pointer(PQencryptPassword) := GetProcedureAddress(Postgres3LibraryHandle,'PQencryptPassword');
|
||||||
|
pointer(pg_encoding_to_char) := GetProcedureAddress(Postgres3LibraryHandle,'pg_encoding_to_char');
|
||||||
|
|
||||||
InitialiseDllist(libpath);
|
InitialiseDllist(libpath);
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user