WalnutDBMsgSetsImpl.mesa
Copyright Ó 1984, 1988, 1989, 1992 by Xerox Corporation. All rights reserved.
Willie-Sue, March 29, 1989 6:04:55 pm PST
Donahue, May 12, 1986 2:13:42 pm PDT
Jack Kent, June 23, 1987 6:57:27 pm PDT
Doug Terry, August 28, 1990 5:35:08 pm PDT
TerryTest, November 15, 1990 12:40 pm PST
Contents: types and procedures dealing with the Walnut message database
Initiated by Willie-Sue, September 24, 1984
Swinehar, February 25, 1991 9:50 am PST
DIRECTORY
Ascii USING [Lower],
Atom USING [MakeAtomFromRefText, GetPName],
IO,
LoganBerry,
LoganBerryEntry,
RefTab USING [Ref, Val, Create, Delete, Fetch, Store],
RefText USING [line, InlineAppendChar],
Rope,
WalnutDefs USING [dontCareDomainVersion, dontCareMsgSetVersion, Error, MsgSet, VersionMismatch, WalnutOpsHandle],
WalnutDB,
WalnutRegistry USING [MsgGroup, MsgGroupSize],
WalnutRegistryPrivate USING [ CheckForMsgGroupRegistration, NotifyForEvent, NotifyForMove, NotifyForMsgEvent, NotifyForMsgGroup],
WalnutRoot USING [CommitAndContinue, RootHandle],
WalnutSchema;
Types
ROPE: TYPE = Rope.ROPE;
Relship: TYPE = LoganBerry.Entry;
WalnutOpsHandle: TYPE = WalnutDefs.WalnutOpsHandle;
RootHandle: TYPE = WalnutRoot.RootHandle;
SchemaHandle: TYPE = WalnutSchema.SchemaHandle;
SchemaHandleRec: PUBLIC TYPE = WalnutSchema.SchemaHandleRec;
MsgSet: TYPE = WalnutDefs.MsgSet;
dontCareDomainVersion: INT = WalnutDefs.dontCareDomainVersion;
dontCareMsgSetVersion: INT = WalnutDefs.dontCareMsgSetVersion;
CheckReportProc: TYPE = WalnutDB.CheckReportProc;
Internal Types and Variables
Value: TYPE = LoganBerry.AttributeValue;
DBMinusOneInt: Value ¬ LoganBerryEntry.I2V[-1];
DBZeroInt: Value ¬ LoganBerryEntry.I2V[0];
DBTrueBool: Value ¬ LoganBerryEntry.B2V[TRUE];
DBFalseBool: Value ¬ LoganBerryEntry.B2V[FALSE];
nullValue: Value = NIL;
ActiveMsgSetName: ROPE = "Active";
DeletedMsgSetName: ROPE = "Deleted";
timeToDelete: CARD16 ¬ 40;
IsEntity: TYPE = RECORD [entity: LoganBerry.Entry, exists: BOOL];
MSInfo's are stored in the msgSetsTable
MSInfo: TYPE = REF MSInfoObject;
MSInfoObject: TYPE = RECORD [canonicalName: ROPE, entity: ROPE, versionRel: Relship];
nameText: REF TEXT = NEW[TEXT[RefText.line]];
LazyEnumerator: TYPE = REF LazyEnumeratorRec;
LazyEnumeratorRec:
PUBLIC
TYPE =
RECORD[
opsH: WalnutOpsHandle,
msgSet: MsgSet,
pos: INT,
set: LoganBerry.Cursor,
valid: BOOL,
checkShow: BOOL
];
Operations on Message Sets
MsgSetExists:
PUBLIC
PROC[opsH: WalnutOpsHandle, name:
ROPE, msDomainVersion:
INT]
RETURNS[existed:
BOOL, msVersion:
INT] = {
Does this message set already exist in the database.
msI: MSInfo;
CheckDomainVersion[opsH, msDomainVersion];
[msI, msVersion] ¬ GetMsgSetAndVersion[opsH, name];
existed ¬ msI # NIL;
};
CreateMsgSet:
PUBLIC
PROC[opsH: WalnutOpsHandle, name:
ROPE, msDomainVersion:
INT]
RETURNS [existed:
BOOL, msVersion:
INT] = {
Create this message set if it doesn't already exist in the database.
msI: MSInfo;
mse: ROPE;
cName: ROPE;
aName: ATOM;
sH: SchemaHandle = opsH.schemaHandle;
CheckDomainVersion[opsH, msDomainVersion];
[msI, msVersion] ¬ GetMsgSetAndVersion[opsH, name];
IF existed ¬ (msI # NIL) THEN RETURN;
cName ¬ Atom.GetPName[aName ¬ CanonicalName[name]];
mse ¬ cName;
msI ¬ NEW[MSInfoObject ¬ [canonicalName: cName, entity: mse, versionRel: NIL] ];
msI.versionRel ¬
LIST [
[$Key, Rope.Concat[sH.msBasicInfo, mse]],
[sH.msPrintNameIs, name],
[sH.msBICount, "0"],
[sH.msBIVersion, "1"]
];
LoganBerry.WriteEntry[db: opsH.db, entry: msI.versionRel];
ChangeGlobalMsgSetInfo[opsH, 1];
[] ¬ RefTab.Store[sH.msgSetsTable, aName, msI];
};
NumInMsgSet:
PUBLIC
PROC[opsH: WalnutOpsHandle, name:
ROPE]
RETURNS[num: INT, msVersion: INT] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
msI: MSInfo;
CountOne:
PROC[msg, tocEntry:
ROPE, hasBeenRead:
BOOL, startOfSubject:
INT]
RETURNS[continue: BOOL¬TRUE] ~ { num ¬ num+1; };
num ¬ 0;
[msI, msVersion] ¬ GetMsgSetAndVersion[opsH, name];
IF msI = NIL THEN num← 0
ELSE num ← LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[msI.versionRel, sH.msBICount]];
Replaced by:
IF msI # NIL THEN []¬EnumerateMsgsInSet[opsH, name, TRUE, CountOne];
Could now update msI.versionRel entry.
};
EmptyMsgSet:
PUBLIC
PROC[opsH: WalnutOpsHandle, msgSet: MsgSet, report: CheckReportProc]
RETURNS[someInDeleted: BOOL ¬ FALSE] = {
Removes any messages from msgSet - long running op
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
numIn: INT;
msI: MSInfo;
IF WalnutDB.EqMsgSets[msgSet.name, DeletedMsgSetName]
THEN {
WalnutDB.SetOpInProgressPos[opsH, -1];
ERROR WalnutDefs.Error[$db, $InvalidOperation, "Can't empty the Deleted MsgSet"];
};
msI ¬ CheckMsgSetEntity[opsH, msgSet];
IF msI = NIL THEN RETURN;
numIn ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[msI.versionRel, sH.msBICount]];
someInDeleted ¬ EmptyThisMsgSet[opsH, msI.entity, msgSet.name, report];
IF numIn # 0 THEN WalnutDB.ChangeCountInMsgSet[opsH, msI.entity, -numIn];
};
DestroyMsgSet:
PUBLIC
PROC[opsH: WalnutOpsHandle, msgSet: MsgSet, msDomainVersion:
INT, report: CheckReportProc]
RETURNS[someInDeleted: BOOL ¬ FALSE] = {
Destroys the given msgSet, removing any messages from it first (uses equivalent of RemoveMsgFromMsgSet below). If the message set is "Deleted" then WalnutDefs.IError[$db, $InvalidMsgSet] - long running op
msI: MSInfo;
sH: SchemaHandle = opsH.schemaHandle;
IF WalnutDB.EqMsgSets[msgSet.name, DeletedMsgSetName]
THEN {
WalnutDB.SetOpInProgressPos[opsH, -1];
ERROR WalnutDefs.Error[$db, $InvalidOperation, "Can't destroy the Deleted MsgSet"];
};
CheckDomainVersion[opsH, msDomainVersion];
msI ¬ CheckMsgSetEntity[opsH, msgSet];
IF msI = NIL THEN RETURN;
someInDeleted ¬ EmptyThisMsgSet[opsH, msI.entity, msgSet.name, report];
LoganBerry.DeleteEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.msBasicInfo, msI.entity]];
msI.versionRel ¬ NIL;
ChangeGlobalMsgSetInfo[opsH, -1];
};
VerifyMsgSet:
PUBLIC
PROC[opsH: WalnutOpsHandle, msgSet: MsgSet]
RETURNS[exists: BOOL] = {
msI: MSInfo ¬ CheckMsgSetEntity[opsH, msgSet];
exists ¬ msI # NIL;
};
VerifyDomainVersion:
PUBLIC
PROC[opsH: WalnutOpsHandle, msDomainVersion:
INT] =
{ CheckDomainVersion[opsH, msDomainVersion] };
ExpungeMsgs:
PUBLIC
PROC[opsH: WalnutOpsHandle, deletedVersion:
INT, report: CheckReportProc] = {
Destroys the Deleted message set - long running op
OPEN WalnutDB;
sH: SchemaHandle = opsH.schemaHandle;
msI: MSInfo = CheckMsgSetEntity[opsH, [DeletedMsgSetName, deletedVersion]];
BEGIN
deletedCount: INT = LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[msI.versionRel, sH.msBICount]];
rs: LoganBerry.Cursor ¬ LoganBerry.GenerateEntries[db: opsH.db, key: sH.cdMsgSet, start: sH.deletedEntity, end: sH.deletedEntity];
sinceLastCommit: INT ¬ 0;
commitFrequency: CARDINAL = WalnutRegistry.MsgGroupSize;
delArray: WalnutRegistry.MsgGroup;
numDel: CARDINAL ¬ 0;
count: INT ¬ 0;
doMsgGroup: BOOL ¬ WalnutRegistryPrivate.CheckForMsgGroupRegistration[];
IF doMsgGroup THEN delArray ¬ ALL[NIL];
If the counts are wrong, change them. They may already have been changed if you're restarting an Expunge
IF deletedCount # 0
THEN {
ChangeCountInMsgSet[opsH, sH.deletedEntity, -deletedCount];
ChangeCountOfMsgs[opsH, -deletedCount];
This counts as one operation
sinceLastCommit ¬ sinceLastCommit + 1
};
BEGIN
ENABLE
UNWIND =>
NULL;
rel: Relship;
rLogInfo: Relship = LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: sH.gLogInfo].entry;
bytesDestroyed: INT ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rLogInfo, sH.gBytesInDestroyedMsgs]];
firstDestroyedPos: INT ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rLogInfo, sH.gFirstDestroyedMsgPos]];
UNTIL (rel ¬ LoganBerry.NextEntry[cursor: rs]) =
NIL
DO
me: ROPE = LoganBerryEntry.GetAttr[rel, sH.cdMsg];
textRel: Relship = LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.mMsgInfo, me]].entry;
startPos: INT ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[textRel, sH.mMIEntryStart]];
thisLen:
INT ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[textRel, sH.mMITextOffset]] +
LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[textRel, sH.mMITextLen]] + LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[textRel, sH.mMIFormatLen]];
bytesDestroyed ¬ bytesDestroyed + thisLen;
IF firstDestroyedPos = 0
OR startPos < firstDestroyedPos
THEN
firstDestroyedPos ¬ startPos;
IF doMsgGroup
THEN {
delArray[numDel] ¬ me;
numDel ¬ numDel + 1;
};
Destroy all information about message
LoganBerry.DeleteEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.mMsgInfo, me]];
LoganBerry.DeleteEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.mDisplayInfo, me]];
LoganBerry.DeleteEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.mInfo, me]];
LoganBerry.DeleteEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.cdRelation, me]];
LoganBerry.DeleteEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.toRelation, me]];
LoganBerry.DeleteEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.ccRelation, me]];
LoganBerry.DeleteEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.fromRelation, me]];
IF (sinceLastCommit ¬ sinceLastCommit + 1) >= commitFrequency
THEN {
LoganBerryEntry.SetAttr[rLogInfo, sH.gBytesInDestroyedMsgs, LoganBerryEntry.I2V[bytesDestroyed]];
LoganBerryEntry.SetAttr[rLogInfo, sH.gFirstDestroyedMsgPos, LoganBerryEntry.I2V[firstDestroyedPos]];
LoganBerry.WriteEntry[db: opsH.db, entry: rLogInfo, replace: TRUE];
WalnutRoot.CommitAndContinue[opsH];
sinceLastCommit ¬ 0;
IF doMsgGroup
THEN {
WalnutRegistryPrivate.NotifyForMsgGroup[destroyed, delArray];
delArray ¬ ALL[NIL]; -- clear out the Array
numDel ¬ 0;
};
};
IF report # NIL THEN count ¬ CheckCount[opsH, count, report];
ENDLOOP;
LoganBerry.EndGenerate[cursor: rs];
IF sinceLastCommit # 0
THEN {
LoganBerryEntry.SetAttr[rLogInfo, sH.gBytesInDestroyedMsgs, LoganBerryEntry.I2V[bytesDestroyed]];
LoganBerryEntry.SetAttr[rLogInfo, sH.gFirstDestroyedMsgPos, LoganBerryEntry.I2V[firstDestroyedPos]];
LoganBerry.WriteEntry[db: opsH.db, entry: rLogInfo, replace: TRUE];
};
BEGIN
-- change the version number of Deleted
delRel: Relship = LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.msBasicInfo, sH.deletedEntity]].entry;
LoganBerryEntry.SetAttr[delRel, sH.msBIVersion, LoganBerryEntry.I2V[LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[delRel, sH.msBIVersion]] + 1]];
LoganBerry.WriteEntry[db: opsH.db, entry: delRel, replace: TRUE];
now change the length of the last log back to -1
WalnutRoot.CommitAndContinue[opsH];
IF doMsgGroup
AND (numDel # 0)
THEN
WalnutRegistryPrivate.NotifyForMsgGroup[destroyed, delArray];
WalnutRegistryPrivate.NotifyForEvent[expungeComplete];
END;
DestroyOrphanAddrsAndSubjs[opsH, report];
END;
END;
};
MsgSetsInfo:
PUBLIC
PROC[opsH: WalnutOpsHandle]
RETURNS[version, num:
INT] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
rVersionInfo: Relship = LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: sH.gVersionInfo].entry;
version ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rVersionInfo, sH.gMsgSetsVersion]];
num ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rVersionInfo, sH.gMsgSetCount]];
};
Operations to Move Messages Among Message Sets
AddMsg:
PUBLIC
PROC[opsH: WalnutOpsHandle, msg:
ROPE, from, to: MsgSet]
RETURNS[exists: BOOL] = {
Adds Msg to MsgSet, if it's not already in it. IF msgSet=deletedMsgSet, does nothing and returns exists=FALSE
m: IsEntity;
msI: MSInfo;
sH: SchemaHandle = opsH.schemaHandle;
exists ¬ FALSE;
IF WalnutDB.EqMsgSets[to.name, DeletedMsgSetName] THEN RETURN;
m ¬ GetMsgEntity[opsH, msg];
IF ~m.exists THEN RETURN;
[] ¬ CheckMsgSetEntity[opsH, from];
msI ¬ CheckMsgSetEntity[opsH, to];
IF msI = NIL THEN RETURN; -- can't create msgset here
{ rel: Relship ¬ LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.cdRelation, msg]].entry;
wasInDeleted: BOOL ¬ FALSE;
FOR msL:
LIST
OF
ROPE ¬
LoganBerryEntry.GetAllAttrs[rel, sH.cdMsgSet], msL.rest
WHILE msL #
NIL
DO
msgSet: ROPE ¬ msL.first;
IF Rope.Equal[msI.entity, msgSet, FALSE] THEN { exists ¬ TRUE; EXIT };
IF Rope.Equal[msgSet, sH.deletedEntity,
FALSE]
THEN {
rel ¬ LoganBerryEntry.RemoveAttr[rel, sH.cdMsgSet, msgSet];
LoganBerry.WriteEntry[db: opsH.db, entry: rel, replace: TRUE];
WalnutDB.ChangeCountInMsgSet[opsH, sH.deletedEntity, -1]
}
ENDLOOP;
};
IF NOT exists THEN AddMsgTo[opsH, msg, to.name];
};
RemoveMsg:
PUBLIC
PROC[opsH: WalnutOpsHandle, msg:
ROPE, from: MsgSet, deletedVersion:
INT]
RETURNS[deleted:
BOOL] = {
IF removing msg from msgSet would leave it in no MsgSet, then msg gets added to the distinguished MsgSet Deleted, and returns deleted = TRUE
m: IsEntity;
msI: MSInfo;
rel, thisCDRel: Relship;
sH: SchemaHandle = opsH.schemaHandle;
deleted ¬ FALSE;
IF NOT (m¬ GetMsgEntity[opsH, msg]).exists THEN RETURN;
IF (msI¬ CheckMsgSetEntity[opsH, from]) = NIL THEN RETURN;
deleted ¬ TRUE; -- We're committed to deleting the message unless we find it in another message set
rel ¬ LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.cdRelation, msg]].entry;
FOR msL:
LIST
OF
ROPE ¬
LoganBerryEntry.GetAllAttrs[rel, sH.cdMsgSet], msL.rest
WHILE msL #
NIL
DO
IF Rope.Equal[msI.entity, msL.first, FALSE] THEN thisCDRel ¬ rel
ELSE deleted ¬ FALSE
ENDLOOP;
IF thisCDRel = NIL THEN { deleted ¬ FALSE; RETURN }; -- Something strange here
WalnutDB.ChangeCountInMsgSet[opsH, msI.entity, -1];
IF
NOT deleted
THEN
{
thisCDRel ¬ LoganBerryEntry.RemoveAttr[thisCDRel, sH.cdMsgSet, msI.entity];
LoganBerry.WriteEntry[db: opsH.db, entry: thisCDRel, replace: TRUE];
WalnutDB.ChangeCountInMsgSet[opsH, msI.entity, -1];
}
ELSE {
LoganBerryEntry.SetAttr[thisCDRel, sH.cdMsgSet, sH.deletedEntity];
LoganBerry.WriteEntry[db: opsH.db, entry: thisCDRel, replace: TRUE];
};
};
MoveMsg:
PUBLIC
PROC[opsH: WalnutOpsHandle, msg:
ROPE, from: MsgSet, to: MsgSet]
RETURNS [exists: BOOL ¬ FALSE] = {
Move the message. Note that the result of a move may be that a message becomes deleted (if to was the Deleted message set) or undeleted (if from is the Deleted message set)
m: IsEntity;
fromMsI, toMsI: MSInfo;
sH: SchemaHandle = opsH.schemaHandle;
rel, fromCDRelship, toCDRelship: Relship;
IF WalnutDB.EqMsgSets[from.name, to.name] THEN RETURN[TRUE]; -- don't do anything
IF NOT (m ¬ GetMsgEntity[opsH, msg]).exists THEN RETURN;
IF (fromMsI ¬ CheckMsgSetEntity[opsH, from]) = NIL THEN RETURN;
IF (toMsI ¬ CheckMsgSetEntity[opsH, to]) = NIL THEN RETURN;
rel ¬ LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.cdRelation, msg]].entry;
FOR msL:
LIST
OF
ROPE ¬
LoganBerryEntry.GetAllAttrs[rel, sH.cdMsgSet], msL.rest
WHILE msL #
NIL
DO
IF Rope.Equal[fromMsI.entity, msL.first,
FALSE]
THEN
fromCDRelship ¬ rel
ELSE
IF Rope.Equal[toMsI.entity, msL.first,
FALSE]
THEN
toCDRelship ¬ rel;
ENDLOOP;
IF fromCDRelship = NIL THEN {exists ¬ FALSE; RETURN};
exists ¬ toCDRelship # NIL;
rel ¬ LoganBerryEntry.RemoveAttr[rel, sH.cdMsgSet, fromMsI.entity];
IF
NOT exists
THEN {
rel ¬ LoganBerryEntry.AddAttr[rel, sH.cdMsgSet, toMsI.entity];
};
LoganBerry.WriteEntry[db: opsH.db, entry: rel, replace: TRUE];
WalnutDB.ChangeCountInMsgSet[opsH, fromMsI.entity, -1];
IF NOT exists THEN WalnutDB.ChangeCountInMsgSet[opsH, toMsI.entity, 1];
};
Enumerations
MsgsEnumeration:
PUBLIC
PROC [opsH: WalnutOpsHandle, alphaOrder:
BOOL]
RETURNS [mL: LIST OF ROPE] = {
ok: BOOL ¬ FALSE;
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
MEnum:
PROC = {
last: LIST OF ROPE;
enum: LoganBerry.Cursor ¬
IF alphaOrder
THEN LoganBerry.GenerateEntries[db: opsH.db, key: $Key, start: Rope.Concat[sH.mMsgInfo, "\000"], end: Rope.Concat[sH.mMsgInfo, "\177"]]
ELSE LoganBerry.GenerateEntries[db: opsH.db, key: $Key, start: sH.mMsgInfo, end: Rope.Concat[sH.mMsgInfo, "\255"]];
mL ¬ NIL;
BEGIN
ENABLE
UNWIND =>
GOTO end;
e: LoganBerry.Entry;
msg: ROPE;
count: INT ¬ 0;
FOR e ¬ LoganBerry.NextEntry[cursor: enum], LoganBerry.NextEntry[cursor: enum]
UNTIL e =
NIL
DO
msg ¬ LoganBerryEntry.GetAttr[e, sH.mMIOf];
IF mL =
NIL
THEN mL ¬ last ¬
CONS[msg,
NIL]
ELSE
{ last.rest ¬ CONS[msg, NIL]; last ¬ last.rest};
count ¬ CheckForCommit[opsH, count];
ENDLOOP;
ok ¬ TRUE;
EXITS end => NULL;
END;
LoganBerry.EndGenerate[cursor: enum ! LoganBerry.Error => CONTINUE];
};
MEnum[];
IF ok
THEN
RETURN
ELSE WalnutDefs.Error[$db, $DatabaseInaccessible, "During Msgs enumeration"];
};
MsgsInSetEnumeration:
PUBLIC
PROC[opsH: WalnutOpsHandle, name:
ROPE, fromStart:
BOOL]
RETURNS [mL:
LIST
OF
ROPE, msVersion:
INT ¬ -1] = {
Note: fromStart is ignored.
ok: BOOL ¬ FALSE;
sH: SchemaHandle = opsH.schemaHandle;
MEnum:
PROC = {
msI: MSInfo;
enum: LoganBerry.Cursor;
checkShow: BOOL;
count: INT ¬ 0;
lastInList: LIST OF ROPE ¬ NIL;
mL ¬ NIL;
[msI, msVersion] ¬ GetMsgSetAndVersion[opsH, name];
IF msI = NIL THEN {ok ¬ TRUE; RETURN};
checkShow ¬ name.Equal["Active", FALSE];
enum ¬
LoganBerry.GenerateEntries[db: opsH.db, key: sH.cdMsgSet, start: msI.entity, end: msI.entity];
BEGIN
ENABLE
UNWIND =>
GOTO end;
DO
rel: Relship = LoganBerry.NextEntry[cursor: enum];
me: ROPE;
IF rel = NIL THEN EXIT;
me ¬ LoganBerryEntry.GetAttr[rel, sH.cdMsg];
IF checkShow
THEN {
sRel: Relship = LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.mInfo, me]].entry;
IF NOT Rope.Equal[LoganBerryEntry.GetAttr[sRel, sH.mShowIs], "NULL"] THEN LOOP; -- mShowIs is unaccepted
};
IF fromStart
THEN {
thisL: LIST OF ROPE = CONS[me, NIL];
IF mL =
NIL
THEN mL ¬ lastInList ¬ thisL
ELSE {
lastInList.rest ¬ thisL;
lastInList ¬ lastInList.rest;
};
}
ELSE mL ¬ CONS[me, mL];
count ¬ CheckForCommit[opsH, count];
ENDLOOP;
ok ¬ TRUE;
EXITS end => NULL;
END;
LoganBerry.EndGenerate[cursor: enum ! LoganBerry.Error => CONTINUE]
};
MEnum[];
IF ok
THEN
RETURN
ELSE WalnutDefs.Error[$db, $DatabaseInaccessible, "During MsgSets enumeration"];
};
MsgSetsNames:
PUBLIC
PROC[opsH: WalnutOpsHandle, alphaOrder:
BOOL]
RETURNS[msL: LIST OF ROPE, msDomainVersion: INT ¬ -1] = {
ok: BOOL ¬ FALSE;
sH: SchemaHandle = opsH.schemaHandle;
MSEnum:
PROC = {
last: LIST OF ROPE;
rVersionInfo: Relship = LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: sH.gVersionInfo].entry;
enum: LoganBerry.Cursor ¬
IF alphaOrder
THEN LoganBerry.GenerateEntries[db: opsH.db, key: $Key, start: Rope.Concat[sH.msBasicInfo, "\000"], end: Rope.Concat[sH.msBasicInfo, "\177"]]
ELSE LoganBerry.GenerateEntries[db: opsH.db, key: $Key, start: sH.msBasicInfo, end: Rope.Concat[sH.msBasicInfo, "\255"]];
msDomainVersion ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rVersionInfo, sH.gMsgSetsVersion]];
msL ¬ NIL;
BEGIN
ENABLE
UNWIND =>
GOTO end;
FOR e: LoganBerry.Entry ¬ LoganBerry.NextEntry[cursor: enum], LoganBerry.NextEntry[cursor: enum]
UNTIL e =
NIL
DO
thisName:
ROPE =
LoganBerryEntry.GetAttr[e, sH.msPrintNameIs];
IF msL =
NIL
THEN msL ¬ last ¬
CONS[thisName,
NIL]
ELSE
{ last.rest ¬ CONS[thisName, NIL]; last ¬ last.rest};
ENDLOOP;
ok ¬ TRUE;
EXITS end => NULL;
END;
LoganBerry.EndGenerate[cursor: enum ! LoganBerry.Error => CONTINUE]
};
MSEnum[];
IF ok
THEN
RETURN
ELSE WalnutDefs.Error[$db, $DatabaseInaccessible, "During MsgSets enumeration"];
};
EnumerateMsgSets:
PUBLIC
PROC[opsH: WalnutOpsHandle, alphaOrder:
BOOL, proc:
PROC[msgSet: MsgSet]
RETURNS[continue:
BOOL] ]
RETURNS[msDomainVersion:
INT ¬ -1] = {
ok: BOOL ¬ FALSE;
sH: SchemaHandle = opsH.schemaHandle;
MSEnum:
PROC = {
enum: LoganBerry.Cursor ¬
IF alphaOrder
THEN LoganBerry.GenerateEntries[db: opsH.db, key: $Key, start: Rope.Concat[sH.msBasicInfo, "\000"], end: Rope.Concat[sH.msBasicInfo, "\177"]]
ELSE LoganBerry.GenerateEntries[db: opsH.db, key: $Key, start: sH.msBasicInfo, end: Rope.Concat[sH.msBasicInfo, "\255"]];
rVersionInfo: Relship = LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: sH.gVersionInfo].entry;
msDomainVersion ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rVersionInfo, sH.gMsgSetsVersion]];
BEGIN
ENABLE
UNWIND =>
GOTO end;
FOR e: LoganBerry.Entry ¬ LoganBerry.NextEntry[cursor: enum], LoganBerry.NextEntry[cursor: enum]
UNTIL e =
NIL
DO
msgSet: MsgSet ¬
[LoganBerryEntry.GetAttr[e, sH.msPrintNameIs], LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[e, sH.msBIVersion]]];
IF NOT proc[msgSet] THEN EXIT;
ENDLOOP;
ok ¬ TRUE;
EXITS end => NULL;
END;
LoganBerry.EndGenerate[cursor: enum ! LoganBerry.Error => CONTINUE]
};
MSEnum[];
IF ok
THEN
RETURN
ELSE ERROR WalnutDefs.Error[$db, $DatabaseInaccessible, "During MsgSets enumeration"]
};
EnumerateMsgsInSet:
PUBLIC
PROC [
opsH: WalnutOpsHandle,
name:
ROPE,
fromStart:
BOOL ¬
TRUE,
proc:
PROC[msg, tocEntry:
ROPE, hasBeenRead:
BOOL, startOfSubject:
INT]
RETURNS[continue:
BOOL] ]
RETURNS [msVersion: INT ¬ -1] = {
Note: fromStart is ignored.
ok: BOOL ¬ FALSE;
sH: SchemaHandle = opsH.schemaHandle;
MEnum:
PROC = {
msI: MSInfo;
enum: LoganBerry.Cursor;
checkShow: BOOL;
count: INT ¬ 0;
[msI, msVersion] ¬ GetMsgSetAndVersion[opsH, name];
IF msI = NIL THEN {ok¬ TRUE; RETURN};
checkShow ¬ name.Equal["Active", FALSE];
enum ¬
LoganBerry.GenerateEntries[db: opsH.db, key: sH.cdMsgSet, start: msI.entity, end: msI.entity];
BEGIN
ENABLE
UNWIND =>
GOTO end;
DO
rel: Relship = LoganBerry.NextEntry[cursor: enum];
msg, tocEntry: ROPE;
hasBeenRead: BOOL;
startOfSubject: INT;
IF rel = NIL THEN EXIT;
msg ¬ LoganBerryEntry.GetAttr[rel, sH.cdMsg];
IF checkShow
THEN {
sRel: Relship = LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.mInfo, msg]].entry;
IF NOT Rope.Equal[LoganBerryEntry.GetAttr[sRel, sH.mShowIs], "NULL"] THEN LOOP; -- mShowIs is unaccepted
};
[hasBeenRead, tocEntry, startOfSubject] ¬ WalnutDB.GetMsgDisplayInfo[opsH, msg];
IF NOT proc[msg, tocEntry, hasBeenRead, startOfSubject] THEN EXIT;
count ¬ CheckForCommit[opsH, count];
ENDLOOP;
ok¬ TRUE;
EXITS end => NULL;
END;
LoganBerry.EndGenerate[cursor: enum ! LoganBerry.Error => CONTINUE]
};
MEnum[];
IF ok
THEN
RETURN
ELSE ERROR WalnutDefs.Error[$db, $DatabaseInaccessible, "During MsgSets enumeration"]
};
EnumerateMsgsInMsgSet:
PUBLIC
PROC[opsH: WalnutOpsHandle, msgSet: MsgSet]
RETURNS[lazyEnum: WalnutDB.LazyEnumerator] = {
sH: SchemaHandle = opsH.schemaHandle;
lazyEnum ¬ NEW[LazyEnumeratorRec];
lazyEnum.opsH ¬ opsH;
lazyEnum.msgSet ¬ msgSet;
lazyEnum.pos ¬ 0;
lazyEnum.checkShow ¬ Rope.Equal[msgSet.name, ActiveMsgSetName];
lazyEnum.set ¬
LoganBerry.GenerateEntries[db: opsH.db, key: sH.cdMsgSet, start: msgSet.name, end: msgSet.name];
lazyEnum.valid ¬ TRUE;
};
NextMsgInMsgSet:
PUBLIC
PROC[lazyEnum: WalnutDB.LazyEnumerator]
RETURNS[msgID: ROPE, valid: BOOL ¬ FALSE] = {
howManyIterations: INT ¬ 0;
sH: SchemaHandle = lazyEnum.opsH.schemaHandle;
GetNextMsg:
PROC = {
me: ROPE;
IF lazyEnum.valid = FALSE THEN { valid ¬ FALSE; RETURN };
DO
rel: Relship = LoganBerry.NextEntry[cursor: lazyEnum.set];
howManyIterations ¬ howManyIterations+1;
IF rel = NIL THEN { LoganBerry.EndGenerate[cursor: lazyEnum.set]; lazyEnum.valid ¬ FALSE; EXIT };
me ¬ LoganBerryEntry.GetAttr[rel, sH.cdMsg];
IF lazyEnum.checkShow
THEN {
sRel: Relship = LoganBerry.ReadEntry[db: lazyEnum.opsH.db, key: $Key, value: Rope.Concat[sH.mInfo, me]].entry;
IF NOT Rope.Equal[LoganBerryEntry.GetAttr[sRel, sH.mShowIs], "NULL"] THEN LOOP ELSE EXIT }
ELSE EXIT
ENDLOOP;
IF me # NIL THEN msgID ¬ me;
valid ¬ TRUE
};
ResetToStart:
PROC = {
lazyEnum.set ¬ LoganBerry.GenerateEntries[db: lazyEnum.opsH.db, key: sH.cdMsgSet, start: lazyEnum.msgSet.name, end: lazyEnum.msgSet.name];
FOR i: INT IN [0..lazyEnum.pos) DO [] ¬ LoganBerry.NextEntry[cursor: lazyEnum.set] ENDLOOP;
howManyIterations ¬ 0 };
IF NOT VerifyMsgSet[lazyEnum.opsH, lazyEnum.msgSet] THEN RETURN[NIL, FALSE];
FOR tryRestart:
BOOL ¬
TRUE,
FALSE
WHILE tryRestart
DO
failed: BOOL ¬ FALSE;
GetNextMsg[ ! LoganBerry.Error => { failed ¬ TRUE; CONTINUE } ];
IF NOT failed THEN { lazyEnum.pos ¬ lazyEnum.pos+howManyIterations; RETURN };
ResetToStart[]
ENDLOOP
};
EnumerateUnacceptedMsgs:
PUBLIC
PROC[opsH: WalnutOpsHandle, activeVersion:
INT, proc:
PROC[msg, tocEntry:
ROPE, startOfSubject:
INT] ] = {
ok: BOOL ¬ FALSE;
sH: SchemaHandle = opsH.schemaHandle;
Eum:
PROC = {
enum: LoganBerry.Cursor;
count: INT ¬ 0;
[] ¬ CheckMsgSetEntity[opsH, [ActiveMsgSetName, activeVersion]];
enum ¬ LoganBerry.GenerateEntries[db: opsH.db, key: sH.mShowIs, start: sH.unacceptedEntity, end: sH.unacceptedEntity];
BEGIN
ENABLE
UNWIND =>
GOTO end;
showRel: Relship;
msg: ROPE;
UNTIL (showRel ¬ LoganBerry.NextEntry[cursor: enum]) =
NIL
DO
tocEntry: ROPE;
startOfSubject: INT;
me: ROPE = LoganBerryEntry.GetAttr[showRel, sH.mInfoOf];
msg ¬ me;
[ , tocEntry, startOfSubject] ¬ WalnutDB.GetMsgDisplayInfo[opsH, me];
proc[msg, tocEntry, startOfSubject];
count ¬ CheckForCommit[opsH, count];
ENDLOOP;
ok¬ TRUE;
EXITS end => NULL;
END;
LoganBerry.EndGenerate[cursor: enum ! LoganBerry.Error => CONTINUE]
};
Eum[];
IF ok THEN RETURN;
ERROR WalnutDefs.Error[$db, $DatabaseInaccessable, "During Get New Mail"];
};
AcceptNewMail:
PUBLIC
PROC[opsH: WalnutOpsHandle, pos, activeVersion:
INT] = {
long running op
rs: LoganBerry.Cursor;
es: LoganBerry.Cursor;
commitFrequency: CARDINAL = WalnutRegistry.MsgGroupSize;
accArray: WalnutRegistry.MsgGroup;
numAcc: CARDINAL ¬ 0;
sinceLastCommit: INT ¬ 0;
sH: SchemaHandle = opsH.schemaHandle;
rNewMailInfo, rLogInfo: Relship;
activeRel: Relship = LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.msBasicInfo, sH.activeEntity]].entry;
doMsgGroup: BOOL ¬ WalnutRegistryPrivate.CheckForMsgGroupRegistration[];
[] ¬ CheckMsgSetEntity[opsH, [ActiveMsgSetName, activeVersion]];
IF doMsgGroup THEN accArray ¬ ALL[NIL];
BEGIN
ENABLE
UNWIND =>
NULL;
showRel: Relship;
rs ¬ LoganBerry.GenerateEntries[db: opsH.db, key: sH.mShowIs, start: sH.unacceptedEntity, end: sH.unacceptedEntity];
UNTIL (showRel ¬ LoganBerry.NextEntry[cursor: rs]) =
NIL
DO
me: ROPE = LoganBerryEntry.GetAttr[showRel, sH.mInfoOf];
LoganBerryEntry.SetAttr[showRel, sH.mShowIs, "NULL"];
LoganBerry.WriteEntry[db: opsH.db, entry: showRel, replace: TRUE];
IF doMsgGroup
THEN {
accArray[numAcc] ¬ me;
numAcc ¬ numAcc + 1;
};
IF (sinceLastCommit ¬ sinceLastCommit + 1) >= commitFrequency
THEN {
newCount: INT = LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[activeRel, sH.msBICount]] + sinceLastCommit;
LoganBerryEntry.SetAttr[activeRel, sH.msBICount, LoganBerryEntry.I2V[newCount]];
LoganBerry.WriteEntry[db: opsH.db, entry: activeRel, replace: TRUE];
WalnutRoot.CommitAndContinue[opsH];
sinceLastCommit ¬ 0;
IF doMsgGroup
THEN {
WalnutRegistryPrivate.NotifyForMsgGroup[added, accArray];
accArray ¬ ALL[NIL];
numAcc ¬ 0;
};
};
ENDLOOP;
LoganBerry.EndGenerate[cursor: rs];
END;
BEGIN
ENABLE
UNWIND =>
NULL;
es ¬ LoganBerry.GenerateEntries[db: opsH.db, key: $Key, start: Rope.Concat[sH.sBasicInfo, "\000"], end: Rope.Concat[sH.sBasicInfo, "\177"]];
FOR se: LoganBerry.Entry ¬ LoganBerry.NextEntry[cursor: es], LoganBerry.NextEntry[cursor: es]
UNTIL se =
NIL
DO
rel: Relship = se;
LoganBerryEntry.SetAttr[rel, sH.sBINum, DBZeroInt];
LoganBerry.WriteEntry[db: opsH.db, entry: rel, replace: TRUE];
ENDLOOP;
LoganBerry.EndGenerate[cursor: es];
END;
IF sinceLastCommit # 0
THEN
LoganBerryEntry.SetAttr[activeRel, sH.msBICount, LoganBerryEntry.I2V[LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[activeRel, sH.msBICount]] + sinceLastCommit]];
LoganBerryEntry.SetAttr[activeRel, sH.msBIVersion, LoganBerryEntry.I2V[LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[activeRel, sH.msBIVersion]] + 1]];
LoganBerry.WriteEntry[db: opsH.db, entry: activeRel, replace: TRUE];
rNewMailInfo ¬ LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: sH.gNewMailInfo].entry;
LoganBerryEntry.SetAttr[rNewMailInfo, sH.gAcceptNewMailLogPos, LoganBerryEntry.I2V[pos]];
LoganBerry.WriteEntry[db: opsH.db, entry: rNewMailInfo, replace: TRUE];
rLogInfo ¬ LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: sH.gLogInfo].entry;
LoganBerryEntry.SetAttr[rLogInfo, sH.gOpInProgressPos, DBMinusOneInt];
LoganBerry.WriteEntry[db: opsH.db, entry: rLogInfo, replace: TRUE];
WalnutRoot.CommitAndContinue[opsH];
IF doMsgGroup
AND (numAcc # 0)
THEN
WalnutRegistryPrivate.NotifyForMsgGroup[added, accArray];
};
Internal procedures
mismatchReport: ROPE = "Msgset: %g: version is %g, version expected is: %g";
GetMsgSetBasicInfoRel:
PROC[opsH: WalnutOpsHandle, ms:
ROPE]
RETURNS[rel: Relship] =
{ RETURN[LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[opsH.schemaHandle.msBasicInfo, ms]].entry] };
GetMsgEntity:
PROC[opsH: WalnutOpsHandle, msg:
ROPE]
RETURNS[e: IsEntity] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
e.entity ¬ LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.mMsgInfo, msg]].entry;
e.exists ¬ (e.entity # NIL);
};
GetMsgSetAndVersion:
PROC[opsH: WalnutOpsHandle, name:
ROPE]
RETURNS[msI: MSInfo, version: INT] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
msI ¬ GetMsgSetEntity[opsH, name];
IF msI #
NIL
THEN version ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[msI.versionRel, sH.msBIVersion]]
ELSE version¬ -1;
};
GetMsgSetEntity:
PROC[opsH: WalnutOpsHandle, name:
ROPE]
RETURNS[msInfo: MSInfo] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
aName: ATOM = CanonicalName[name]; -- all lower case
cName: ROPE;
found: BOOL;
val: RefTab.Val;
mse: ROPE;
IF opsH.schemaHandle.msgSetsTable =
NIL
THEN {
rVersionInfo: Relship = LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: sH.gVersionInfo].entry;
numMsgSets: INT ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rVersionInfo, sH.gMsgSetCount]];
opsH.schemaHandle.msgSetsTable ¬ RefTab.Create[MAX[numMsgSets*2+1, 16]];
};
[found, val] ¬ RefTab.Fetch[opsH.schemaHandle.msgSetsTable, aName];
IF found
THEN {
msInfo ¬ NARROW[val];
IF msInfo.versionRel # NIL THEN RETURN;
msInfo.versionRel ¬ GetMsgSetBasicInfoRel[opsH, msInfo.entity];
IF msInfo.versionRel =
NIL
THEN {
-- no longer exists
[] ¬ RefTab.Delete[opsH.schemaHandle.msgSetsTable, aName];
RETURN[NIL]
};
RETURN;
};
mse ¬ cName ¬ Atom.GetPName[aName];
msInfo ¬ NEW[MSInfoObject ¬ [canonicalName: cName, entity: mse]];
msInfo.versionRel ¬ GetMsgSetBasicInfoRel[opsH, mse];
IF msInfo.versionRel = NIL THEN RETURN[NIL]; -- return NIL IF msgSet doesn't exist
[] ¬ RefTab.Store[opsH.schemaHandle.msgSetsTable, aName, msInfo];
};
changes name to all lower case, to get canonical names
CanonicalName:
PUBLIC
PROC[name:
ROPE]
RETURNS[aName:
ATOM] = {
nameText.length ¬ 0;
FOR i:
INT
IN [0 .. name.Length[])
DO
[] ¬ RefText.InlineAppendChar[nameText, Ascii.Lower[name.Fetch[i]]];
ENDLOOP;
aName ¬ Atom.MakeAtomFromRefText[nameText];
};
CheckDomainVersion:
PROC[opsH: WalnutOpsHandle, version:
INT] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
is: INT;
rVersionInfo: Relship;
IF version = dontCareDomainVersion THEN RETURN;
rVersionInfo ¬ LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: sH.gVersionInfo].entry;
IF version # (is ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rVersionInfo, sH.gMsgSetsVersion]])
THEN {
rLogInfo: Relship ¬ LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: sH.gLogInfo].entry;
LoganBerryEntry.SetAttr[rLogInfo, sH.gOpInProgressPos, DBMinusOneInt];
LoganBerry.WriteEntry[db: opsH.db, entry: rLogInfo, replace: TRUE];
ERROR WalnutDefs.VersionMismatch[
IO.PutFR["Domain version is %g, version expected is: %g", [integer[is]], [integer[version]]] ];
};
};
CheckMsgSetVersion:
PROC[opsH: WalnutOpsHandle, msgSet: MsgSet]
RETURNS[msI: MSInfo, version: INT] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
[msI, version] ¬ GetMsgSetAndVersion[opsH, msgSet.name];
Commented out since I don't know why we need message set versions. ... DBT
BEGIN
rLogInfo: Relship;
IF msI = NIL THEN RETURN;
IF msgSet.version = dontCareMsgSetVersion THEN RETURN;
IF msgSet.version = version THEN RETURN;
rLogInfo ← LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: sH.gLogInfo].entry;
LoganBerryEntry.SetAttr[rLogInfo, sH.gOpInProgressPos, DBMinusOneInt];
LoganBerry.WriteEntry[db: opsH.db, entry: rLogInfo, replace: TRUE];
ERROR WalnutDefs.VersionMismatch[IO.PutFR[mismatchReport,
[rope[msgSet.name]], [integer[version]], [integer[msgSet.version]]] ];
END;
End of commented out code. ... DBT
};
CheckMsgSetEntity:
PROC[opsH: WalnutOpsHandle, msgSet: MsgSet]
RETURNS[msI: MSInfo] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
msI ¬ GetMsgSetEntity[opsH, msgSet.name];
Commented out since I don't know why we need message set versions. ... DBT
BEGIN
is: INT;
rLogInfo: Relship;
IF msI = NIL THEN RETURN;
IF msgSet.version = dontCareMsgSetVersion THEN RETURN;
IF msgSet.version = (is ← LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[msI.versionRel, sH.msBIVersion]]) THEN RETURN;
rLogInfo ← LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: sH.gLogInfo].entry;
LoganBerryEntry.SetAttr[rLogInfo, sH.gOpInProgressPos, DBMinusOneInt];
LoganBerry.WriteEntry[db: opsH.db, entry: rLogInfo, replace: TRUE];
ERROR WalnutDefs.VersionMismatch[IO.PutFR[mismatchReport,
[rope[msgSet.name]], [integer[is]], [integer[msgSet.version]]] ];
END;
End of commented out code. ... DBT
};
CheckForCommit:
PROC[opsH: WalnutOpsHandle, count:
INT]
RETURNS[new:
INT] = {
IF ( (new ¬ count + 1) MOD commitFreq ) = 0 THEN WalnutRoot.CommitAndContinue[opsH];
};
EmptyThisMsgSet:
PROC[opsH: WalnutOpsHandle, mse:
ROPE, name:
ROPE, report: CheckReportProc]
RETURNS[someInDeleted: BOOL] = {
sH: SchemaHandle = opsH.schemaHandle;
rs: LoganBerry.Cursor = LoganBerry.GenerateEntries[db: opsH.db, key: sH.cdMsgSet, start: mse, end: mse];
BEGIN
ENABLE
UNWIND =>
NULL;
rel: Relship;
de: ROPE = sH.deletedEntity;
commitFrequency: CARDINAL = WalnutRegistry.MsgGroupSize;
delArray: WalnutRegistry.MsgGroup;
remArray: WalnutRegistry.MsgGroup;
numDel: CARDINAL ¬ 0;
numRem: CARDINAL ¬ 0;
sinceLastCommit: INT ¬ 0;
count: INT ¬ 0;
someInDeleted ¬ FALSE;
UNTIL (rel ¬ LoganBerry.NextEntry[cursor: rs]) =
NIL
DO
me: ROPE = LoganBerryEntry.GetAttr[rel, sH.cdMsg];
deleted: BOOL;
rel ¬ LoganBerryEntry.RemoveAttr[rel, sH.cdMsgSet, mse];
deleted ¬ LoganBerryEntry.GetAttr[rel, sH.cdMsgSet] = NIL;
IF deleted
THEN {
rel ¬ LoganBerryEntry.AddAttr[rel, sH.cdMsgSet, sH.deletedEntity];
delArray[numDel] ¬ me;
numDel ¬ numDel + 1;
}
ELSE {
remArray[numRem] ¬ me;
numRem ¬ numRem + 1;
};
LoganBerry.WriteEntry[db: opsH.db, entry: rel, replace: TRUE];
someInDeleted¬ someInDeleted OR deleted;
IF (sinceLastCommit ¬ sinceLastCommit + 1) >= commitFrequency
THEN {
WalnutRoot.CommitAndContinue[opsH];
sinceLastCommit ¬ 0;
FOR i:
CARDINAL
IN [0 .. numDel)
DO
WalnutRegistryPrivate.NotifyForMsgEvent[deleted, delArray[i]]; ENDLOOP;
FOR i:
CARDINAL
IN [0 .. numRem)
DO
WalnutRegistryPrivate.NotifyForMove[msg: remArray[i], to:
NIL, from: name];
ENDLOOP;
numDel ¬ numRem ¬ 0;
};
IF report # NIL THEN count ¬ CheckCount[opsH, count, report];
ENDLOOP;
LoganBerry.EndGenerate[cursor: rs];
IF sinceLastCommit # 0
THEN {
WalnutRoot.CommitAndContinue[opsH];
FOR i:
CARDINAL
IN [0 .. numDel)
DO
WalnutRegistryPrivate.NotifyForMsgEvent[deleted, delArray[i]]; ENDLOOP;
FOR i:
CARDINAL
IN [0 .. numRem)
DO
WalnutRegistryPrivate.NotifyForMove[msg: remArray[i], to: NIL, from: name];
ENDLOOP;
};
END;
};
ChangeGlobalMsgSetInfo:
PROC[opsH: WalnutOpsHandle, delta:
INT] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
rVersionInfo: Relship = LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: sH.gVersionInfo].entry;
numNow: INT ¬ LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rVersionInfo, sH.gMsgSetCount]];
newV: INT = LoganBerryEntry.V2I[LoganBerryEntry.GetAttr[rVersionInfo, sH.gMsgSetsVersion]] + 1;
LoganBerryEntry.SetAttr[rVersionInfo, sH.gMsgSetsVersion, LoganBerryEntry.I2V[newV]];
LoganBerryEntry.SetAttr[rVersionInfo, sH.gMsgSetCount, LoganBerryEntry.I2V[numNow + delta]];
LoganBerry.WriteEntry[db: opsH.db, entry: rVersionInfo, replace: TRUE];
};
AddMsgTo:
PROC[opsH: WalnutOpsHandle, me:
ROPE, mse:
ROPE] = {
sH: WalnutSchema.SchemaHandle = opsH.schemaHandle;
rel: Relship ¬ LoganBerry.ReadEntry[db: opsH.db, key: $Key, value: Rope.Concat[sH.cdRelation, me]].entry;
rel ¬ LoganBerryEntry.AddAttr[rel, sH.cdMsgSet, mse];
LoganBerry.WriteEntry[db: opsH.db, entry: rel, replace: TRUE];
WalnutDB.ChangeCountInMsgSet[opsH, mse, 1]
};
CheckCount:
PROC[opsH: WalnutOpsHandle, count:
INT, report: CheckReportProc]
RETURNS[c: INT] = {
IF count = 0 THEN report[opsH, " "]; -- put out spaces first
IF (c ¬ count + 1) MOD 10 # 0 THEN RETURN;
IF c MOD 100 = 0 THEN report[opsH, "! "] ELSE report[opsH, "&"];
};
DestroyOrphanAddrsAndSubjs:
PROC[opsH: WalnutOpsHandle, report: CheckReportProc] = {
Now a no-op.
};