/ivy/binding/hickory/hickoryQueryImpl.mesa
Implements the querying of events from data base
Last Edited by: Binding, August 16, 1984 1:31:30 pm PDT
DIRECTORY
BasicTime USING [ GMT, nullGMT, earliestGMT, Period],
DB USING [ AttributeValue, AttributeValueList, Relship, RelshipSet, RelationSubset, NextRelship, ReleaseRelshipSet, Entity, Aborted, T2V, I2V, DeclareEntity, MarkTransaction, TransactionOf, V2B, GetF, NameOf, V2E, V2T, V2I, V2S],
Hickory USING [ EventTuple, Event, EventSet, EventList, Error, RepetitionType, All, Reminders, ExpandRepetitions, Group, ProtectionType, Options, NoOptions, MergeLists, OrderEventList],
HickoryCache USING [ FindNextOccurrence, MaintainCache, MaxCost, FindCachedEvent],
HickoryGroup USING [ GetGroupEvents],
HickorySupport USING [ StartTransaction, RestartTransaction, I2P, I2R, R2I, ReConvertRopes, GetNextOccurrence],
HickoryStorage USING [ protData],
Rope USING [ ROPE],
RopeSets USING [ IsSetEmpty, IsValueInSet]
;

HickoryQueryImpl: CEDAR MONITOR
LOCKS HickoryStorage.protData
IMPORTS DB, BasicTime, Hickory, HickorySupport, HickoryStorage, HickoryCache, HickoryGroup, RopeSets
EXPORTS Hickory
SHARES HickorySupport, HickoryCache, HickoryGroup
= BEGIN OPEN Hickory, DB, HickoryStorage.protData;
MinTime: PROCEDURE [ t1, t2: BasicTime.GMT] RETURNS [ min: BasicTime.GMT] = INLINE BEGIN
IF BasicTime.Period[ t1, t2] >= 0 THEN RETURN[ t1] ELSE RETURN[ t2];
END; -- MinTime
Querying the data base
RestoreEvents: PUBLIC ENTRY PROCEDURE [ from, to: BasicTime.GMT, options: Options ← NoOptions, group: Group ← NIL, key: Event ← NIL] RETURNS [ evList: LIST OF EventTuple] = BEGIN
returns the event tuples falling in the given time interval [ from..to].
This includes the repeated occurrences of a repeated event that fall into the interval
if 'options' include 'ExpandRepetition'. Otherwise, only the first occurrence of a
repeated event which falls into the given time interval is returned.
If 'All' is set in 'options', also return the events that have been "forgotten".
If group # NIL and key = NIL, return only events that are in the specified group.
If key # NIL, then return a list that either includes only one element
or the expanded repetions if options are set properly. The 'group' argument is
ignored if key # NIL.
If it is not the owner of the data base who is accessing it, some fields of the
returned tuples are blanked out...( Text, Message, Place)
raises Error[ NullInterval], Error[ BadInterval], Error[ NoSuchEvent]
ENABLE UNWIND => NULL;
simpleEvl: EventList ← NIL;
repeatedEvl: EventList ← NIL;
eventsOfGroup: Hickory.EventSet;
keyEnt: DB.Entity ← NIL;
success: BOOLEANFALSE;
IF BasicTime.Period[ from, to] < 0 THEN ERROR Hickory.Error[ BadInterval, NIL]
ELSE IF from = to THEN ERROR Hickory.Error[ NullInterval, NIL];
eventsOfGroup.Head ← NIL; eventsOfGroup.Tail ← NIL;
evList ← NIL;
IF key # NIL AND group = NIL THEN BEGIN
found: BOOLEANFALSE;
[ found, evList] ← LookInEnteredEventsCache[ key, options, from, to];
IF found THEN RETURN[ evList];
END;
WHILE NOT success DO
ENABLE Aborted => { HickorySupport.RestartTransaction; success ← FALSE; LOOP };
HickorySupport.StartTransaction[];
IF key # NIL THEN BEGIN
keyEnt ← DeclareEntity[ eventDomain, key, OldOnly];
IF keyEnt = NIL THEN ERROR Error[ NoSuchEvent, key];
END
ELSE IF group # NIL THEN BEGIN
eventsOfGroup ← HickoryGroup.GetGroupEvents[ group];
IF RopeSets.IsSetEmpty[ eventsOfGroup] THEN RETURN[ NIL]; -- empty group
END;
simpleEvl ← GetEvents[ from, to, options, eventsOfGroup, keyEnt, TRUE];
repeatedEvl ← GetEvents[ from, to, options, eventsOfGroup, keyEnt, FALSE];
MarkTransaction[ TransactionOf[ $Hickory]];
success ← TRUE;
ENDLOOP;
evList ← MergeLists[ simpleEvl, repeatedEvl];
RETURN[ evList];
END; --RestoreEvents
LookInEnteredEventsCache: INTERNAL PROCEDURE [ key: Event, options: Options, from, to: BasicTime.GMT] RETURNS [ found: BOOLEAN, evl: EventList] = BEGIN
l: EventList ← HickoryCache.FindCachedEvent[ key];
IF l = NIL THEN RETURN[ FALSE, NIL];
IF options[ Reminders] AND NOT l.first.Remind THEN RETURN[ TRUE, NIL];
IF NOT options[ All] AND l.first.Forgotten THEN RETURN[ TRUE, NIL];
IF NOT( BasicTime.Period[ from, l.first.EventTime] >= 0 AND BasicTime.Period[ l.first.EventTime, to] >= 0) THEN RETURN[ TRUE, NIL];
IF l.first.RepeatType = None OR NOT options[ ExpandRepetitions] THEN RETURN[ TRUE, l]
ELSE BEGIN
until: BasicTime.GMT ← MinTime[ to, l.first.RepeatUntil];
evl ← Expand[ l.first, until];
evl ← OrderEventList[ evl];
RETURN[ TRUE, evl];
END;
END; -- LookInEnteredEventsCache
BuildQueryConstraints: INTERNAL PROCEDURE [ simple: BOOLEAN, options: Options, from, to: BasicTime.GMT, evSet: Hickory.EventSet, keyEnt: Entity ← NIL] RETURNS [ avl: AttributeValueList ← NIL] = BEGIN
to figure out the constraints on a query
OPEN eventRel;
av: AttributeValue;
forget: BOOLEANNOT options[ All];
reminders: BOOLEAN ← options[ Reminders];
IF forget THEN BEGIN -- don't include forgotten events
av ← [ attribute: Forgotten, hi: B2V[ FALSE], lo: B2V[ FALSE]];
avl ← CONS[ av, avl]; *****DB. DBModelPrivateImpl.NCode throws up...!
NULL;
END;
IF reminders THEN BEGIN -- include only events to be reminded
av ← [ attribute: Reminder, hi: B2V[ TRUE], lo: B2V[ TRUE]];
avl ← CONS[ av, avl]; *****DB. DBModelPrivateImpl.NCode throws up...!
NULL;
END;
IF simple THEN av ← [ Time, T2V[ LOOPHOLE[ from]], T2V[ LOOPHOLE[ to]]]
ELSE av ← [ Time, T2V[ LOOPHOLE[ BasicTime.earliestGMT]], T2V[ LOOPHOLE[ to]]];
avl ← CONS[ av, avl];
IF simple THEN av ← [ RepeatType, I2V[ HickorySupport.R2I[ None]], I2V[ HickorySupport.R2I[ None]]]
ELSE av ← [ attribute: RepeatType, lo: I2V[ HickorySupport.R2I[ Hourly]], hi: I2V[ HickorySupport.R2I[ Complicated]]];
avl ← CONS[ av, avl];
IF keyEnt # NIL THEN BEGIN
av ← [ attribute: Key, lo: keyEnt, hi: keyEnt];
avl ← CONS[ av, avl];
END
ELSE IF NOT RopeSets.IsSetEmpty[ evSet] THEN BEGIN -- it's a group query...
we can restrict query to largest and smallest event entity in that group!
headEnt, tailEnt: Entity;
headEnt ← DeclareEntity[ eventDomain, evSet.Head.Value, OldOnly];
tailEnt ← DeclareEntity[ eventDomain, evSet.Tail.Value, OldOnly];
IF headEnt = NIL THEN ERROR Hickory.Error[ NoSuchEvent, evSet.Head.Value];
IF tailEnt = NIL THEN ERROR Hickory.Error[ NoSuchEvent, evSet.Tail.Value ];
av ← [ attribute: Key, lo: headEnt, hi: tailEnt];
avl ← CONS[ av, avl];
END;
RETURN[ avl];
END; -- BuildQueryConstraints
GetEvents: INTERNAL PROCEDURE [ from, to: BasicTime.GMT, options: Options, es: Hickory.EventSet, keyEnt: Entity, simple: BOOLEAN] RETURNS [ evl: EventList] = BEGIN
returns all the events in time interval. 'to' inclusive..!
OPEN eventRel;
avl: AttributeValueList;
relSet: RelshipSet;
expand: BOOLEAN ← options[ ExpandRepetitions];
avl ← BuildQueryConstraints[ simple, options, from, to, es, keyEnt];
relSet ← RelationSubset[ Rel, avl];
IF simple THEN evl ← BuildSimpleEventList[ relSet, options, es]
ELSE evl ← BuildRepeatedEventList[ relSet, from, to, options, es];
ReleaseRelshipSet[ relSet];
IF NOT simple THEN BEGIN -- for repeated events
evl ← OrderEventList[ evl]; -- must reorder on EventTime
IF expand THEN evl ← ExpandRepeatedEventList[ evl, to];
END;
END; -- GetEvents
BuildSimpleEventList: INTERNAL PROCEDURE [ relSet: RelshipSet, options: Options, es: Hickory.EventSet] RETURNS [ evList: LIST OF EventTuple] = BEGIN
to convert from a RelshipSet into a list of EventTuples Assume all events have RepetionType
= None...
OPEN eventRel;
key: Event;
msg, txt, pl, iconFlav, iconLab: Rope.ROPE;
prot: Hickory.ProtectionType;
tuple: Relship;
ev: EventTuple;
head, tail, new: EventList;
reminders: BOOLEAN ← options[ Reminders];
forget: BOOLEANNOT options[ All];
head ← NIL; tail ← NIL; new ← NIL;
WHILE ( tuple ← NextRelship[ relSet]) # NIL DO
forgotten: BOOLEAN ← V2B[ GetF[ tuple, Forgotten]];
reminder: BOOLEAN ← V2B[ GetF[ tuple, Reminder]];
IF forget AND forgotten THEN LOOP;
IF NOT reminder AND reminders THEN LOOP;
key ← NameOf[ V2E[ GetF[ tuple, Key]]];
IF NOT RopeSets.IsSetEmpty[ es] AND NOT RopeSets.IsValueInSet[ key, es] THEN LOOP;
[ iconFlav, iconLab] ← QueryIcons[ V2E[ GetF[ tuple, Icon]]];
prot ← HickorySupport.I2P[ V2I[ GetF[ tuple, Protection]]];
IF prot = Private AND NOT owner THEN BEGIN
msg ← ""; txt ← ""; pl ← "";
END
ELSE BEGIN
msg ← NameOf[ V2E[ GetF[ tuple, Message]]];
txt ← NameOf[ V2E[ GetF[ tuple, Text]]];
pl ← NameOf[ V2E[ GetF[ tuple, Place]]];
END;
ev ← [ Key: key,
EventTime: LOOPHOLE[ V2T[ GetF[ tuple, Time]]],
Duration: V2I[ GetF[ tuple, Duration]],
RepeatType: None,
RepeatTime: NIL,
RepeatUntil: BasicTime.nullGMT,
KeepUntil: LOOPHOLE[ V2T[ GetF[ tuple, KeepTime]]],
LeadTime: V2I[ GetF[ tuple, LeadTime]],
NagTime: V2I[ GetF[ tuple, NagTime]],
IconFlavor: iconFlav,
IconLabel: iconLab,
Message: msg,
Text: txt,
Place: pl,
Protection: prot,
Forgotten: forgotten,
Remind: reminder];
ev ← HickorySupport.ReConvertRopes[ ev]; -- "" -> NIL!
new ← CONS[ ev, NIL];
IF head = NIL THEN BEGIN -- pretty painful to keep things ordered
head ← new; tail ← head;
END
ELSE BEGIN
tail.rest ← new;
tail ← new;
END;
ENDLOOP;
I do not know why things are not orderd on EventTime: DB must be the culprit?
head ← OrderEventList[ head]; -- this should not be needed if DB returned things ordered!
RETURN[ head];
END; -- BuildSimpleEventList
BuildRepeatedEventList: INTERNAL PROCEDURE [ relSet: RelshipSet, from, to: BasicTime.GMT, options: Options, es: Hickory.EventSet] RETURNS [ evList: EventList] = BEGIN
to convert from a RelshipSet into a list of EventTuples Assume all events have
RepetionType # None... The returned list is NOT ORDERED.
OPEN eventRel;
key: Event;
msg, txt, pl, iconFlav, iconLab, repTime: Rope.ROPE;
prot: Hickory.ProtectionType;
tuple: Relship;
ev: EventTuple;
evEnt: Entity;
lastTime, firstTime, next, repeatUntil: BasicTime.GMT;
repType: RepetitionType;
forget: BOOLEAN ← NOT options[ All];
reminders: BOOLEAN ← options[ Reminders];
evList ← NIL;
-- relSet contains ALL repeated events, even the forgotten ones for now!
WHILE ( tuple ← NextRelship[ relSet]) # NIL DO
forgotten: BOOLEAN ← V2B[ GetF[ tuple, Forgotten]];
reminder: BOOLEAN ← V2B[ GetF[ tuple, Reminder]];
IF forget AND forgotten THEN LOOP;
IF NOT reminder AND reminders THEN LOOP;
evEnt ← V2E[ GetF[ tuple, Key]];
[ firstTime, lastTime, repType, repTime, repeatUntil] ← GetRepeatPars[ evEnt];
IF BasicTime.Period[ from, repeatUntil] >= 0 THEN BEGIN
must take a closer look
key ← NameOf[ evEnt];
IF NOT RopeSets.IsSetEmpty[ es] AND NOT RopeSets.IsValueInSet[ key, es] THEN LOOP;
assumption: lastTime is the closest repeated occurrence before the present...
firstTime was the very first time it ever happened.
next ← HickoryCache.FindNextOccurrence[ key, firstTime, lastTime, from, repType, repTime];
next >= from...
IF BasicTime.Period[ to, next] <= 0 THEN BEGIN -- there is occurrence in interval...
[ iconFlav, iconLab] ← QueryIcons[ V2E[ GetF[ tuple, Icon]]];
prot ← HickorySupport.I2P[ V2I[ GetF[ tuple, Protection]]];
IF prot = Private AND NOT owner THEN BEGIN
msg ← ""; txt ← ""; pl ← "";
END
ELSE BEGIN
msg ← NameOf[ V2E[ GetF[ tuple, Message]]];
txt ← NameOf[ V2E[ GetF[ tuple, Text]]];
pl ← NameOf[ V2E[ GetF[ tuple, Place]]];
END;
ev ← [ Key: key,
EventTime: next,
Duration: V2I[ GetF[ tuple, Duration]],
RepeatType: repType,
RepeatTime: repTime,
RepeatUntil: repeatUntil,
KeepUntil: LOOPHOLE[ V2T[ GetF[ tuple, KeepTime]]],
LeadTime: V2I[ GetF[ tuple, LeadTime]],
NagTime: V2I[ GetF[ tuple, NagTime]],
IconFlavor: iconFlav,
IconLabel: iconLab,
Message: msg,
Text: txt,
Place: pl,
Protection: prot,
Remind: reminder,
Forgotten: forgotten];
ev ← HickorySupport.ReConvertRopes[ ev];
evList ← CONS[ ev, evList]; -- order within list not relevant here
END; -- if next > to...
END; -- if repeatUntil < from...
ENDLOOP;
RETURN[ evList];
END; -- BuildRepeatedEventList
GetRepeatPars: INTERNAL PROCEDURE [ evEnt: Entity] RETURNS [ firstTime, lastTime: BasicTime.GMT, repType: RepetitionType, repTime: Rope.ROPE, repUntil: BasicTime.GMT] = BEGIN
to get hold of the repetiton parameters
OPEN repeatRel;
relSet: RelshipSet;
tuple: Relship;
av: AttributeValue;
av ← [ Event, evEnt, evEnt];
relSet ← RelationSubset[ Rel, LIST[ av]];
tuple ← NextRelship[ relSet];
IF tuple = NIL THEN ERROR;
firstTime ← LOOPHOLE[ V2T[ GetF[ tuple, FirstTime]]];
lastTime ← LOOPHOLE[ V2T[ GetF[ tuple, LastOccurred]]];
repTime ← NameOf[ V2E[ GetF[ tuple, RepeatTime]]];
repType ← HickorySupport.I2R[ V2I[ GetF[ tuple, RepeatType]]];
repUntil ← LOOPHOLE[ V2T[ GetF[ tuple, RepeatUntil]]];
IF NextRelship[ relSet] # NIL THEN ERROR;
ReleaseRelshipSet[ relSet];
RETURN[ firstTime, lastTime, repType, repTime, repUntil];
END; -- GetRepeatPar
ExpandRepeatedEventList: INTERNAL PROCEDURE [ evl: EventList, to: BasicTime.GMT] RETURNS [ newList: EventList] = BEGIN
insert repeated occurrences for the repeated events that happen before 'to'
tempList: EventList;
newList ← NIL;
FOR l: EventList ← evl, l.rest UNTIL l = NIL DO
tempList ← Expand[ l.first, to];
newList ← MergeLists[ newList, tempList];
ENDLOOP;
RETURN[ newList];
END; -- ExpandRepeatedEventList
Expand: INTERNAL PROCEDURE[ ev: EventTuple, to: BasicTime.GMT] RETURNS [ evl: EventList] = BEGIN
make a list for all repetitions that happen until 'to'. Don't use the cache since
generation of times is purely sequential..., so it is not worth the bother. Do however
insert generated occurrences into cache
tail, new: EventList;
nextTime: BasicTime.GMT;
cost: CARDINAL ← 0;
evl ← CONS[ ev, NIL];
tail ← evl;
DO -- ..EventTime is for sure in time interval of query
nextTime ← HickorySupport.GetNextOccurrence[ LOOPHOLE[ tail.first.EventTime], tail.first.RepeatType, tail.first.RepeatTime];
IF cost >= HickoryCache.MaxCost THEN BEGIN
HickoryCache.MaintainCache[ ev.Key, nextTime];
cost ← 0;
END
ELSE cost ← cost + 1;
IF BasicTime.Period[ to, nextTime] > 0 THEN EXIT;
IF BasicTime.Period[ ev.RepeatUntil, nextTime] > 0 THEN EXIT;
new ← CONS[ tail.first, NIL];
new.first.EventTime ← nextTime;
tail.rest ← new;
tail ← new;
ENDLOOP;
RETURN[ evl];
END; -- Expand
QueryIcons: INTERNAL PROCEDURE [ key: Entity] RETURNS [ iconFlav, iconLab: Rope.ROPE] = BEGIN
given the key we retrieve the associated label and flavor attribute in icon relation
OPEN iconRel;
av: AttributeValue;
relSet: RelshipSet;
tuple: Relship;
av ← [ Key, key, key];
relSet ← RelationSubset[ Rel, LIST[ av]];
tuple ← NextRelship[ relSet];
IF tuple = NIL THEN RETURN[ "", ""];
iconFlav ← V2S[ GetF[ tuple, IconFlavor]];
iconLab ← V2S[ GetF[ tuple, IconLabel]];
ReleaseRelshipSet[ relSet];
RETURN[ iconFlav, iconLab];
END; -- QueryIcons
END.