WalnutDBOpsImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Willie-Sue, September 9, 1986 4:25:26 pm PDT
Donahue, May 22, 1986 1:17:05 pm PDT
Contents: types and procedures dealing with the Walnut message database
Initiated by Willie-Sue, September 24, 1984
DIRECTORY
Atom USING [GetPName],
BasicTime USING [GMT],
DB USING [Aborted, CopyEntity, Error, ErrorCode, Failure,
CreateRelship, DeclareEntity, DomainSubset, EntityInfo, FirstRelship, GetF, GetSegmentInfo,
LookupEntity, LookupProperty, NextEntity, NullDomain, ReleaseEntitySet, SetF,
B2V, E2V, I2V, L2VS, S2V, T2V, V2B, V2I, V2S, V2T,
Entity, EntitySet, Relship, ValueSequence],
IO,
RefTab USING [Ref],
Rope,
WalnutDB,
WalnutDefs USING [Error, MsgSet, Segment, ServerInfo],
WalnutKernelDefs USING [LogExpungePhase],
WalnutRoot USING [CommitAndContinue, StatsReport],
WalnutSchema;
WalnutDBOpsImpl: CEDAR PROGRAM
IMPORTS
Atom, DB, IO,
WalnutDB, WalnutDefs, WalnutRoot, WalnutSchema
EXPORTS WalnutDB =
BEGIN OPEN WalnutSchema;
Types
GMT: TYPE = BasicTime.GMT;
ROPE: TYPE = Rope.ROPE;
MsgSet: TYPE = WalnutDefs.MsgSet;
Entity: TYPE = DB.Entity;
EntitySet: TYPE = DB.EntitySet;
Relship: TYPE = DB.Relship;
ServerInfo: TYPE = WalnutDefs.ServerInfo;
Public Variables
walnutSegment: PUBLIC ATOM ← $Walnut;
activeMsgSet: PUBLIC MsgSet ← ["Active", -1];
deletedMsgSet: PUBLIC MsgSet ← ["Deleted", -1];
activeMessageSet: PUBLIC Entity;
deletedMessageSet: PUBLIC Entity;
unacceptedEntity: PUBLIC Entity;
msgSetsTable: PUBLIC RefTab.Ref ← NIL;
debugging: BOOLFALSE;
DBErrorSeen: SIGNAL = CODE;
Database initialization
DeclareDB: PUBLIC PROC[segment: WalnutDefs.Segment, schemaInvalid: BOOL] = {
IF schemaInvalid OR DB.NullDomain[MsgSetDomain] THEN
InitSchema[segment, schemaInvalid]
ELSE {
walnutSegment ← segment;
msgSetsTable ← NIL;
};
};
InitSchema: PUBLIC PROC[segment: WalnutDefs.Segment, schemaInvalid: BOOLTRUE] = {
init: DB.ValueSequence;
fullNeeded: BOOL;
aaName: ATOM = WalnutDB.CanonicalName[activeMsgSet.name];
caName: ROPE = Atom.GetPName[aaName];
walnutSegment ← segment;
msgSetsTable ← NIL;
IF schemaInvalid THEN {
WalnutSchema.SetSchemaVersion[walnutSegment];
WalnutRoot.CommitAndContinue[];
};
WalnutSchema.Initialize[segment];
fullNeeded ← (DB.LookupEntity[MsgSetDomain, caName] = NIL);
BEGIN
adName: ATOM = WalnutDB.CanonicalName[deletedMsgSet.name];
e: Entity ← DB.DeclareEntity[MsgSetDomain, caName];
activeMessageSet ← DB.CopyEntity[e];
e ← DB.DeclareEntity[MsgSetDomain, Atom.GetPName[adName]];
deletedMessageSet ← DB.CopyEntity[e];
END;
unacceptedEntity ← DB.CopyEntity[DB.DeclareEntity[UnacceptedDomain, "TRUE"]];
IF fullNeeded THEN {
init ← DB.L2VS[LIST [
DB.E2V[activeMessageSet],
[integer[0]],
[integer[1]],
[rope[activeMsgSet.name]]
] ];
[] ← DB.CreateRelship[msBasicInfo, init];
init ← DB.L2VS[LIST [
DB.E2V[deletedMessageSet],
[integer[0]],
[integer[1]],
[rope[deletedMsgSet.name]]
] ];
[] ← DB.CreateRelship[msBasicInfo, init];
};
};
Global Values
IsReadOnly: PUBLIC PROC RETURNS[readonly: BOOL] =
{ readonly← DB.GetSegmentInfo[walnutSegment].readOnly };
GetDBName: PUBLIC PROC RETURNS[fileName: ROPE] =
{ fileName← DB.GetSegmentInfo[walnutSegment].filePath };
GetRootInfo: PUBLIC PROC RETURNS[rootFileStamp: GMT, rootFileKey, mailFor: ROPE] = {
Gri: PROC = {
rRootInfo: Relship = DB.FirstRelship[gRootInfo];
rootFileStamp ← DB.V2T[DB.GetF[rRootInfo, gRootFileStamp]];
rootFileKey ← DB.V2S[DB.GetF[rRootInfo, gRootFileKey]];
mailFor ← DB.V2S[DB.GetF[rRootInfo, gMailFor]];
};
CarefullyApply[Gri];
};
SetRootInfo: PUBLIC PROC[rootFileStamp: GMT, rootFileKey, mailFor: ROPE] = {
Sri: PROC = {
rRootInfo: Relship = DB.FirstRelship[gRootInfo];
DB.SetF[rRootInfo, gRootFileStamp, DB.T2V[rootFileStamp]];
DB.SetF[rRootInfo, gRootFileKey, DB.S2V[rootFileKey]];
DB.SetF[rRootInfo, gMailFor, DB.S2V[mailFor]];
};
CarefullyApply[Sri];
};
GetRootFileVersion: PUBLIC PROC RETURNS[rootFileStamp: GMT] = {
Gri: PROC = {
rRootInfo: Relship = DB.FirstRelship[gRootInfo];
rootFileStamp ← DB.V2T[DB.GetF[rRootInfo, gRootFileStamp]];
};
CarefullyApply[Gri];
};
SetRootFileVersion: PUBLIC PROC[rootFileStamp: GMT] = {
Sri: PROC = {
rRootInfo: Relship = DB.FirstRelship[gRootInfo];
DB.SetF[rRootInfo, gRootFileStamp, DB.T2V[rootFileStamp]];
};
CarefullyApply[Sri];
};
GetOpInProgressPos: PUBLIC PROC RETURNS[inProgressPos: INT] = {
Goip: PROC = {
rLogInfo: Relship = DB.FirstRelship[gLogInfo];
inProgressPos← DB.V2I[DB.GetF[rLogInfo, gOpInProgressPos]]
};
CarefullyApply[Goip];
};
SetOpInProgressPos: PUBLIC PROC[inProgressPos: INT] = {
Siop: PROC = {
rLogInfo: Relship = DB.FirstRelship[gLogInfo];
DB.SetF[rLogInfo, gOpInProgressPos, DB.I2V[inProgressPos]];
};
CarefullyApply[Siop];
};
GetParseLogInProgress: PUBLIC PROC RETURNS[inProgress: BOOL] = {
Gplp: PROC = {
rParseLogInfo: Relship = DB.FirstRelship[gParseLogInfo];
inProgress← DB.V2B[DB.GetF[rParseLogInfo, gParseLogInProgress]]
};
CarefullyApply[Gplp];
};
SetParseLogInProgress: PUBLIC PROC[inProgress: BOOL] = {
Splp: PROC = {
rParseLogInfo: Relship = DB.FirstRelship[gParseLogInfo];
DB.SetF[rParseLogInfo, gParseLogInProgress, DB.B2V[inProgress]];
};
CarefullyApply[Splp];
};
GetParseLogPos: PUBLIC PROC RETURNS[pos: INT] = {
Gplp: PROC = {
rParseLogInfo: Relship = DB.FirstRelship[gParseLogInfo];
pos ← DB.V2I[DB.GetF[rParseLogInfo, gParseLogPos]]
};
CarefullyApply[Gplp];
};
SetParseLogPos: PUBLIC PROC[pos: INT] = {
Splp: PROC = {
rParseLogInfo: Relship = DB.FirstRelship[gParseLogInfo];
DB.SetF[rParseLogInfo, gParseLogPos, DB.I2V[pos]];
};
CarefullyApply[Splp];
};
CarefullyApply: PUBLIC PROC [proc: PROC[]] = {
exp: ROPE;
eCode: ATOM;
BEGIN ENABLE BEGIN
DB.Error => {
IF debugging THEN SIGNAL DBErrorSeen;
eCode ← $DBError;
exp ← RopeForDBError[code];
GOTO Quit
};
DB.Failure => { eCode ← $DBFailure; exp ← info; GOTO Quit};
DB.Aborted => { eCode ← $TransactionAbort; GOTO Quit };
END;
proc[];
EXITS
Quit => {
WalnutRoot.StatsReport[IO.PutFR["\n ***DB error - code: %g, exp: %g ",
IO.atom[eCode], IO.rope[exp]]];
ERROR WalnutDefs.Error[$db, eCode, exp];
};
END;
};
GetMsgDisplayInfo: PUBLIC PROC[m: Entity]
RETURNS[hasBeenRead: BOOL, tocEntry: ROPE, startOfSubject: INT] = {
rel: Relship = DB.LookupProperty[mDisplayInfo, m];
hasBeenRead← DB.V2B[DB.GetF[rel, mDIHasBeenRead]];
tocEntry← DB.V2S[DB.GetF[rel, mDITOCEntry]];
startOfSubject← DB.V2I[DB.GetF[rel, mDIStartOfSubject]];
};
ChangeCountInMsgSet: PUBLIC PROC[ms: Entity, inc: INT] = {
rel: Relship = DB.LookupProperty[msBasicInfo, ms];
new: INTDB.V2I[DB.GetF[rel, msBICount]] + inc;
IF new < 0 THEN new ← 0;  -- empty didn't used to change the count!
DB.SetF[rel, msBICount, DB.I2V[new]];
new ← DB.V2I[DB.GetF[rel, msBIVersion]] + 1;
DB.SetF[rel, msBIVersion, DB.I2V[new]];
};
ChangeCountOfMsgs: PUBLIC PROC[delta: INT] = {
rVersionInfo: Relship = DB.FirstRelship[gVersionInfo];
numNow: INT = DB.V2I[DB.GetF[rVersionInfo, gMsgCount]];
DB.SetF[rVersionInfo, gMsgCount, DB.I2V[numNow + delta]];
};
Internal procedures
RopeForDBError: PROC[code: DB.ErrorCode] RETURNS[ROPE] = {
SELECT code FROM
AlreadyExists => RETURN["AlreadyExists"];
IllegalAttribute => RETURN["IllegalAttribute"];
IllegalDomain => RETURN["IllegalDomain"];
IllegalEntity => RETURN["IllegalEntity"];
IllegalProperty => RETURN["IllegalRelship"];
IllegalRelation => RETURN["IllegalRelation"];
IllegalString => RETURN["IllegalString"];
MismatchedExistingRelation => RETURN["MismatchedExistingRelation"];
MismatchedExistingSegment => RETURN["MismatchedExistingSegment"];
MultipleMatch => RETURN["MultipleMatch"];
NILArgument => RETURN["NILArgument"];
NullifiedArgument => RETURN["NullifiedArgument"];
NotFound => RETURN["NotFound"];
TransactionNotOpen => RETURN["TransactionNotOpen"];
TransactionAlreadyOpen => RETURN["TransactionAlreadyOpen"];
QuotaExceeded => RETURN["QuotaExceeded"];
Unknown => RETURN["Unknown"];
ENDCASE => RETURN["Other Error"];
};
Operations used during Expunge, NewMail and reading Archive files
GetExpungeInfo: PUBLIC PROC RETURNS[
firstDestroyedMsgPos, bytesInDestroyedMsgs: INT] = {
Gei: PROC = {
rLogInfo: Relship = DB.FirstRelship[gLogInfo];
firstDestroyedMsgPos ← DB.V2I[DB.GetF[rLogInfo, gFirstDestroyedMsgPos]];
bytesInDestroyedMsgs ← DB.V2I[DB.GetF[rLogInfo, gBytesInDestroyedMsgs]]
};
WalnutDB.CarefullyApply[Gei];
};
SetExpungeInfo: PUBLIC PROC[firstDestroyedMsgPos, bytesInDestroyedMsgs: INT] = {
Sei: PROC = {
rLogInfo: Relship = DB.FirstRelship[gLogInfo];
DB.SetF[rLogInfo, gFirstDestroyedMsgPos, DB.I2V[firstDestroyedMsgPos]];
DB.SetF[rLogInfo, gBytesInDestroyedMsgs, DB.I2V[bytesInDestroyedMsgs]];
};
WalnutDB.CarefullyApply[Sei];
};
GetLogExpungePhase: PUBLIC PROC RETURNS[exp: WalnutKernelDefs.LogExpungePhase] = {
Gep: PROC = {
rExpungeInfo: Relship = DB.FirstRelship[gExpungeInfo];
ph: INT DB.V2I[DB.GetF[rExpungeInfo, gLogExpungePhase]];
SELECT ph FROM
0 => exp← idle;
1 => exp← initializingExpungeLog;
2 => exp← writingExpungeLog;
3 => exp← swappingLogs;
ENDCASE => {
SIGNAL WalnutDefs.Error[$db, $Bug, IO.PutFR[badExpVal, IO.int[ph]] ];
exp← idle
};
};
WalnutDB.CarefullyApply[Gep];
};
badExpVal: ROPE = "Bad expungePhase value (%g)";
SetLogExpungePhase: PUBLIC PROC[exp: WalnutKernelDefs.LogExpungePhase] = {
Sep: PROC = {
rExpungeInfo: Relship = DB.FirstRelship[gExpungeInfo];
val: INT;
SELECT exp FROM
idle => val ← 0;
initializingExpungeLog => val ← 1;
writingExpungeLog => val ← 2;
swappingLogs => val ← 3;
ENDCASE => {
ph: INTEGERLOOPHOLE[exp];
SIGNAL WalnutDefs.Error[$db, $Bug,IO.PutFR[badExpVal, IO.int[val]] ];
val ← 0;
};
DB.SetF[rExpungeInfo, gLogExpungePhase, DB.I2V[val]];
};
WalnutDB.CarefullyApply[Sep];
};
GetExpungeProgressInfo: PUBLIC PROC RETURNS[currentLogPos, expungeLogPos: INT] = {
Gepi: PROC = {
rExpungeInfo: Relship = DB.FirstRelship[gExpungeInfo];
currentLogPos ← DB.V2I[DB.GetF[rExpungeInfo, gCurrentLogPos]];
expungeLogPos ← DB.V2I[DB.GetF[rExpungeInfo, gExpungeLogPos]];
};
WalnutDB.CarefullyApply[Gepi];
};
SetExpungeProgressInfo: PUBLIC PROC[currentLogPos, expungeLogPos: INT] = {
Sepi: PROC = {
rExpungeInfo: Relship = DB.FirstRelship[gExpungeInfo];
DB.SetF[rExpungeInfo, gCurrentLogPos, DB.I2V[currentLogPos]];
DB.SetF[rExpungeInfo, gExpungeLogPos, DB.I2V[expungeLogPos]];
};
WalnutDB.CarefullyApply[Sepi];
};
GetExpungeFileID: PUBLIC PROC RETURNS[expungeFileID: INT] = {
Gefi: PROC = {
rExpungeInfo: Relship = DB.FirstRelship[gExpungeInfo];
expungeFileID ← DB.V2I[DB.GetF[rExpungeInfo, gExpungeFileID]]
};
WalnutDB.CarefullyApply[Gefi];
};
SetExpungeFileID: PUBLIC PROC[expungeFileID: INT] = {
Sefi: PROC = {
rExpungeInfo: Relship = DB.FirstRelship[gExpungeInfo];
DB.SetF[rExpungeInfo, gExpungeFileID, DB.I2V[expungeFileID]]
};
WalnutDB.CarefullyApply[Sefi];
};
GetTimeOfLastExpunge: PUBLIC PROC RETURNS[when: GMT] = {
Gtle: PROC = {
rExpungeInfo: Relship = DB.FirstRelship[gExpungeInfo];
when ← DB.V2T[DB.GetF[rExpungeInfo, gTimeOfLastExpunge]]
};
WalnutDB.CarefullyApply[Gtle];
};
SetTimeOfLastExpunge: PUBLIC PROC[when: GMT] = {
Stle: PROC = {
rExpungeInfo: Relship = DB.FirstRelship[gExpungeInfo];
DB.SetF[rExpungeInfo, gTimeOfLastExpunge, DB.T2V[when]]
};
WalnutDB.CarefullyApply[Stle];
};
GetTimeOfLastScavenge: PUBLIC PROC RETURNS[when: GMT] = {
Gtls: PROC = {
rLogInfo: Relship = DB.FirstRelship[gLogInfo];
when ← DB.V2T[DB.GetF[rLogInfo, gTimeOfLastScavenge]]
};
WalnutDB.CarefullyApply[Gtls];
};
SetTimeOfLastScavenge: PUBLIC PROC[when: GMT] = {
Stls: PROC = {
rLogInfo: Relship = DB.FirstRelship[gLogInfo];
DB.SetF[rLogInfo, gTimeOfLastScavenge, DB.T2V[when]]
};
WalnutDB.CarefullyApply[Stls];
};
SetAcceptNewMailPos: PUBLIC PROC[pos: INT] = {
Sanmp: PROC = {
rNewMailInfo: Relship = DB.FirstRelship[gNewMailInfo];
DB.SetF[rNewMailInfo, gAcceptNewMailLogPos, DB.I2V[pos]]
};
WalnutDB.CarefullyApply[Sanmp];
};
GetAcceptNewMailPos: PUBLIC PROC RETURNS[pos: INT] = {
Ganmp: PROC = {
rNewMailInfo: Relship = DB.FirstRelship[gNewMailInfo];
pos ← DB.V2I[DB.GetF[rNewMailInfo, gAcceptNewMailLogPos]]
};
WalnutDB.CarefullyApply[Ganmp];
};
SetAddingServerMsgs: PUBLIC PROC[addingServerMsgs: BOOL] = {
Sasm: PROC = {
rNewMailInfo: Relship = DB.FirstRelship[gNewMailInfo];
DB.SetF[rNewMailInfo, gAddingServerMsgs, DB.B2V[addingServerMsgs]]
};
WalnutDB.CarefullyApply[Sasm];
};
GetAddingServerMsgs: PUBLIC PROC RETURNS[addingServerMsgs: BOOL] = {
Sasm: PROC = {
rNewMailInfo: Relship = DB.FirstRelship[gNewMailInfo];
addingServerMsgs← DB.V2B[DB.GetF[rNewMailInfo, gAddingServerMsgs]]
};
WalnutDB.CarefullyApply[Sasm];
};
EnumerateServers: PUBLIC PROC RETURNS[serverList: LIST OF ServerInfo] = {
Gsi: PROC = {
enum: EntitySet ← DB.DomainSubset[ServerDomain, NIL, NIL, First];
se: Entity;
FOR se ← DB.NextEntity[enum], DB.NextEntity[enum] UNTIL se = NIL DO
rel: Relship = DB.LookupProperty[sBasicInfo, se];
si: ServerInfo;
si.server ← DB.EntityInfo[se].name;
si.num ← DB.V2I[DB.GetF[rel, sBINum]];
serverList ← CONS[si, serverList];
ENDLOOP;
DB.ReleaseEntitySet[enum];
};
WalnutDB.CarefullyApply[Gsi];
};
GetServerInfo: PUBLIC PROC[server: ROPE] RETURNS[num: INT] = {
Gsr: PROC = {
se: Entity = DB.LookupEntity[ServerDomain, server];
IF se # NIL THEN {
rel: Relship = DB.LookupProperty[sBasicInfo, se];
num ← DB.V2I[DB.GetF[rel, sBINum]]
}
ELSE num ← 0;
};
WalnutDB.CarefullyApply[Gsr];
};
SetNewMailInfo: PUBLIC PROC[logLen: INT, when: GMT, server: ROPE, num: INT] = {
Snmi: PROC = {
rNewMailInfo: Relship = DB.FirstRelship[gNewMailInfo];
IF server # NIL THEN {
se: Entity ← DB.LookupEntity[ServerDomain, server];
IF se = NIL THEN {  -- create this entity
init: DB.ValueSequence = DB.L2VS[LIST[
DB.E2V[DB.DeclareEntity[ServerDomain, server]],
DB.I2V[num]
] ];
[] ← DB.CreateRelship[sBasicInfo, init];
}
ELSE {
rel: Relship = DB.LookupProperty[sBasicInfo, se];
DB.SetF[rel, sBINum, DB.I2V[num]];
};
};
DB.SetF[rNewMailInfo, gNewMailLogLength, DB.I2V[logLen]];
DB.SetF[rNewMailInfo, gLastNewMailTimeStamp, DB.T2V[when]]
};
WalnutDB.CarefullyApply[Snmi];
};
SetNewMailLogLength: PUBLIC PROC[logLen: INT] = {
Gnml: PROC = {
rNewMailInfo: Relship = DB.FirstRelship[gNewMailInfo];
DB.SetF[rNewMailInfo, gNewMailLogLength, DB.I2V[logLen]];
};
WalnutDB.CarefullyApply[Gnml];
};
GetNewMailLogLength: PUBLIC PROC RETURNS[logLen: INT] = {
Gnml: PROC = {
rNewMailInfo: Relship = DB.FirstRelship[gNewMailInfo];
logLen ← DB.V2I[DB.GetF[rNewMailInfo, gNewMailLogLength]]
};
WalnutDB.CarefullyApply[Gnml];
};
GetLastNewMailTimeStamp: PUBLIC PROC RETURNS[when: GMT] = {
Glnmts: PROC = {
rNewMailInfo: Relship = DB.FirstRelship[gNewMailInfo];
when ← DB.V2T[DB.GetF[rNewMailInfo, gLastNewMailTimeStamp]]
};
WalnutDB.CarefullyApply[Glnmts];
};
SetCopyMailLogPos: PUBLIC PROC[pos: INT] = {
Scml: PROC = {
rNewMailInfo: Relship = DB.FirstRelship[gNewMailInfo];
DB.SetF[rNewMailInfo, gCopyNewMailLogPos, DB.I2V[pos]]
};
WalnutDB.CarefullyApply[Scml];
};
GetCopyMailLogPos: PUBLIC PROC RETURNS[pos: INT] = {
Gcml: PROC = {
rNewMailInfo: Relship = DB.FirstRelship[gNewMailInfo];
pos ← DB.V2I[DB.GetF[rNewMailInfo, gCopyNewMailLogPos]]
};
WalnutDB.CarefullyApply[Gcml];
};
SetReadArchivePos: PUBLIC PROC[pos: INT] = {
Srap: PROC = {
rReadArchiveInfo: Relship = DB.FirstRelship[gReadArchiveInfo];
DB.SetF[rReadArchiveInfo, gReadArchiveLogPos, DB.I2V[pos]]
};
WalnutDB.CarefullyApply[Srap];
};
GetReadArchivePos: PUBLIC PROC RETURNS[pos: INT] = {
Grap: PROC = {
rReadArchiveInfo: Relship = DB.FirstRelship[gReadArchiveInfo];
pos ← DB.V2I[DB.GetF[rReadArchiveInfo, gReadArchiveLogPos]]
};
WalnutDB.CarefullyApply[Grap];
};
SetCopyReadArchivePos: PUBLIC PROC[pos: INT] = {
Scra: PROC = {
rReadArchiveInfo: Relship = DB.FirstRelship[gReadArchiveInfo];
DB.SetF[rReadArchiveInfo, gCopyReadArchiveLogPos, DB.I2V[pos]]
};
WalnutDB.CarefullyApply[Scra];
};
GetCopyReadArchivePos: PUBLIC PROC RETURNS[pos: INT] = {
Gcra: PROC = {
rReadArchiveInfo: Relship = DB.FirstRelship[gReadArchiveInfo];
pos ← DB.V2I[DB.GetF[rReadArchiveInfo, gCopyReadArchiveLogPos]]
};
WalnutDB.CarefullyApply[Gcra];
};
END.