--- 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:
marco 2017-04-28 12:38:15 +00:00
parent f34993a217
commit c6ad797d22
8 changed files with 313 additions and 154 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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';

View File

@ -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;

View File

@ -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

View File

@ -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]);

View File

@ -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