KeyNoteDatabaseImpl.mesa
Copyright Ó 1984, 1987 by Xerox Corporation. All rights reserved.
Jack Kent February 23, 1988 10:16:35 am PST
DIRECTORY
AlpineFile,
Ascii,
DB,
DBCommon,
DBDefs,
List,
Process,
Rope,
RPC,
KeyNoteDatabase;
KeyNoteDatabaseImpl: CEDAR PROGRAM
IMPORTS AlpineFile, DB, DBCommon, List, Process, Rope, RPC
EXPORTS KeyNoteDatabase = {
Types
ROPE: TYPE = Rope.ROPE;
DatabaseHandle: TYPE = REF DatabaseHandleObject;
DatabaseHandleObject: PUBLIC TYPE = RECORD [
segment: DB.Segment,
fileName: ROPE,
readOnly: BOOLEAN,
transactionInfo: TransactionInfo
];
TransactionInfo: TYPE = REF TransactionInfoObject;
TransactionInfoObject: PRIVATE TYPE = RECORD [
transactionHandle: DB.TransactionHandle,
tokenDomain: DB.Domain,
fileNameDomain: DB.Domain,
tokenFileNameRelation: DB.Relation,
fileUniverseRelation: DB.Relation,
tokenUniverseRelation: DB.Relation,
metaDataRelation: DB.Relation,
metaDataRelShip: DB.Relship,
tokenFileNameIndex: DB.Index
];
database type info needed my many procedures for field access
tfnrTokenField: CARDINAL = 0;
tfnrFileNameField: CARDINAL = 1;
tfnrFrequencyField: CARDINAL = 2;
turTokenField: CARDINAL = 0;
turFrequencyField: CARDINAL = 1;
fuFileName: CARDINAL = 0;
fuSizeInTokens: CARDINAL = 1;
metaDataNumOfTokensField: CARDINAL = 0;
metaDataNumOfFilesField: CARDINAL = 1;
VersionStatusOfFileToBeChecked: PRIVATE TYPE = {notInDB, inDBasOldVersion, inDBasCurrentVersion};
Errors
Error: PUBLIC SIGNAL [ec: KeyNoteDatabase.ErrorCode, explanation: ROPENIL] = CODE;
Public procedures for database abminstration
if oldDatabaseHandle is NOT NIL then we are simply rebuilding a database handle and databaseHandle will be returned as NIL.
OpenDatabase: PUBLIC PROC [databaseName: ROPE, readonly: BOOLEAN, oldDatabaseHandle: DatabaseHandle] RETURNS [databaseHandle : DatabaseHandle ← NIL] = {
ENABLE {
UNWIND => NULL;
DBCommon.InternalError, DB.Error, DB.Failure => { GO TO CypressProblems};
};
transHandle: DB.TransactionHandle;
schemaInvalid: BOOLEAN;
DB.Initialize[nCachePages: 1000];
special check for error during open as this is the only easy way to distinguish error with transaction from error without transaction. For erro without transaction we can simply raise KeyNoteDatabase.Error (without cleanup) with impunity.
DB.DeclareSegment[filePath: databaseName, segment: $Test, readonly: readonly, createIfNotFound: TRUE, nPagesInitial: 3000, nPagesPerExtent: 256];
[trans: transHandle, schemaInvalid: schemaInvalid] ← DB.OpenTransaction[segment: $Test];
 {
tokenDomain: DB.Domain ← DB.DeclareDomain["token", $Test, FALSE];
fileNameDomain: DB.Domain ← DB.DeclareDomain["fileName", $Test, FALSE];
TypeProc: PROC[domain: DB.Domain] RETURNS[DB.TypeCode] =
{ RETURN[DB.TypeForDomain[domain]] };
TokenType: DB.TypeSpec = [direct[TypeProc[tokenDomain]]];
FileNameType: DB.TypeSpec = [direct[TypeProc[fileNameDomain]]];
tokenFileNameType: DB.FieldSpec =
DB.L2FS[LIST[
[name: "token", type: TokenType ],
[name: "fileName", type: FileNameType ],
[name: "frequency", type: DB.Integer]
]];
tokenUniverseType: DB.FieldSpec =
DB.L2FS[LIST[
[name: "token", type: TokenType ],
[name: "frequency", type: DB.Integer]
]];
fileUniverseType: DB.FieldSpec =
DB.L2FS[LIST[
[name: "fileName", type: FileNameType ],
[name: "sizeInTokens", type: DB.Integer]
]];
metaDataType: DB.FieldSpec =
DB.L2FS[LIST[
[name: "NumOfTokens", type: DB.Integer],
[name: "NumOfFiles", type: DB.Integer]
]];
tokenFileNameRelation: DB.Relation ← DB.DeclareRelation[name: "TokenFileName", segment: $Test, fields: tokenFileNameType];
fileUniverseRelation: DB.Relation ← DB.DeclareProperty[name: "FileNameUniverse", segment: $Test, fields: fileUniverseType];
tokenUniverseRelation: DB.Relation ← DB.DeclareProperty[name: "TokenUniverse", segment: $Test, fields: tokenUniverseType];
metaDataRelation: DB.Relation ← DB.DeclareRelation[name: "MetaData", segment: $Test, fields: metaDataType];
valList: LIST OF DBDefs.Value ← LIST[DB.I2V[0], DB.I2V[0]];
valSeq: DB.ValueSequence ← DB.L2VS[vals: valList];
tempRelShip: DB.Relship;
metaDataRelShip: DB.Relship ← IF (tempRelShip ← DB.FirstRelship[metaDataRelation])=NIL
THEN DB.CreateRelship[r: metaDataRelation, init: valSeq]
ELSE tempRelShip;
tokenFileNameFieldSequence: DB.FieldSequence =
DB.L2F[LIST[tfnrTokenField, tfnrFileNameField]];
tokenFileNameIndex: DB.Index ← DB.DeclareIndex[tokenFileNameRelation, tokenFileNameFieldSequence];
IF oldDatabaseHandle = NIL THEN
databaseHandle ← NEW [DatabaseHandleObject ← [segment: $Test, fileName: databaseName, readOnly: readonly, transactionInfo : NEW[ TransactionInfoObject ← [transactionHandle: transHandle, tokenDomain: tokenDomain, fileNameDomain: fileNameDomain, tokenFileNameRelation: tokenFileNameRelation, fileUniverseRelation: fileUniverseRelation, tokenUniverseRelation: tokenUniverseRelation, metaDataRelation: metaDataRelation, metaDataRelShip: metaDataRelShip, tokenFileNameIndex: tokenFileNameIndex]] ] ]
ELSE
oldDatabaseHandle.transactionInfo ← NEW[ TransactionInfoObject ← [transactionHandle: transHandle, tokenDomain: tokenDomain, fileNameDomain: fileNameDomain, tokenFileNameRelation: tokenFileNameRelation, fileUniverseRelation: fileUniverseRelation, tokenUniverseRelation: tokenUniverseRelation, metaDataRelation: metaDataRelation, metaDataRelShip: metaDataRelShip, tokenFileNameIndex: tokenFileNameIndex]];
};
EXITS
CypressProblems => {
the same as above but pass back different error
DB.CloseTransaction[db.transactionHandle];
SIGNAL Error[$CypressProblems];
};
};
CloseDatabase: PUBLIC PROC [db: DatabaseHandle] = {
DB.CloseTransaction[trans: db.transactionInfo.transactionHandle];
};
Abort: PUBLIC PROC [db: DatabaseHandle] = {
DB.AbortTransaction[trans: db.transactionInfo.transactionHandle];
};
public procedures that operate on database
KeyNote maintains the invariant that at any time...the database will only contain 1 version of each fileName. All names AND counts are consistent after each commit.
AddTokensToDatabase: PUBLIC PROC [db: DatabaseHandle, fileName: ROPE, getTokenProc: KeyNoteDatabase.GetTokenProc, clientData: REF ANY] = {
OPEN db.transactionInfo;
AddTokensToDatabaseCarefully: PROC = {
fne: DB.Entity;
fileIncludingVersionNumberAlreadyInDatabase: BOOLEAN;
[fne, fileIncludingVersionNumberAlreadyInDatabase] ← GetFileNameEntityAndRemoveOldVersionAsSideEffect[db, fileName];
IF ~ fileIncludingVersionNumberAlreadyInDatabase THEN {
fileNameVal: DB.Value ← DB.E2V[fne];
numberOfTokensInFile: INTEGER ← 0;
FOR tokenProcReturnData: KeyNoteDatabase.TokenProcReturnData ← getTokenProc[clientData], getTokenProc[tokenProcReturnData.newClientData] WHILE tokenProcReturnData#NIL DO
tokenVal: DBDefs.Value ← DB.E2V[GetEntity[db,tokenDomain, tokenProcReturnData.token]];
freqVal: DBDefs.Value ← DB.I2V[tokenProcReturnData.frequency];
valList: LIST OF DBDefs.Value ← LIST[tokenVal, fileNameVal, freqVal];
valSeq: DB.ValueSequence ← DB.L2VS[vals: valList];
tokenUniverseRelShip: DB.Relship ← DB.LookupProperty[tokenUniverseRelation, DB.V2E[tokenVal]];
numberOfTokensInFile ← numberOfTokensInFile +1;
make appropriate entry into tokenFileNameRelation
[] ← DB.CreateRelship[r: tokenFileNameRelation, init: valSeq];
and make appropriate entry into tokenUniverseRelation
[must look at old value as we're bumping it up]
IF tokenUniverseRelShip=NIL THEN {
tokenUniverseValList: LIST OF DBDefs.Value ← LIST[tokenVal, DB.I2V[1]];
tokenUniverseValSeq: DB.ValueSequence ← DB.L2VS[vals: tokenUniverseValList];
[] ← DB.CreateRelship[r: tokenUniverseRelation, init: tokenUniverseValSeq];
}
ELSE {
tokenUniverseFrequency: INTDB.V2I[DB.GetF[tokenUniverseRelShip, turFrequencyField]] + 1;
DB.SetF[tokenUniverseRelShip, turFrequencyField, DB.I2V[tokenUniverseFrequency] ];
};
ENDLOOP;
{
valList: LIST OF DBDefs.Value ← LIST[DB.E2V[fne], DB.I2V[numberOfTokensInFile]];
valSeq: DB.ValueSequence ← DB.L2VS[vals: valList];
relShip: DB.Relship ← DB.CreateRelship[r: fileUniverseRelation, init: valSeq];
};
}
};
CarefullyApply[AddTokensToDatabaseCarefully, TRUE, db];
};
assume that filename exists and that is contains token: otherwsie error
FindFrequencyWordInFile: PUBLIC PROC [db: DatabaseHandle, token: ROPE, fileName: ROPE] RETURNS [frequencyOfWordInFile: INTEGER] = {
OPEN db.transactionInfo;
FindFrequencyWordInFileCarefully: PROC = {
fileNameEntity: DB.Entity ← DB.LookupEntity[fileNameDomain, fileName];
tokenEntity: DB.Entity ← DB.LookupEntity[tokenDomain, token];
constraint: DB.Constraint ← DB.L2C[LIST[ [entity[tokenEntity]], [entity[fileNameEntity]] ] ];
enum: DB.RelshipSet ←
DB.RelationSubset[tokenFileNameRelation, tokenFileNameIndex, constraint, First];
rel: DB.Relship = DB.NextRelship[enum];
frequencyOfWordInFile ← DB.V2I[DB.GetF[rel, tfnrFrequencyField]];
};
CarefullyApply[FindFrequencyWordInFileCarefully, FALSE, db];
};
FindFrequencyWordInUniverse: PUBLIC PROC [db: DatabaseHandle, token: ROPE] RETURNS [frequencyOfWordInFile: INTEGER] = {
OPEN db.transactionInfo;
FindFrequencyWordInUniverseCarefully: PROC = {
tokenEntity: DB.Entity ← DB.LookupEntity[tokenDomain, token];
IF tokenEntity=NIL THEN frequencyOfWordInFile ← 0
ELSE {
relShip: DB.Relship ← DB.LookupProperty[tokenUniverseRelation, tokenEntity];
frequencyOfWordInFile ← DB.V2I[DB.GetF[relShip, turFrequencyField]];
};
};
CarefullyApply[FindFrequencyWordInUniverseCarefully, FALSE, db];
};
assume file exists in database... else error
FindSizeOfFile: PUBLIC PROC [db: DatabaseHandle, fileName: ROPE] RETURNS [sizeOfFileInPages: INTEGER] = {
OPEN db.transactionInfo;
FindSizeOfFileCarefully: PROC = {
fileEntity: DB.Entity ← DB.LookupEntity[fileNameDomain, fileName];
IF fileEntity=NIL THEN ERROR
ELSE {
relShip: DB.Relship ← DB.LookupProperty[tokenUniverseRelation, fileEntity];
sizeOfFileInPages ← DB.V2I[DB.GetF[relShip, fuSizeInTokens]];
};
};
CarefullyApply[FindSizeOfFileCarefully, FALSE, db];
};
GetListOfFilesContainingToken: PUBLIC PROC [db: DatabaseHandle, token: ROPE] RETURNS [listOfFilesContainingToken: KeyNoteDatabase.ListOfFilesContainingToken ← NIL] = {
OPEN db.transactionInfo;
GetListOfFilesContainingTokenCarefully: PROC = {
tokenEntity: DB.Entity ← DB.LookupEntity[tokenDomain, token];
relationshipSet: DB.RelshipSet;
IF tokenEntity = NIL THEN RETURN;
relationshipSet ← DB.RelshipsWithEntityField[r: tokenFileNameRelation, field: tfnrTokenField, val: tokenEntity];
DO
frequency: INTEGER;
fileSize: INTEGER;
fileNameEntity: DB.Entity;
fileName: ROPE;
relationship: DB.Relship ← DB.NextRelship[relationshipSet];
IF relationship = NIL THEN {DB.ReleaseRelshipSet[relationshipSet]; EXIT};
fileNameEntity ← DB.V2E[DB.GetF[relationship, tfnrFileNameField]];
fileName ← DB.EntityInfo[e: fileNameEntity].name;
fileSize ← DB.V2I[DB.GetF[ DB.LookupProperty[fileUniverseRelation, fileNameEntity], fuSizeInTokens]];
frequency ← DB.V2I[DB.GetF[relationship, tfnrFrequencyField]];
listOfFilesContainingToken ← CONS [ NEW[KeyNoteDatabase.FilesContainingTokenObject ← [fileName: fileName, frequency: frequency, fileSize: fileSize]], listOfFilesContainingToken];
ENDLOOP;
};
CarefullyApply[GetListOfFilesContainingTokenCarefully, FALSE, db];
};
GetListOfTokensInFile: PUBLIC PROC [db: DatabaseHandle, fileName: ROPE] RETURNS [listOfTokensInFile: KeyNoteDatabase.ListOfTokensInFile ← NIL] = {
OPEN db.transactionInfo;
GetListOfTokensInFileCarefully: PROC = {
fileNameEntity: DB.Entity ← DB.LookupEntity[fileNameDomain, fileName];
relationshipSet: DB.RelshipSet;
IF fileNameEntity = NIL THEN RETURN;
relationshipSet ← DB.RelshipsWithEntityField[r: tokenFileNameRelation, field: tfnrFileNameField, val: fileNameEntity];
DO
frequency: INTEGER;
frequencyInUniverse: INTEGER;
tokenEntity: DB.Entity;
token: ROPE;
relationship: DB.Relship ← DB.NextRelship[relationshipSet];
IF relationship = NIL THEN {DB.ReleaseRelshipSet[relationshipSet]; EXIT};
tokenEntity ← DB.V2E[DB.GetF[relationship, tfnrTokenField]];
token ← DB.EntityInfo[e: tokenEntity].name;
frequencyInUniverse ← DB.V2I[DB.GetF[ DB.LookupProperty[tokenUniverseRelation, tokenEntity], turFrequencyField]];
frequency ← DB.V2I[DB.GetF[relationship, tfnrFrequencyField]];
listOfTokensInFile ← CONS [ NEW[KeyNoteDatabase.TokensInFileObject ← [token: token, frequency: frequency, frequencyInUniverse: frequencyInUniverse]], listOfTokensInFile];
ENDLOOP;
};
CarefullyApply[GetListOfTokensInFileCarefully, FALSE, db];
};
GetNumberOfTokensInUniverse: PUBLIC PROC [db: DatabaseHandle] RETURNS [numberOfTokensInUniverse: INTEGER ← 0] = {
OPEN db.transactionInfo;
GetNumberOfTokensInUniverseCarefully: PROC = {
numberOfTokensInUniverse ←
DB.V2I[DB.GetF[metaDataRelShip, metaDataNumOfTokensField]];
};
CarefullyApply[GetNumberOfTokensInUniverseCarefully, FALSE, db];
};
GetNumberOfFilesInUniverse: PUBLIC PROC [db: DatabaseHandle] RETURNS [numberOfFilesInUniverse: INTEGER ← 0] = {
OPEN db.transactionInfo;
GetNumberOfFilesInUniverseCarefully: PROC = {
numberOfFilesInUniverse ← DB.V2I[DB.GetF[metaDataRelShip, metaDataNumOfFilesField]];
};
CarefullyApply[GetNumberOfFilesInUniverseCarefully, FALSE, db];
};
VerifyFileNonExistence: PUBLIC PROC [db: DatabaseHandle, fileName: ROPE] RETURNS [fileNotInDatabase: BOOLEAN] = {
OPEN db.transactionInfo;
entity: DB.Entity;
versionStatusOfFileToBeChecked: VersionStatusOfFileToBeChecked;
first...check to see that file is not already in database
[entity: entity, versionStatusOfFileToBeChecked: versionStatusOfFileToBeChecked] ← VerifyOnenessOrNoneness[db, fileName];
fileNotInDatabase ← IF versionStatusOfFileToBeChecked=inDBasCurrentVersion THEN FALSE
ELSE TRUE;
};
private procedures
GetFileNameEntityAndRemoveOldVersionAsSideEffect: PRIVATE PROC [db: DatabaseHandle, fileName: ROPE] RETURNS [entity: DB.Entity, fileIncludingVersionNumberAlreadyInDatabase: BOOLEAN] = {
OPEN db.transactionInfo;
versionStatusOfFileToBeChecked: VersionStatusOfFileToBeChecked;
first...check to see that file is not already in database
[entity: entity, versionStatusOfFileToBeChecked: versionStatusOfFileToBeChecked] ← VerifyOnenessOrNoneness[db, fileName];
SELECT versionStatusOfFileToBeChecked FROM
notInDB => {
fileIncludingVersionNumberAlreadyInDatabase ← FALSE;
entity ← DeclareEntity[db, fileNameDomain, fileName];
};
inDBasOldVersion => {
RetractFileNameFromDatabase[db, DB.EntityInfo[entity].name];
fileIncludingVersionNumberAlreadyInDatabase ← FALSE;
entity ← DeclareEntity[db, fileNameDomain, fileName];
};
inDBasCurrentVersion => {
fileIncludingVersionNumberAlreadyInDatabase ← TRUE;
};
ENDCASE => NULL;
};
verifies that there is not more than one version of fileName in the database. If so, then raises Error[ec: $MultipleVerions, explanation: "fileName"]
VerifyOnenessOrNoneness: PRIVATE PROC [db: DatabaseHandle, fileName: ROPE] RETURNS [entity: DB.Entity, versionStatusOfFileToBeChecked: VersionStatusOfFileToBeChecked] = {
OPEN db.transactionInfo;
now check to see if there is an old version of file in
fileNameStrippedOfVersionInfo: ROPE ← Rope.Substr[base: fileName, start: 0, len: Rope.FindBackward[s1: fileName, s2: "!"]];
cursor: DB.EntitySet ← DB.DomainSubset[d: fileNameDomain, lowName: fileNameStrippedOfVersionInfo, highName:Rope.Concat[base: fileNameStrippedOfVersionInfo, rest: Rope.FromChar[Ascii.DEL]]];
oldEntity: DB.Entity ← DB.NextEntity[cursor];
oldFileName: ROPEDB.EntityInfo[oldEntity].name;
oldFileNameStrippedOfVersionInfo: ROPE ← Rope.Substr[base: oldFileName, start: 0, len: Rope.FindBackward[s1: oldFileName, s2: "!"]];
IF Rope.Equal[s1: fileName, s2: oldFileName]
THEN RETURN [oldEntity, inDBasCurrentVersion]
ELSE {
oldFileNameStrippedOfVersionInfo: ROPE ← Rope.Substr[base: oldFileName, start: 0, len: Rope.FindBackward[s1: oldFileName, s2: "!"]];
IF ~ Rope.Equal[s1: fileNameStrippedOfVersionInfo, s2: oldFileNameStrippedOfVersionInfo]
THEN RETURN [NIL, notInDB]
ELSE {
if another file then error
nextFileName: ROPEDB.EntityInfo[DB.NextEntity[cursor]].name;
nextFileNameStrippedOfVersionInfo: ROPE ← Rope.Substr[base: nextFileName, start: 0, len: Rope.FindBackward[s1: nextFileName, s2: "!"]];
IF Rope.Equal[nextFileNameStrippedOfVersionInfo, oldFileNameStrippedOfVersionInfo]
THEN ERROR Error[$MultipleVersions]
ELSE RETURN [oldEntity, inDBasOldVersion];
};
};
};
assume entity exists
RetractFileNameFromDatabase: PRIVATE PROC [db: DatabaseHandle, fileName: ROPE] = {
OPEN db.transactionInfo;
RetractFromTokenUniverse: PROC [item: REF ANY, list: List.LORA] = {
token: ROPENARROW[item, REF KeyNoteDatabase.TokensInFileObject].token;
tokenVal: DBDefs.Value ← DB.E2V[DB.LookupEntity[tokenDomain, token]];
tokenUniverseRelShip: DB.Relship ← DB.LookupProperty[tokenUniverseRelation, DB.V2E[tokenVal]];
tokenUniverseFrequency: INTDB.V2I[DB.GetF[tokenUniverseRelShip, turFrequencyField]] - 1;
DB.SetF[tokenUniverseRelShip, turFrequencyField, DB.I2V[tokenUniverseFrequency] ];
};
listOfTokensInFile: KeyNoteDatabase.ListOfTokensInFile ← GetListOfTokensInFile[db, fileName];
List.Map[list: listOfTokensInFile, proc: RetractFromTokenUniverse];
now destroy the fileName entity after we've made the counts consistent
DestroyEntity[db, DB.LookupEntity[fileNameDomain, fileName]];
};
GetEntity: PRIVATE PROC [db: DatabaseHandle, domain: DB.Domain, name: ROPE ] RETURNS [entity: DB.Entity] = {
OPEN db.transactionInfo;
IF (entity ← DB.LookupEntity[domain, name])#NIL THEN RETURN [entity] ELSE RETURN DeclareEntity[db, domain, name];
};
as side effects, changes count of meta-data relation
DeclareEntity: PRIVATE PROC [db: DatabaseHandle, domain: DB.Domain, name: ROPE ] RETURNS [entity: DB.Entity] = {
OPEN db.transactionInfo;
SELECT domain FROM
tokenDomain => {
DB.SetF[metaDataRelShip, metaDataNumOfTokensField, DB.I2V[DB.V2I[DB.GetF[metaDataRelShip, metaDataNumOfTokensField]] + 1] ];
};
fileNameDomain => {
DB.SetF[metaDataRelShip, metaDataNumOfFilesField, DB.I2V[DB.V2I[DB.GetF[metaDataRelShip, metaDataNumOfFilesField]] + 1] ];
};
ENDCASE => NULL;
RETURN DB.DeclareEntity[domain, name];
};
as side effects, changes count of meta-data relation
DestroyEntity: PRIVATE PROC [db: DatabaseHandle, e: DB.Entity ] = {
OPEN db.transactionInfo;
SELECT DB.EntityInfo[e].domain FROM
tokenDomain => {
DB.SetF[metaDataRelShip, metaDataNumOfTokensField, DB.I2V[DB.V2I[DB.GetF[metaDataRelShip, metaDataNumOfTokensField]] - 1] ];
};
fileNameDomain => {
DB.SetF[metaDataRelShip, metaDataNumOfFilesField, DB.I2V[DB.V2I[DB.GetF[metaDataRelShip, metaDataNumOfFilesField]] - 1] ];
};
ENDCASE => NULL;
DB.DestroyEntity[e];
};
transaction stuff: swiped for the most part from Walnut (our case is a lot simpler though as we don't deal with multiple Alpine entities (i.e. a log and a database) and we don't maintain schema time stamps
TransRequest: TYPE = { abort, close, continue };
CarefullyApply: PUBLIC PROC[proc: PROC[], didUpdate: BOOL, db: DatabaseHandle] = {
reTryCount: INT ← 10;
schemaInvalid: BOOLTRUE;
DO
BEGIN ENABLE
DB.Aborted => {
IF ( reTryCount ← reTryCount - 1) > 0 THEN GOTO retry;
errorInProgress ← TRUE;
REJECT
};
DB.Error, DB.Failure => {
errorInProgress ← TRUE;
REJECT
};
proc[];
IF didUpdate THEN FinishTransaction[db , continue];
RETURN;
EXITS
retry => NULL;
END;
FinishTransaction[db , abort];
[] ← OpenDatabase[db.fileName, db.readOnly, db];
ENDLOOP;
};
FinishTransaction: PROC[db: DatabaseHandle, request: TransRequest] = {
eCode: ATOM;
BEGIN ENABLE BEGIN
DB.Aborted => {eCode ← $TransactionAbort; GOTO error };
DB.Error => {eCode ← $DBError; GOTO error };
DB.Failure => {eCode ← $DatabaseInaccessible; GOTO error };
RPC.CallFailed => {eCode ← $RPCCallFailed; GOTO error };
AlpineFile.Unknown =>
{ eCode ← IF what = transID THEN $ServerBusy ELSE $Unknown; GOTO error };
END;
SELECT request FROM
abort => DB.AbortTransaction[db.transactionInfo.transactionHandle];
continue => {
DB.MarkTransaction[db.transactionInfo.transactionHandle];
why was the next line needed... to prevent starvation of other transactions
from long-writing transaction?
AlpTransaction.UnlockOwnerDB[rootLog.alpTH, currentVolumeGroupID];
};
close => DB.CloseTransaction[db.transactionInfo.transactionHandle];
ENDCASE =>
Error[$bug, "Bad value passed to FinishTransaction"];
EXITS
error => ERROR Error[$db, "Can't access transaction"];
END;
};
Process.CheckForAbort[];
}.