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: ROPE ← NIL] = 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: INT ← DB.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
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: ROPE ← DB.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: ROPE ← DB.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: ROPE ← NARROW[item, REF KeyNoteDatabase.TokensInFileObject].token;
tokenVal: DBDefs.Value ← DB.E2V[DB.LookupEntity[tokenDomain, token]];
tokenUniverseRelShip: DB.Relship ← DB.LookupProperty[tokenUniverseRelation, DB.V2E[tokenVal]];
tokenUniverseFrequency: INT ← DB.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: BOOL ← TRUE;
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;
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;
};