Files
lazarus-ccr/components/flashfiler/sourcelaz/ffsrcur.pas
2016-12-07 13:31:59 +00:00

1069 lines
40 KiB
ObjectPascal

{*********************************************************}
{* FlashFiler: Server cursor classes *}
{*********************************************************}
(* ***** 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 ffsrcur;
interface
uses
ffllbase,
fflldict,
fflltemp,
ffsrbase,
ffsrbde,
ffsreng,
ffsrfltr,
ffsrfold,
ffsrlock,
fftbdict,
fftbindx;
type
{ This class manages a set of data stored in a logical table. It is very
similar to TffSrTable. However, the data is not indexed.
To create a new simple table, you must do the following:
1. Call TffSrSimpleTable.Create.
2. Have the table build its files by calling its BuildFiles method.
3. Have the table open its fils by calling its OpenFiles method. }
TffSrSimpleTable = class(TffSrBaseTable)
public
procedure AddIndex(const aIndexDesc : TffIndexDescriptor;
aTI : PffTransInfo); override;
procedure BuildFiles(aTI : PffTransInfo;
aForServer : boolean;
aDictionary : TffDataDictionary;
aAttribs : TffFileAttributes;
aStore : TffBaseTempStorage); override;
function BuildKeyForRecord(aIndexID : integer;
aData : PffByteArray;
aKey : PffByteArray;
aFieldCount : integer;
aPartialLen : integer) : TffResult; override;
function CompareKeysForCursor(var aKID : TffKeyIndexData;
aKey1 : PffByteArray;
aKey2 : PffByteArray) : integer; override;
function DeleteRecord(aTI : PffTransInfo;
const aCursorID : TffCursorID;
const aRefNr : TffInt64;
const aLockObtained : Boolean;
var aBTreeChanged : Boolean) {!!.05}
: TffResult; override;
procedure DropIndex(aTI : PffTransInfo; aIndexID : Longint); override;
function FindKey(var aKID : TffKeyIndexData;
var aRefNr : TffInt64;
aTI : PffTransInfo;
aKey : PffByteArray;
var aKeyPath : TffKeyPath;
aAction : TffSearchKeyAction) : boolean; override;
function GetNextKey(var aKID : TffKeyIndexData;
var aRefNr : TffInt64;
aTI : PffTransInfo;
aKey : PffByteArray;
var aKeyPath : TffKeyPath) : TffResult; override;
function GetNextRecord(aTI : PffTransInfo;
const aDatabaseID : TffDatabaseID; {!!.10}
const aCursorID : TffCursorID; {!!.10}
var aKID : TffKeyIndexData;
var aRefNr : TffInt64;
aKey : PffByteArray;
var aKeyPath : TffKeyPath;
aData : PffByteArray;
const aLockType : TffSrLockType) : TffResult; override; {!!.10}
function GetPriorRecord(aTI : PffTransInfo;
const aDatabaseID : TffDatabaseID; {!!.10}
const aCursorID : TffCursorID; {!!.10}
var aKID : TffKeyIndexData;
var aRefNr : TffInt64;
aKey : PffByteArray;
var aKeyPath : TffKeyPath;
aData : PffByteArray;
const aLockType : TffSrLockType) : TffResult; override; {!!.10}
function InsertRecord(aTI : PffTransInfo;
aCursorID : TffCursorID;
aData : PffByteArray;
aLockType : TffSrLockType;
var aNewRefNr : TffInt64) : TffResult; override;
function InsertRecordNoDefault(aTI : PffTransInfo; {!!.10}
aCursorID : TffCursorID;
aData : PffByteArray;
aLockType : TffSrLockType;
var aNewRefNr : TffInt64) : TffResult; override;
procedure MakeKIDForCursor(aIndexID : integer; var aKID : TffKeyIndexData); override;
procedure OpenFiles(aTI : PffTransInfo; aForServer : boolean;
aAttribs : TffFileAttributes); override;
function PutRecord(aTI : PffTransInfo;
aCursorID : TffCursorID;
aRefNr : TffInt64;
aData : PffByteArray;
aRelLock : boolean; {!!.05}
var aKeyChanged : Boolean) : TffResult; override; {!!.05}
end;
{ Use this cursor class to manage sets of data that do not require indices. }
TffSrSimpleCursor = class(TffSrBaseCursor)
protected
procedure bcTableOpenPreconditions(aTable : TffSrBaseTable;
const aIndexName : string;
var aIndexID : Longint;
const aOpenMode : TffOpenMode); override;
{ Used by Create method to verify a thread may open a table. }
public
constructor Create(anEngine : TffServerEngine;
aDatabase : TffSrDatabase;
const aTimeout : Longint); override;
destructor Destroy; override;
function AddIndexToTable(const aIndexDesc : TffIndexDescriptor) : TffResult; override;
function CheckBookmark(aBookmark : PffByteArray) : TffResult; override;
procedure ClearIndex; override;
function CloneCursor(aOpenMode : TffOpenMode) : TffSrBaseCursor; override;
function CompareBookmarks(aBookmark1, aBookmark2 : PffByteArray;
var CmpResult : Longint) : TffResult; override;
function DropIndexFromTable(const aIndexName : TffDictItemName;
aIndexID : Longint) : TffResult; override;
function ExtractKey(aData : PffByteArray; aKey : PffByteArray) : TffResult; override;
function GetBookmark(aBookmark : PffByteArray) : TffResult; override;
function GetBookmarkSize : integer; override;
function GetNextRecord(aData : PffByteArray; aLockType : TffSrLockType) : TffResult; override;
function GetPriorRecord(aData : PffByteArray; aLockType : TffSrLockType) : TffResult; override;
function GetRecordCount(var aRecCount : Longint) : TffResult; override;
function GetRecordForKey(aDirectKey : boolean;
aFieldCount : integer;
aPartialLen : integer;
aKeyData : PffByteArray;
aData : PffByteArray;
aFirstCall : Boolean) : TffResult; override;
function InsertRecord(aData : PffByteArray; aLockType : TffSrLockType) : TffResult; override;
function InsertRecordNoDefault(aData : PffByteArray; aLockType : TffSrLockType) : TffResult; override;{!!.10}
function IsInRange(aKey : PffByteArray) : integer; override;
function ModifyRecord(aData : PffByteArray; aRelLock : boolean) : TffResult; override;
procedure Open(const aTableName : TffTableName;
const aIndexName : TffName;
const aIndexID : Longint;
const aOpenMode : TffOpenMode;
aShareMode : TffShareMode;
aForServer : boolean;
const aExclContLock : Boolean; {!!.10}
aAttribs : TffFileAttributes); override;
procedure ResetRange; override;
function SetRange(aDirectKey : boolean;
aFieldCount1 : integer;
aPartialLen1 : integer;
aKeyData1 : PffByteArray;
aKeyIncl1 : boolean;
aFieldCount2 : integer;
aPartialLen2 : integer;
aKeyData2 : PffByteArray;
aKeyIncl2 : boolean) : TffResult; override;
procedure SetToBegin; override;
function SetToBookmark(aBookmark : PffByteArray) : TffResult; override;
function SetToCursor(aCursor : TffSrBaseCursor) : TffResult; override;
procedure SetToEnd; override;
function SetToKey(aSearchAction : TffSearchKeyAction;
aDirectKey : boolean;
aFieldCount : integer;
aPartialLen : integer;
aKeyData : PffByteArray) : TffResult; override;
function SwitchToIndex(aIndexID : integer;
aPosnOnRec : boolean) : TffResult; override;
end;
{ Use this class to create a cursor representing a query's result set.
Do not directly create TffSrSimpleCursor. }
TffSrSQLResultSet = class(TffSrSimpleCursor);
implementation
uses
ffconst,
ffhash,
fflleng,
ffllexcp,
fftbbase,
fftbBLOB, {!!.11}
fftbdata;
{===TffSrSQLTable====================================================}
procedure TffSrSimpleTable.AddIndex(const aIndexDesc : TffIndexDescriptor;
aTI : PffTransInfo);
begin
FFRaiseException(EffException, ffStrResServer, fferrIndexNotSupported,
[btBaseName^, Self.ClassName]);
end;
{--------}
procedure TffSrSimpleTable.BuildFiles(aTI : PffTransInfo;
aForServer : boolean;
aDictionary : TffDataDictionary;
aAttribs : TffFileAttributes;
aStore : TffBaseTempStorage);
var
FileInx : integer;
DataFile : PffFileInfo;
FileCnt : integer; {dup for speed}
begin
{ Create the data file. }
btFiles.Count := 1;
btFiles[0] := FFAllocFileInfo(FFMakeFullFileName(Folder.Path, BaseName),
ffc_ExtForData, btBufMgr);
{ Validate the dictionary. }
aDictionary.CheckValid;
{ Assimilate the dictionary. }
btDictionary.ForceOffReadOnly;
btDictionary.Assign(aDictionary);
btDictionary.BindIndexHelpers;
with PffFileInfo(btFiles[0])^ do begin
fiAttributes := aAttribs;
fiForServer := aForServer;
fiEncrypted := btEngine.Configuration.GeneralInfo^.giAllowEncrypt and
aDictionary.IsEncrypted;
fiRecLenPlusTrailer := btDictionary.RecordLength + SizeOf(Byte);
fiRecordLength := btDictionary.RecordLength;
fiTempStore := aStore;
end;
{ Get the file count for this table (for speed reasons, etc). }
FileCnt := Dictionary.FileCount;
FileCount := FileCnt;
{ Get the data file for speed reasons. }
DataFile := Files[0];
{ Scan through the secondary files. This table supports separate BLOB
files but not separate index files. }
for FileInx := 0 to pred(FileCnt) do begin
if Dictionary.FileType[FileInx] = ftIndexFile then
FFRaiseException(EffException, ffStrResServer, fferrIndexNotSupported,
[btBaseName^, Self.ClassName]);
btCreateFile(FileInx, aTI, btDictionary.FileExt[FileInx], aForServer,
aAttribs, aStore);
end;
{ Write the dictionary to the data file. }
Dictionary.WriteToFile(DataFile, aTI);
{Begin !!.11}
Files[Dictionary.BLOBFileNumber].fiBLOBrscMgr :=
TffBaseBLOBResourceMgr.GetMgr(Files[Dictionary.BLOBFileNumber]);
btBLOBEngine := TffBaseBLOBEngine.GetEngine(Files[Dictionary.BLOBFileNumber]);
{End !!.11}
Files[Dictionary.BLOBFileNumber].fiMaxSegSize :=
FFCalcMaxBLOBSegSize(Files[Dictionary.BLOBFileNumber]);
end;
{--------}
function TffSrSimpleTable.BuildKeyForRecord(aIndexID : integer;
aData : PffByteArray;
aKey : PffByteArray;
aFieldCount : integer;
aPartialLen : integer) : TffResult;
begin
Result := DBIERR_INVALIDINDEXCREATE;
end;
{--------}
function TffSrSimpleTable.CompareKeysForCursor(var aKID : TffKeyIndexData;
aKey1 : PffByteArray;
aKey2 : PffByteArray) : integer;
begin
Result := DBIERR_INVALIDINDEXCREATE;
end;
{--------}
function TffSrSimpleTable.DeleteRecord(aTI : PffTransInfo;
const aCursorID : TffCursorID;
const aRefNr : TffInt64;
const aLockObtained : Boolean;
var aBTreeChanged : Boolean) {!!.05}
: TffResult;
var
OldData : PffByteArray;
RecLen : integer;
begin
RecLen := btDictionary.RecordLength;
FFGetMem(OldData, RecLen);
Result := DBIERR_NONE;
{ If we have yet to lock the record then do so. }
if (not aLockObtained) then
FFAcqRecordLock(Files[0], aTI, aRefNr, ffsltExclusive, {!!.10}
aTI^.tirTrans.DatabaseID, {!!.10}
aCursorID, false); {!!.02}{!!.10}
{ Note: We leave all such locks active until the transaction is committed. }
try
FFTblReadRecord(Files[0], aTI, aRefNr, OldData);
btDeleteBLOBsForRecord(aTI, OldData);
FFTblDeleteRecord(Files[0], aTI, aRefNr);
finally
btInformCursors(aCursorID, roDelete, aRefNr, 0);
FFFreeMem(OldData, RecLen);
end;{try..finally}
end;
{--------}
procedure TffSrSimpleTable.DropIndex(aTI : PffTransInfo;
aIndexID : Longint);
begin
FFRaiseException(EffException, ffStrResServer, fferrIndexNotSupported,
[btBaseName^, Self.ClassName]);
end;
{--------}
function TffSrSimpleTable.FindKey(var aKID : TffKeyIndexData;
var aRefNr : TffInt64;
aTI : PffTransInfo;
aKey : PffByteArray;
var aKeyPath : TffKeyPath;
aAction : TffSearchKeyAction) : boolean;
begin
ffInitI64(aRefNr);
Result := False;
end;
{--------}
function TffSrSimpleTable.GetNextKey(var aKID : TffKeyIndexData;
var aRefNr : TffInt64;
aTI : PffTransInfo;
aKey : PffByteArray;
var aKeyPath : TffKeyPath) : TffResult;
begin
Result := DBIERR_INVALIDINDEXCREATE;
end;
{--------}
function TffSrSimpleTable.GetNextRecord(aTI : PffTransInfo;
const aDatabaseID : TffDatabaseID; {!!.10}
const aCursorID : TffCursorID; {!!.10}
var aKID : TffKeyIndexData;
var aRefNr : TffInt64;
aKey : PffByteArray;
var aKeyPath : TffKeyPath;
aData : PffByteArray;
const aLockType : TffSrLockType) : TffResult;
begin
Result := DBIERR_NONE;
GetNextRecordSeq(aTI, aRefNr, aData);
end;
{--------}
function TffSrSimpleTable.GetPriorRecord(aTI : PffTransInfo;
const aDatabaseID : TffDatabaseID; {!!.10}
const aCursorID : TffCursorID; {!!.10}
var aKID : TffKeyIndexData;
var aRefNr : TffInt64;
aKey : PffByteArray;
var aKeyPath : TffKeyPath;
aData : PffByteArray;
const aLockType : TffSrLockType) : TffResult; {!!.10}
begin
Result := DBIERR_NONE;
GetPrevRecordSeq(aTI, aRefNr, aData);
end;
{--------}
function TffSrSimpleTable.InsertRecord(aTI : PffTransInfo;
aCursorID : TffCursorID;
aData : PffByteArray;
aLockType : TffSrLockType;
var aNewRefNr : TffInt64) : TffResult;
var
RefNr : TffInt64;
begin
RefNr.iLow := 0;
RefNr.iHigh := 0;
Result := DBIERR_NONE;
if not Dictionary.CheckRequiredRecordFields(aData) then
Result := DBIERR_REQDERR
else begin
{we need to add the default field values}
if Dictionary.DefaultFieldCount > 0 then
Dictionary.SetDefaultFieldValues(aData);
{ Updating the autoinc value obtains an exclusive lock on block 0 which
prevents other cursors from inserting the same or additional records
until we are done. }
btUpdateAutoInc(aTI, aData);
FFTblAddRecord(Files[0], aTI, RefNr, aData);
aNewRefNr := RefNr;
FFAcqRecordLock(Files[0], aTI, aNewRefNr, aLockType, {!!.10}
aTI^.tirTrans.DatabaseID, {!!.10}
aCursorID, false); {!!.02}{!!.10}
end;
end;
{--------}
function TffSrSimpleTable.InsertRecordNoDefault(aTI : PffTransInfo;{!!.10}
aCursorID : TffCursorID;
aData : PffByteArray;
aLockType : TffSrLockType;
var aNewRefNr : TffInt64) : TffResult;
var
RefNr : TffInt64;
begin
RefNr.iLow := 0;
RefNr.iHigh := 0;
Result := DBIERR_NONE;
if not Dictionary.CheckRequiredRecordFields(aData) then
Result := DBIERR_REQDERR
else begin
{ Updating the autoinc value obtains an exclusive lock on block 0 which
prevents other cursors from inserting the same or additional records
until we are done. }
btUpdateAutoInc(aTI, aData);
FFTblAddRecord(Files[0], aTI, RefNr, aData);
aNewRefNr := RefNr;
FFAcqRecordLock(Files[0], aTI, aNewRefNr, aLockType, {!!.10}
aTI^.tirTrans.DatabaseID, {!!.10}
aCursorID, false); {!!.02}{!!.10}
end;
end;
{--------}
procedure TffSrSimpleTable.MakeKIDForCursor(aIndexID : integer; var aKID : TffKeyIndexData);
begin
FFRaiseException(EffException, ffStrResServer, fferrIndexNotSupported,
[btBaseName^, Self.ClassName]);
end;
{--------}
procedure TffSrSimpleTable.OpenFiles(aTI : PffTransInfo; aForServer : boolean;
aAttribs : TffFileAttributes);
var
FileInx : integer;
begin
{ Are any of the files marked as index files? If so then raise an exception
because this class does not support indexes. Assume that file 0 is the
data file. }
for FileInx := 1 to pred(Dictionary.FileCount) do
if Dictionary.FileType[FileInx] = ftIndexFile then
FFRaiseException(EffException, ffStrResServer, fferrIndexNotSupported,
[btBaseName^, Self.ClassName]);
{ If we've made it this far then open the files. }
inherited OpenFiles(aTI, aForServer, aAttribs);
end;
{--------}
function TffSrSimpleTable.PutRecord(aTI : PffTransInfo;
aCursorID : TffCursorID;
aRefNr : TffInt64;
aData : PffByteArray;
aRelLock : boolean; {!!.05}
var aKeyChanged : Boolean) : TffResult; {!!.05}
var
RecLen : integer;
OldData: PffByteArray;
begin
{ Assumption: By the time we have reached this point, the transaction has
acquired a content lock on the table and we are the only ones who are
going to be modifying the record. }
aKeyChanged := False; {!!.05}
RecLen := 0;
if not Dictionary.CheckRequiredRecordFields(aData) then begin
Result := DBIERR_REQDERR;
Exit;
end;
Result := DBIERR_NONE;
try
try
RecLen := Dictionary.RecordLength;
FFGetMem(OldData, RecLen);
FFTblReadRecord(Files[0], aTI, aRefNr, OldData);
{ Acquire an exclusive lock. }
FFAcqRecordLock(Files[0], aTI, aRefNr, ffsltExclusive, {!!.10}
aTI^.tirTrans.DatabaseID, {!!.10}
aCursorID, false); {!!.02}{!!.10}
FFTblUpdateRecord(Files[0], aTI, aRefNr, aData);
except
FFRelRecordLock(Files[0], aTI, aRefNr, aTI^.tirTrans.DatabaseID, {!!.10}
aCursorID); {!!.10}
end;
finally
FFFreeMem(OldData, RecLen);
end;{try..finally}
end;
{====================================================================}
{===TffSrSimpleCursor================================================}
constructor TffSrSimpleCursor.Create(anEngine : TffServerEngine;
aDatabase : TffSrDatabase;
const aTimeout : Longint);
begin
bcTableClass := TffSrSimpleTable;
inherited Create(anEngine, aDatabase, aTimeout);
end;
{--------}
destructor TffSrSimpleCursor.Destroy;
begin
{ Free the table locks held by the cursor. }
if Assigned(bcTable) then
bcTable.RelLock(CursorID, True);
inherited Destroy;
end;
{--------}
function TffSrSimpleCursor.AddIndexToTable(const aIndexDesc : TffIndexDescriptor) : TffResult;
begin
Result := DBIERR_INVALIDINDEXCREATE;
FFRaiseException(EffException, ffStrResServer, fferrIndexNotSupported,
[bcTable.BaseName, Self.ClassName]);
end;
{--------}
procedure TffSrSimpleCursor.bcTableOpenPreconditions(aTable : TffSrBaseTable;
const aIndexName : string;
var aIndexID : Longint;
const aOpenMode : TffOpenMode);
begin
{ Ignore the index information. }
{ If the table's data file is open in read-only mode it means the
physical file is read-only: hence this call's openmode must be
read-only as well. }
if (aTable.Files[0]^.fiOpenMode = omReadOnly) and
(aOpenMode <> omReadOnly) then
FFRaiseException(EffException, ffStrResServer, fferrCursorReadOnly,
[aTable.BaseName]);
end;
{--------}
function TffSrSimpleCursor.CheckBookmark(aBookmark : PffByteArray) : TffResult;
var
CheckHash : Longint;
begin
Result := DBIERR_INVALIDBOOKMARK;
if (aBookmark = nil) then
Exit;
with PffSrBookmark(aBookmark)^ do begin
CheckHash := FFCalcElfHash(sbIndexID,
ffcl_FixedBookmarkSize - SizeOf(sbHash));
if (sbHash <> CheckHash) then
Exit;
end;
Result := DBIERR_NONE;
end;
{--------}
procedure TffSrSimpleCursor.ClearIndex;
begin
FFRaiseException(EffException, ffStrResServer, fferrIndexNotSupported,
[bcTable.BaseName, Self.ClassName]);
end;
{--------}
function TffSrSimpleCursor.CloneCursor(aOpenMode : TffOpenMode) : TffSrBaseCursor;
begin
{NOTE: we are not checking rights for this action because the client
had the rights to open the cursor}
{ Resolve the open mode. }
if (bcOpenMode = omReadOnly) then
aOpenMode := omReadOnly;
AcqContentLock(ffclmRead);
try
{ Create the cursor. }
Result := TffSrSimpleCursor.Create(bcEngine, bcDatabase, soTimeout);
Result.Open(bcTable.BaseName, '', bcIndexID, aOpenMode, smShared,
bcTable.IsServerTable, False, bcTable.Files[0]^.fiAttributes);
{ Set up all of the misc fields. }
Result.CursorInfo := bcInfo;
if Assigned(bcFilter) then
Result.SetFilter(bcFilter.Expression,bcFilter.Timeout);
finally
RelContentLock(ffclmRead);
end;
end;
{--------}
function TffSrSimpleCursor.CompareBookmarks(aBookmark1, aBookmark2 : PffByteArray;
var CmpResult : Longint) : TffResult;
var
BM1 : PffSrBookmark absolute aBookmark1;
BM2 : PffSrBookmark absolute aBookmark2;
begin
Result := CheckBookmark(aBookmark1);
if (Result = DBIERR_NONE) then
Result := CheckBookmark(aBookmark2);
if (Result <> DBIERR_NONE) then
Exit;
case BM1^.sbPos of
cpUnknown : CmpResult := -1;
cpBOF : if (BM2^.sbPos = cpBOF) then
CmpResult := 0
else
CmpResult := -1;
cpEOF : if (BM2^.sbPos = cpEOF) then
CmpResult := 0
else
CmpResult := 1;
else
{ Bookmark 1 is on a crack or on a record. }
case BM2^.sbPos of
cpUnknown : CmpResult := 1;
cpBOF : CmpResult := 1;
cpEOF : CmpResult := -1;
else
{ Bookmark 2 is also on a crack or on a record. Check the reference
numbers.}
CmpResult := ffCmpI64(BM1^.sbRefNr, BM2^.sbRefNr);
end; {case}
end; {case}
end;
{--------}
function TffSrSimpleCursor.DropIndexFromTable(const aIndexName : TffDictItemName;
aIndexID : Longint) : TffResult;
begin
Result := DBIERR_INVALIDINDEXCREATE;
FFRaiseException(EffException, ffStrResServer, fferrIndexNotSupported,
[bcTable.BaseName, Self.ClassName]);
end;
{--------}
function TffSrSimpleCursor.ExtractKey(aData : PffByteArray; aKey : PffByteArray) : TffResult;
begin
Result := DBIERR_INVALIDINDEXCREATE;
FFRaiseException(EffException, ffStrResServer, fferrIndexNotSupported,
[bcTable.BaseName, Self.ClassName]);
end;
{--------}
function TffSrSimpleCursor.GetBookmark(aBookmark : PffByteArray) : TffResult;
begin
Result := DBIERR_NONE;
AcqContentLock(ffclmRead);
try
FillChar(PffSrBookmark(aBookmark)^, ffcl_FixedBookmarkSize, 0);
with PffSrBookmark(aBookmark)^ do begin
sbRefNr := bcInfo.RefNr;
sbPos := bcInfo.Pos;
sbKeyValid := bcInfo.KeyValid;
sbHash := FFCalcElfHash(sbIndexID, ffcl_FixedBookmarkSize - sizeof(sbHash));
end;
finally
RelContentLock(ffclmRead);
end;
end;
{--------}
function TffSrSimpleCursor.GetBookmarkSize : integer;
begin
Result := ffcl_FixedBookmarkSize;
end;
{--------}
function TffSrSimpleCursor.GetNextRecord(aData : PffByteArray; aLockType : TffSrLockType) : TffResult;
begin
{ If we are at EOF, then obviously there's no next record. }
if (bcInfo.pos = cpEOF) then begin
Result := DBIERR_EOF;
Exit;
end;
AcqContentLock(ffclmRead);
try
{ Make sure that we have somewhere to read the record into. }
if (aData = nil) then
aData := bcRecordData;
if Assigned(bcFilter) then
bcFilter.BeginTimeout;
Result := DBIERR_NONE;
repeat
bcTable.GetNextRecordSeq(bcDatabase.TransactionInfo, bcInfo.RefNr, aData);
if ffI64IsZero(bcInfo.RefNr) then begin
Result := DBIERR_EOF;
SetToEnd;
Exit;
end;
{ In theory we're on a record. }
bcInfo.Deleted := false;
bcInfo.KeyValid := true;
bcInfo.Pos := cpOnRecord;
until (Result <> DBIERR_NONE) or not Assigned(bcFilter) or
bcFilter.MatchesRecord(aData) or bcFilter.CheckTimeout(Result);
{ Place the lock if needed... record will not be read again. }
if (Result = DBIERR_NONE) and (aLockType <> ffsltNone) then
Result := Table.GetRecord(bcDatabase.TransactionInfo,
bcDatabase.DatabaseID, CursorID, {!!.10}
bcInfo.RefNr, nil, aLockType, false, false); {!!.02}
finally
RelContentLock(ffclmRead);
end;
end;
{--------}
function TffSrSimpleCursor.GetPriorRecord(aData : PffByteArray; aLockType : TffSrLockType) : TffResult;
begin
{ If we are at BOF, then obviously there's no prior record. }
if (bcInfo.pos = cpBOF) then begin
Result := DBIERR_BOF;
Exit;
end;
AcqContentLock(ffclmRead);
try
{ Make sure that we have somewhere to read the record into. }
if (aData = nil) then
aData := bcRecordData;
{ Get the previous record. }
if Assigned(bcFilter) then
bcFilter.BeginTimeout;
Result := DBIERR_NONE;
repeat
bcTable.GetPrevRecordSeq(bcDatabase.TransactionInfo, bcInfo.RefNr,
aData);
if FFI64IsZero(bcInfo.RefNr) then begin
Result := DBIERR_BOF;
SetToBegin;
Exit;
end;
{ In theory we're on a record. }
bcInfo.Deleted := false;
bcInfo.KeyValid := true;
bcInfo.Pos := cpOnRecord;
until (Result <> DBIERR_NONE) or not Assigned(bcFilter) or
bcFilter.MatchesRecord(aData) or bcFilter.CheckTimeout(Result);
{ Place the lock if needed... record will not be read again. }
if (Result = DBIERR_NONE) and (aLockType <> ffsltNone) then
Result := bcTable.GetRecord(bcDatabase.TransactionInfo, {!!.10}
bcDatabase.DatabaseID, {!!.10}
CursorID, {!!.10}
bcInfo.refNr, nil, aLockType, false, false); {!!.02}
finally
RelContentLock(ffclmRead);
end;
end;
{--------}
function TffSrSimpleCursor.GetRecordCount(var aRecCount : Longint) : TffResult;
var
aData : PffByteArray;
Info : TffRecordInfo;
begin
Result := DBIERR_NONE;
AcqContentLock(ffclmRead);
try
{ Is a filter active? }
if Assigned(bcFilter) then begin
{ Yes. Set count to zero. We are going to scan through the records. }
aRecCount := 0;
{ Save the current position. }
bcSaveCurInfo;
FFGetZeroMem(aData, bcRecordLen);
try
{BOF}
SetToBegin;
{ While not EOF or other error do. }
while (Result = DBIERR_NONE) do begin
Result := GetNextRecord(aData, ffsltNone);
if (Result = DBIERR_NONE) then
inc(aRecCount);
end;
Result := DBIERR_NONE;
finally
FFFreeMem(aData, bcRecordLen);
{ Reset current position. }
bcRestoreCurInfo;
end;
end
else begin
FFTblGetRecordInfo(bcTable.Files[0], bcDatabase.TransactionInfo, Info);
aRecCount := Info.riRecCount;
end;
finally
RelContentLock(ffclmRead);
end;
end;
{--------}
function TffSrSimpleCursor.GetRecordForKey(aDirectKey : boolean;
aFieldCount : integer;
aPartialLen : integer;
aKeyData : PffByteArray;
aData : PffByteArray;
aFirstCall : Boolean) : TffResult;
begin
Result := DBIERR_INVALIDINDEXCREATE;
FFRaiseException(EffException, ffStrResServer, fferrIndexNotSupported,
[bcTable.BaseName, Self.ClassName]);
end;
{--------}
function TffSrSimpleCursor.InsertRecord(aData : PffByteArray; aLockType : TffSrLockType) : TffResult;
var
NewRefNr : TffInt64;
begin
{ Notify extenders. }
Result := NotifyExtenders(ffeaBeforeRecInsert, ffeaInsertRecFail);
if Result = DBIERR_NONE then begin
AcqContentLock(ffclmWrite);
Result := bcTable.InsertRecord(bcDatabase.TransactionInfo,
CursorID, aData, aLockType, NewRefNr);
if (Result = DBIERR_NONE) then begin
bcInfo.pos := cpOnRecord;
bcInfo.refNr := NewRefNr;
bcInfo.Deleted := false;
bcInfo.KeyValid := True;
{ Notify extenders of successful insert. }
NotifyExtenders(ffeaAfterRecInsert, ffeaNoAction);
end else
{ Notify extenders of failed insert. }
NotifyExtenders(ffeaInsertRecFail, ffeaNoAction);
end;
end;
{--------}
function TffSrSimpleCursor.InsertRecordNoDefault(aData : PffByteArray;{!!.10}
aLockType : TffSrLockType) : TffResult;
var
NewRefNr : TffInt64;
begin
{ Notify extenders. }
Result := NotifyExtenders(ffeaBeforeRecInsert, ffeaInsertRecFail);
if Result = DBIERR_NONE then begin
AcqContentLock(ffclmWrite);
Result := bcTable.InsertRecordNoDefault(bcDatabase.TransactionInfo,{!!.10}
CursorID, aData, aLockType, NewRefNr);
if (Result = DBIERR_NONE) then begin
bcInfo.pos := cpOnRecord;
bcInfo.refNr := NewRefNr;
bcInfo.Deleted := false;
bcInfo.KeyValid := True;
{ Notify extenders of successful insert. }
NotifyExtenders(ffeaAfterRecInsert, ffeaNoAction);
end else
{ Notify extenders of failed insert. }
NotifyExtenders(ffeaInsertRecFail, ffeaNoAction);
end;
end;
{--------}
function TffSrSimpleCursor.IsInRange(aKey : PffByteArray) : integer;
begin
{ This class does not support ranges. }
Result := 0;
FFRaiseException(EffException, ffStrResServer, fferrRangeNotSupported,
[bcTable.BaseName, Self.ClassName]);
end;
{--------}
function TffSrSimpleCursor.ModifyRecord(aData : PffByteArray; aRelLock : boolean) : TffResult;
var {!!.05}
aKeyChanged: Boolean; {!!.05}
begin
{ Note: By this time, any other cursor deleting or modifying the record ahead
of us has completed and has set bcInfo.Deleted. We can be assured of this
because TffServerEngine.RecordDelete calls Cursor.EnsureWritable(true) which
obtains a lock on the record to be deleted. We won't get that lock until
the other cursor has finished. }
{ Has this record already been deleted? }
if bcInfo.Deleted then begin
{ Yes. }
Result := DBIERR_KEYORRECDELETED;
Exit;
end;
{ Are we on a record? }
if (bcInfo.Pos <> cpOnRecord) then begin
{ No. }
case bcInfo.Pos of
cpBOF : Result := DBIERR_BOF;
cpEOF : Result := DBIERR_EOF;
else
Result := DBIERR_NOCURRREC;
end;
Exit;
end;
{ Notify extenders. }
Result := NotifyExtenders(ffeaBeforeRecUpdate, ffeaUpdateRecFail);
if Result = DBIERR_NONE then begin
AcqContentLock(ffclmWrite);
Result := bcTable.PutRecord(bcDatabase.TransactionInfo, CursorID, bcInfo.refNr,
aData, aRelLock, aKeyChanged); {!!.05}
if (Result = DBIERR_NONE) then begin
bcInfo.KeyValid := True;
bcInfo.pos := cpOnRecord;
{ Notify extenders of successful update. }
NotifyExtenders(ffeaAfterRecUpdate, ffeaNoAction);
end else
{ Notify extenders of failed update. }
NotifyExtenders(ffeaUpdateRecFail, ffeaNoAction);
end;
end;
{--------}
procedure TffSrSimpleCursor.Open(const aTableName : TffTableName;
const aIndexName : TffName;
const aIndexID : Longint;
const aOpenMode : TffOpenMode;
aShareMode : TffShareMode;
aForServer : boolean;
const aExclContLock : Boolean; {!!.10}
aAttribs : TffFileAttributes);
begin
inherited Open(aTableName, aIndexName, aIndexID, aOpenMode, aShareMode,
aForServer, aExclContLock, aAttribs); {!!.10}
SetToBegin;
end;
{--------}
procedure TffSrSimpleCursor.ResetRange;
begin
FFRaiseException(EffException, ffStrResServer, fferrRangeNotSupported,
[bcTable.BaseName, Self.ClassName]);
end;
{--------}
function TffSrSimpleCursor.SetRange(aDirectKey : boolean;
aFieldCount1 : integer;
aPartialLen1 : integer;
aKeyData1 : PffByteArray;
aKeyIncl1 : boolean;
aFieldCount2 : integer;
aPartialLen2 : integer;
aKeyData2 : PffByteArray;
aKeyIncl2 : boolean) : TffResult;
begin
Result := DBIERR_FF_RangeNotSupported;
FFRaiseException(EffException, ffStrResServer, fferrRangeNotSupported,
[bcTable.BaseName, Self.ClassName]);
end;
{--------}
procedure TffSrSimpleCursor.SetToBegin;
begin
AcqContentLock(ffclmRead);
try
bcInfo.Deleted := False;
bcInfo.KeyValid := False;
bcInfo.Pos := cpBOF;
FFInitI64(bcInfo.RefNr);
finally
RelContentLock(ffclmRead);
end;
end;
{--------}
function TffSrSimpleCursor.SetToBookmark(aBookmark : PffByteArray) : TffResult;
begin
Result := CheckBookmark(aBookmark);
if (Result = DBIERR_NONE) then begin
AcqContentLock(ffclmRead);
try
{ Initialize the key path. }
FFInitKeyPath(bcInfo.KeyPath);
with PffSrBookmark(aBookmark)^ do begin
bcInfo.Pos := sbPos;
bcInfo.RefNr := sbRefNr;
bcInfo.KeyValid := sbKeyValid;
bcInfo.Deleted := false;
{ Does the record still exist? }
try
bcTable.GetRecord(bcDatabase.TransactionInfo, {!!.10}
bcDatabase.DatabaseID, {!!.10}
CursorID, sbRefNr, {!!.10}
bcRecordData, ffsltNone, false, false); {!!.02}
except
on E:EffException do begin
if E.ErrorCode = fferrRecDeleted then begin
bcInfo.Pos := cpOnCrack;
bcInfo.Deleted := True;
end
else begin
SetToBegin;
Result := DBIERR_INVALIDBOOKMARK;
end;
end
else begin
SetToBegin;
Result := DBIERR_INVALIDBOOKMARK;
end;
end;
end; { with }
finally
RelContentLock(ffclmRead);
end;
end;
end;
{--------}
function TffSrSimpleCursor.SetToCursor(aCursor : TffSrBaseCursor) : TffResult;
begin
Result := DBIERR_NONE;
if (aCursor.Table <> Table) then begin
Result := DBIERR_DIFFERENTTABLES;
Exit;
end;
AcqContentLock(ffclmRead);
try
bcInfo := aCursor.CursorInfo;
finally
RelContentLock(ffclmRead);
end;
end;
{--------}
procedure TffSrSimpleCursor.SetToEnd;
begin
AcqContentLock(ffclmRead);
try
bcInfo.Pos := cpEOF;
bcInfo.KeyValid := False;
FFInitI64(bcInfo.refNr);
bcInfo.Deleted := false;
finally
RelContentLock(ffclmRead);
end;
end;
{--------}
function TffSrSimpleCursor.SetToKey(aSearchAction : TffSearchKeyAction;
aDirectKey : boolean;
aFieldCount : integer;
aPartialLen : integer;
aKeyData : PffByteArray) : TffResult;
begin
{ To set to a specific record, specify a value for the RefNr property. }
Result := DBIERR_INVALIDINDEXCREATE;
FFRaiseException(EffException, ffStrResServer, fferrIndexNotSupported,
[bcTable.BaseName, Self.ClassName]);
end;
{--------}
function TffSrSimpleCursor.SwitchToIndex(aIndexID : integer;
aPosnOnRec : boolean) : TffResult;
begin
Result := DBIERR_INVALIDINDEXCREATE;
FFRaiseException(EffException, ffStrResServer, fferrIndexNotSupported,
[bcTable.BaseName, Self.ClassName]);
end;
{====================================================================}
end.