
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5438 8e941d3f-bd1b-0410-a28a-d453659cc2b4
617 lines
22 KiB
ObjectPascal
617 lines
22 KiB
ObjectPascal
{*********************************************************}
|
|
{* FlashFiler: Table access - general & helper routines *}
|
|
{*********************************************************}
|
|
|
|
(* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is TurboPower FlashFiler
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* TurboPower Software
|
|
*
|
|
* Portions created by the Initial Developer are Copyright (C) 1996-2002
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* ***** END LICENSE BLOCK ***** *)
|
|
|
|
{$I ffdefine.inc}
|
|
|
|
unit fftbbase;
|
|
|
|
interface
|
|
|
|
uses
|
|
Windows,
|
|
SysUtils,
|
|
Classes,
|
|
ffconst,
|
|
ffllbase,
|
|
ffsrlock,
|
|
ffsrmgr,
|
|
ffllexcp,
|
|
ffsrbase,
|
|
fffile,
|
|
ffsrintf,
|
|
fflldict;
|
|
|
|
{---Field comparison routine---}
|
|
function FFKeyCompareField(const Key1, Key2;
|
|
FieldType : TffFieldType;
|
|
FieldLen : Integer;
|
|
NoCase : Boolean) : integer;
|
|
{-Treat Key1 and Key2 as Filer typed fields, compare}
|
|
|
|
{---Table helper routines---}
|
|
function FFTblHlpGetNewBlock(aFI : PffFileInfo;
|
|
aTI : PffTransInfo;
|
|
var aReleaseMethod : TffReleaseMethod)
|
|
: PffBlock;
|
|
{-Return a new block, pre-marked as dirty. Exclusively locks the
|
|
file header block and recycled block (if one is available). }
|
|
procedure FFTblHlpDelete(const aPath : TffPath;
|
|
const aTableName : TffTableName;
|
|
const aDict : TffDataDictionary);
|
|
{-Delete all files associated with a table}
|
|
procedure FFTblHlpDeleteBlock(aFI : PffFileInfo;
|
|
aFileHeader : PffBlockHeaderFile;
|
|
aBlock : PffBlock);
|
|
{-Delete the block, add it to the deleted block chain. Assumes the block
|
|
has been exclusively locked. }
|
|
|
|
procedure FFTblHlpRename(const aPath : TffPath;
|
|
const aTableName : TffTableName;
|
|
const aNewName : TffTableName;
|
|
const aDict : TffDataDictionary);
|
|
{-Renames all files associated with a table}
|
|
|
|
{---Buffer manager access routines---}
|
|
function FFBMAddBlock(aFI : PffFileInfo;
|
|
aTI : PffTransInfo;
|
|
const aBlockNum : TffWord32;
|
|
var aReleaseMethod : TffReleaseMethod) : PffBlock;
|
|
{-Return address of a new block}
|
|
procedure FFBMDirtyBlock(aFI : PffFileInfo;
|
|
const aBlockNum : TffWord32;
|
|
aTI : PffTransInfo;
|
|
var aModifiableBlock : PffBlock);
|
|
{-Mark a block dirty. The block *must* be in the buffer already.
|
|
Returns a pointer to the modifiable copy of the block in output variable
|
|
aModifiableBlock. The calling function *MUST*
|
|
use the modifiable copy instead of the read-only copy it currently
|
|
possesses. }
|
|
function FFBMGetBlock(aFI : PffFileInfo;
|
|
aTI : PffTransInfo;
|
|
const aBlockNum : TffWord32;
|
|
const aMarkDirty : Boolean;
|
|
var aReleaseMethod : TffReleaseMethod) : PffBlock;
|
|
{-Retrieves the specified file block. If aMarkDirty is False then returns
|
|
a pointer to the read-only copy of the block. If aMarkDirty is True then
|
|
returns a modifiable copy of the block. }
|
|
function FFBMGetFileHeaderBlock(aFI : PffFileInfo;
|
|
aTI : PffTransInfo;
|
|
const aMarkDirty : Boolean;
|
|
var aReleaseMethod : TffReleaseMethod) : PffBlock;
|
|
{-Reads a file header (block 0) into buffer, returns address.
|
|
Note: this routine verifies the header block to be a valid header
|
|
block, so it must be used IMMEDIATELY after opening a file;
|
|
also sets the block size for the file}
|
|
procedure FFBMRemoveFile(aFI : PffFileInfo);
|
|
{-Mark a file's blocks in the buffer manager as available for reuse}
|
|
procedure FFBMUnlockBlock(aFI : PffFileInfo; aBlockNum : TffWord32);
|
|
{-Unlock a block making it available for reuse immediately.}
|
|
|
|
{---Lock manager access routines---}
|
|
procedure FFAcqRecordLock(aFI : PffFileInfo;
|
|
aTI : PffTransInfo;
|
|
const aRefNum : TffInt64;
|
|
const aLockType : TffSrLockType;
|
|
const aDatabaseID : TffDatabaseID; {!!.10}
|
|
const aCursorID : TffCursorID; {!!.02}
|
|
const aConditional : Boolean); {!!.02}
|
|
{ Use this procedure to obtain a lock on a record. If the lock is not
|
|
granted then an exception is raised. }
|
|
|
|
{Begin !!.10}
|
|
procedure FFRelaxRecordLock(aFI : PffFileInfo;
|
|
aTI : PffTransInfo;
|
|
const aCursorID : TffCursorID;
|
|
const aRefNum : TffInt64);
|
|
{ Used by data modification operations to make a modified record available
|
|
to other cursors within the same transaction. }
|
|
{End !!.10}
|
|
|
|
procedure FFRelRecordLock(aFI : PffFileInfo;
|
|
aTI : PffTransInfo;
|
|
const aRefNum : TffInt64;
|
|
const aDatabaseID : TffDatabaseID; {!!.10}
|
|
const aCursorID : TffCursorID);
|
|
{ Use this procedure to release an existing record lock. }
|
|
|
|
implementation
|
|
|
|
uses {!!.11}
|
|
FFSrEng; {!!.11}
|
|
|
|
resourcestring
|
|
ffcRecord = 'record %d:%d (high:low)';
|
|
|
|
{== Field comparison routine =========================================}
|
|
function FFKeyCompareField(const Key1, Key2;
|
|
FieldType : TffFieldType;
|
|
FieldLen : Integer;
|
|
NoCase : Boolean)
|
|
: Integer;
|
|
var
|
|
CompareData : TffCompareData;
|
|
Ch1, Ch2 : AnsiChar;
|
|
begin
|
|
CompareData.cdPartLen := 0;
|
|
{we'll use an ascending type comparison here, our caller will flip
|
|
the sign of our result if required}
|
|
CompareData.cdAscend := True;
|
|
CompareData.cdNoCase := NoCase;
|
|
case FieldType of
|
|
fftBoolean :
|
|
begin
|
|
{assumption: True is greater than False}
|
|
if boolean(Key1) then
|
|
if boolean(Key2) then Result := 0
|
|
else Result := 1
|
|
else
|
|
if boolean(Key2) then Result := -1
|
|
else Result := 0;
|
|
end;
|
|
fftChar :
|
|
begin
|
|
if NoCase then begin
|
|
Ch1 := upcase(AnsiChar(Key1));
|
|
Ch2 := upcase(AnsiChar(Key2));
|
|
Result := FFCmpB(byte(Ch1), byte(Ch2));
|
|
end
|
|
else
|
|
Result := FFCmpB(byte(Key1), byte(Key2));
|
|
end;
|
|
fftWideChar :
|
|
begin
|
|
CompareData.cdKeyLen := 1;
|
|
Result := FFKeyCompareWideChar(Key1, Key2, @CompareData);
|
|
end;
|
|
fftByte :
|
|
begin
|
|
Result := FFCmpB(byte(Key1), byte(Key2));
|
|
end;
|
|
fftWord16 :
|
|
begin
|
|
Result := FFCmpW(TffWord16(Key1), TffWord16(Key2));
|
|
end;
|
|
fftWord32 :
|
|
begin
|
|
Result := FFCmpDW(TffWord32(Key1), TffWord32(Key2));
|
|
end;
|
|
fftInt8 :
|
|
begin
|
|
Result := FFCmpI8(shortint(Key1), shortint(Key2));
|
|
end;
|
|
fftInt16 :
|
|
begin
|
|
Result := FFCmpI16(smallint(Key1), smallint(Key2));
|
|
end;
|
|
fftInt32 :
|
|
begin
|
|
Result := FFCmpI32(longint(Key1), longint(Key2));
|
|
end;
|
|
fftAutoInc :
|
|
begin
|
|
Result := FFCmpDW(TffWord32(Key1), TffWord32(Key2));
|
|
end;
|
|
fftSingle :
|
|
begin
|
|
if single(Key1) = single(Key2) then Result := 0
|
|
else if single(Key1) > single(Key2) then Result := 1
|
|
else Result := -1;
|
|
end;
|
|
fftDouble :
|
|
begin
|
|
if double(Key1) = double(Key2) then Result := 0
|
|
else if double(Key1) > double(Key2) then Result := 1
|
|
else Result := -1;
|
|
end;
|
|
fftExtended :
|
|
begin
|
|
if extended(Key1) = extended(Key2) then Result := 0
|
|
else if extended(Key1) > extended(Key2) then Result := 1
|
|
else Result := -1;
|
|
end;
|
|
fftComp,
|
|
fftCurrency :
|
|
begin
|
|
if comp(Key1) = comp(Key2) then Result := 0
|
|
else if comp(Key1) > comp(Key2) then Result := 1
|
|
else Result := -1;
|
|
end;
|
|
fftStDate,
|
|
fftStTime :
|
|
begin
|
|
Result := FFCmpI32(longint(Key1), longint(Key2));
|
|
end;
|
|
fftDateTime :
|
|
begin
|
|
if double(Key1) = double(Key2) then Result := 0
|
|
else if double(Key1) > double(Key2) then Result := 1
|
|
else Result := -1;
|
|
end;
|
|
fftBLOB,
|
|
fftBLOBMemo,
|
|
fftBLOBFmtMemo,
|
|
fftBLOBOLEObj,
|
|
fftBLOBGraphic,
|
|
fftBLOBDBSOLEObj,
|
|
fftBLOBTypedBin,
|
|
fftBLOBFile :
|
|
begin
|
|
Result := 0; {a spurious value}
|
|
FFRaiseExceptionNoData(EffServerException, ffStrResServer, fferrCannotCompare);
|
|
end;
|
|
fftByteArray :
|
|
begin
|
|
CompareData.cdKeyLen := FieldLen;
|
|
Result := FFKeyCompareBytes(Key1, Key2, @CompareData);
|
|
end;
|
|
fftShortString :
|
|
begin
|
|
CompareData.cdKeyLen := FieldLen - 1;
|
|
Result := FFKeyCompareStr(Key1, Key2, @CompareData);
|
|
end;
|
|
fftShortAnsiStr :
|
|
begin
|
|
CompareData.cdKeyLen := FieldLen - 1;
|
|
Result := FFKeyCompareAnsiStr(Key1, Key2, @CompareData);
|
|
end;
|
|
fftNullString :
|
|
begin
|
|
CompareData.cdKeyLen := FieldLen;
|
|
Result := FFKeyCompareStrZ(Key1, Key2, @CompareData);
|
|
end;
|
|
fftNullAnsiStr :
|
|
begin
|
|
CompareData.cdKeyLen := FieldLen;
|
|
Result := FFKeyCompareAnsiStrZ(Key1, Key2, @CompareData);
|
|
end;
|
|
fftWideString :
|
|
begin
|
|
CompareData.cdKeyLen := FieldLen div sizeof(WideChar);
|
|
Result := FFKeyCompareWideStr(Key1, Key2, @CompareData);
|
|
end;
|
|
else
|
|
Result := 0; {a spurious value}
|
|
FFRaiseExceptionNoData(EffServerException, ffStrResServer, fferrBadFieldType);
|
|
end;{case}
|
|
end;
|
|
{=====================================================================}
|
|
|
|
{== Internal Table Helper routines ===================================}
|
|
function FFTblHlpGetNewBlock(aFI : PffFileInfo;
|
|
aTI : PffTransInfo;
|
|
var aReleaseMethod : TffReleaseMethod) {!!.11}
|
|
: PffBlock;
|
|
var
|
|
aFileHeader : PffBlockHeaderFile;
|
|
BlockHdr : PffBlockHeaderFile absolute Result; {a convenient typecast}
|
|
BlockNumber : Longint;
|
|
aFHRelMethod : TffReleaseMethod;
|
|
begin
|
|
|
|
aFileHeader := PffBlockHeaderFile(FFBMGetBlock(aFI,
|
|
aTI,
|
|
0,
|
|
True,
|
|
aFHRelMethod));
|
|
try
|
|
{find a new block}
|
|
with aFileHeader^ do begin
|
|
{if there are no deleted blocks...}
|
|
if (bhfAvailBlocks = 0) then begin
|
|
{Have we reached the max # of blocks? }
|
|
if (bhfUsedBlocks = aFI^.fiMaxBlocks) then
|
|
FFRaiseExceptionNoData(EffServerException,
|
|
ffStrResServer,
|
|
fferrTableFull);
|
|
{Begin !!.11}
|
|
if ((TffSrDatabase(aTI^.tirTrans.DatabaseID).CheckSpace) and
|
|
(not (fffaTemporary in aFI^.fiAttributes)) and {!!.12}
|
|
(FFGetDiskFreeSpace(ExtractFileDir(aFI^.fiName^)) <
|
|
aFI^.fiBlockSizeK)) then
|
|
FFRaiseExceptionNoData(EffServerException,
|
|
ffStrResServer,
|
|
fferrDiskFull);
|
|
{End !!.11}
|
|
|
|
{get a brand new block}
|
|
BlockNumber := bhfUsedBlocks;
|
|
{Note: We don't need to lock the new block because the file's
|
|
header block is exclusively locked. This prevents
|
|
other threads from adding the same block to the file
|
|
and doesn't allow them to read it since they don't know
|
|
about it yet. }
|
|
Result := FFBMAddBlock(aFI, aTI, BlockNumber, aReleaseMethod);
|
|
inc(bhfUsedBlocks);
|
|
aFI^.fiUsedBlocks := bhfUsedBlocks;
|
|
if not (fffaTemporary in aFI^.fiAttributes) then {!!.12}
|
|
aTI^.tirTrans.NewSpace := aTI^.tirTrans.NewSpace + aFI^.fiBlockSizeK; {!!.11}
|
|
end
|
|
else {...there are some deleted blocks} begin
|
|
{ Reuse the first in the deleted block chain. }
|
|
BlockNumber := bhf1stFreeBlock;
|
|
Result := FFBMGetBlock(aFI,
|
|
aTI,
|
|
BlockNumber,
|
|
True,
|
|
aReleaseMethod);
|
|
bhf1stFreeBlock := BlockHdr^.bhfNextBlock;
|
|
dec(bhfAvailBlocks);
|
|
end;
|
|
end;
|
|
{set the only field in the block header we can}
|
|
BlockHdr^.bhfThisBlock := BlockNumber;
|
|
finally
|
|
aFHRelMethod(PffBlock(aFileHeader));
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure FFTblHlpDelete(const aPath : TffPath;
|
|
const aTableName : TffTableName;
|
|
const aDict : TffDataDictionary);
|
|
var
|
|
SL : TffStringList;
|
|
i : integer;
|
|
TblName : TffTableName;
|
|
FullName : TffFullFileName;
|
|
begin
|
|
SL := TffStringList.Create;
|
|
try
|
|
if (FFExtractExtension(aTableName) = ffc_ExtForData) then
|
|
TblName := FFExtractFileName(aTableName)
|
|
else
|
|
TblName := aTableName;
|
|
|
|
for i := 0 to Pred(aDict.FileCount) do begin
|
|
if i = 0 then
|
|
{ Force file extension. }
|
|
FullName := FFMakeFullFileName(aPath,
|
|
FFMakeFileNameExt(TblName,
|
|
ffc_ExtForData))
|
|
else
|
|
{ Use file extension from data dictionary. }
|
|
FullName := FFMakeFullFileName(aPath,
|
|
FFMakeFileNameExt(TblName,
|
|
aDict.FileExt[i]));
|
|
FFDeleteFile(FullName);
|
|
end;
|
|
finally
|
|
SL.Free;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure FFTblHlpDeleteBlock(aFI : PffFileInfo;
|
|
aFileHeader : PffBlockHeaderFile;
|
|
aBlock : PffBlock);
|
|
var
|
|
BlockHdr : PffBlockHeaderFile absolute aBlock; {a convenient typecast}
|
|
begin
|
|
{ Assumption: File header block & aBlock have been exclusively locked. }
|
|
|
|
{add it to the deleted block chain}
|
|
with aFileHeader^ do begin
|
|
{destroy the info in the block header}
|
|
BlockHdr^.bhfSignature := ffc_SigFreeBlock;
|
|
BlockHdr^.bhfNextBlock := bhf1stFreeBlock;
|
|
{add it to the deleted block chain}
|
|
bhf1stFreeBlock := BlockHdr^.bhfThisBlock;
|
|
inc(bhfAvailBlocks);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure FFTblHlpRename(const aPath : TffPath;
|
|
const aTableName : TffTableName;
|
|
const aNewName : TffTableName;
|
|
const aDict : TffDataDictionary);
|
|
var
|
|
SL : TffStringList;
|
|
i : integer;
|
|
TblName : TffTableName;
|
|
NewName : TffTableName;
|
|
FullName : TffFullFileName;
|
|
FullNewName : TffFullFileName;
|
|
begin
|
|
SL := TffStringList.Create;
|
|
try
|
|
if (FFExtractExtension(aTableName) = ffc_ExtForData) then
|
|
TblName := FFExtractFileName(aTableName)
|
|
else
|
|
TblName := aTableName;
|
|
|
|
if (FFExtractExtension(aNewName) = ffc_ExtForData) then
|
|
NewName := FFExtractFileName(aNewname)
|
|
else
|
|
NewName := aNewName;
|
|
|
|
for i := 0 to Pred(aDict.FileCount) do begin
|
|
if i = 0 then begin
|
|
{ Force file extension. }
|
|
FullName := FFMakeFullFileName(aPath,
|
|
FFMakeFileNameExt(TblName,
|
|
ffc_ExtForData));
|
|
FullNewName := FFMakeFullFileName(aPath,
|
|
FFMakeFileNameExt(NewName,
|
|
ffc_ExtForData));
|
|
end else begin
|
|
{ Use file extension from data dictionary. }
|
|
FullName := FFMakeFullFileName(aPath,
|
|
FFMakeFileNameExt(TblName,
|
|
aDict.FileExt[i]));
|
|
FullNewName := FFMakeFullFileName(aPath,
|
|
FFMakeFileNameExt(NewName,
|
|
aDict.FileExt[i]));
|
|
end;
|
|
FFRenameFile(FullName, FullNewName);
|
|
end;
|
|
finally
|
|
SL.Free;
|
|
end;
|
|
end;
|
|
{====================================================================}
|
|
|
|
|
|
{===Buffer manager access routines===================================}
|
|
function FFBMAddBlock(aFI : PffFileInfo;
|
|
aTI : PffTransInfo;
|
|
const aBlockNum : TffWord32;
|
|
var aReleaseMethod : TffReleaseMethod) : PffBlock;
|
|
begin
|
|
Assert(assigned(aFI) and assigned(aFI^.fiBufMgr));
|
|
Result := aFI^.fiBufMgr.AddBlock(aFI, aTI, aBlockNum, aReleaseMethod)
|
|
end;
|
|
{--------}
|
|
procedure FFBMDirtyBlock(aFI : PffFileInfo;
|
|
const aBlockNum : TffWord32;
|
|
aTI : PffTransInfo;
|
|
var aModifiableBlock : PffBlock);
|
|
begin
|
|
Assert(assigned(aFI));
|
|
aFI^.fiBufMgr.DirtyBlock(aFI, aBlockNum, aTI, aModifiableBlock);
|
|
end;
|
|
{--------}
|
|
function FFBMGetBlock(aFI : PffFileInfo;
|
|
aTI : PffTransInfo;
|
|
const aBlockNum : TffWord32;
|
|
const aMarkDirty : Boolean;
|
|
var aReleaseMethod : TffReleaseMethod) : PffBlock;
|
|
begin
|
|
Assert(aTI <> nil, 'No transaction specified.'); {!!.03}
|
|
if Assigned(aFI) and Assigned(aFI^.fiBufMgr) then
|
|
Result := aFI^.fiBufMgr.GetBlock(aFI, aBlockNum, aTI, aMarkDirty,
|
|
aReleaseMethod)
|
|
else
|
|
Result := nil;
|
|
end;
|
|
{--------}
|
|
function FFBMGetFileHeaderBlock(aFI : PffFileInfo;
|
|
aTI : PffTransInfo;
|
|
const aMarkDirty : Boolean;
|
|
var aReleaseMethod : TffReleaseMethod) : PffBlock;
|
|
begin
|
|
if Assigned(aFI) and Assigned(aFI^.fiBufMgr) then
|
|
Result := aFI^.fiBufMgr.AddFile(aFI, aTI, aMarkDirty, aReleaseMethod)
|
|
else
|
|
Result := nil;
|
|
end;
|
|
{--------}
|
|
procedure FFBMRemoveFile(aFI : PffFileInfo);
|
|
begin
|
|
if Assigned(aFI) and Assigned(aFI^.fiBufMgr) then
|
|
aFI^.fiBufMgr.RemoveFile(aFI);
|
|
end;
|
|
{--------}
|
|
procedure FFBMUnlockBlock(aFI : PffFileInfo; aBlockNum : TffWord32);
|
|
begin
|
|
if Assigned(aFI) and Assigned(aFI^.fiBufMgr) then
|
|
aFI^.fiBufMgr.UnlockBlock(aFI, aBlockNum);
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===Lock manager access routines=====================================}
|
|
procedure FFAcqRecordLock(aFI : PffFileInfo;
|
|
aTI : PffTransInfo;
|
|
const aRefNum : TffInt64;
|
|
const aLockType : TffSrLockType;
|
|
const aDatabaseID : TffDatabaseID; {!!.10}
|
|
const aCursorID : TffCursorID; {!!.02}
|
|
const aConditional : Boolean); {!!.02}
|
|
var
|
|
LockStatus : TffLockRequestStatus;
|
|
RecStr : string;
|
|
RetryUntil : DWORD;
|
|
TickCount : DWORD;
|
|
begin
|
|
|
|
{ We support only exclusive locking of records. }
|
|
if (aFI^.fiExclOwner = aCursorID) or (aLockType <> ffsltExclusive) then
|
|
Exit;
|
|
|
|
RetryUntil := FFGetRetry;
|
|
TickCount := GetTickCount;
|
|
|
|
{ Do we have any time left? Note that we are doing this check to avoid
|
|
the situation where we ask for a lock and pass a negative timeout. }
|
|
if (RetryUntil > TickCount) and
|
|
((RetryUntil - TickCount) >= 5) then begin
|
|
{ Obtain an exclusive lock on the record. }
|
|
LockStatus := TffLockManager(aTI^.tirLockMgr).AcquireRecordLock
|
|
(aRefNum, aFI,
|
|
aLockType,
|
|
aConditional, {!!.02}
|
|
(RetryUntil - TickCount),
|
|
aTI^.tirTrans,
|
|
aDatabaseID, {!!.10}
|
|
aCursorID);
|
|
|
|
{ Raise an exception if something went awry. }
|
|
if LockStatus <> fflrsGranted then
|
|
RecStr := format(ffcRecord,[aRefNum.iHigh, aRefNum.iLow]);
|
|
case LockStatus of
|
|
fflrsTimeout :
|
|
FFRaiseException(EffServerException, ffStrResServer, fferrLockTimeout,
|
|
[FFMapLockToName(aLockType), RecStr, aFI^.fiName^]);
|
|
fflrsRejected :
|
|
FFRaiseException(EffServerException, ffStrResServer, fferrLockRejected,
|
|
[FFMapLockToName(aLockType), RecStr, aFI^.fiName^]);
|
|
end; { case }
|
|
end
|
|
else
|
|
{ No. Assume we will time out waiting for the resource. }
|
|
FFRaiseExceptionNoData(EffServerException, ffStrResServer,
|
|
fferrGeneralTimeout);
|
|
end;
|
|
{Begin !!.10}
|
|
{--------}
|
|
procedure FFRelaxRecordLock(aFI : PffFileInfo;
|
|
aTI : PffTransInfo;
|
|
const aCursorID : TffCursorID;
|
|
const aRefNum : TffInt64);
|
|
begin
|
|
if (aFI^.fiExclOwner = aCursorID) then
|
|
Exit;
|
|
|
|
TffLockManager(aTI^.tirLockMgr).RelaxRecordLock
|
|
(aRefNum, aFI, aTI^.tirTrans.DatabaseID);
|
|
end;
|
|
{End !!.10}
|
|
{--------}
|
|
procedure FFRelRecordLock(aFI : PffFileInfo; aTI : PffTransInfo;
|
|
const aRefNum : TffInt64;
|
|
const aDatabaseID : TffDatabaseID; {!!.10}
|
|
const aCursorID : TffCursorID);
|
|
begin
|
|
if (aFI^.fiExclOwner = aCursorID) then
|
|
Exit;
|
|
|
|
TffLockManager(aTI^.tirLockMgr).ReleaseRecordLock
|
|
(aRefNum, aFI, aTI^.tirTrans, aDatabaseID); {!!.10}
|
|
end;
|
|
{====================================================================}
|
|
|
|
end.
|