-- File: QueryProcs.mesa
-- Contents: Querying procs for messages and any kind of entity
-- Last edited by:
--  Cattell on January 17, 1983 8:43 am

DIRECTORY DB, System, WalnutDB;

QueryProcs: PROGRAM IMPORTS DB, WalnutDB =

BEGIN OPEN DB, WalnutDB;

MsgQuery: PROC[sender, to: RName, lowDate, highDate: System.GreenwichMeanTime]
  RETURNS[LIST OF Msg] = 
  BEGIN pl: LIST OF PropertyValue← NIL;
  dateLast: BOOL← highDate-lowDate>LONG[60]*60*24 -- one day --;
  IF dateLast AND highDate#lowDate THEN
    pl← CONS[[mDateCodeOf, LIST[[mDateCodeIs, T2V[lowDate], T2V[highDate]]]], pl];
  IF to#NIL THEN
    pl← CONS[[mToOf, LIST[[mToIs, to]]], pl];
  IF sender#NIL THEN
    pl← CONS[[mSenderOf, LIST[[mSenderIs, sender]]], pl];
  IF NOT dateLast AND highDate#lowDate THEN
    pl← CONS[[mDateCodeOf, LIST[[mDateCodeIs, T2V[lowDate], T2V[highDate]]]], pl];
  IF pl=NIL THEN -- Enumerate all messages!!
    BEGIN ml: LIST OF Msg; msgs: EntitySet← DomainSubset[MsgDomain];
    FOR m: Msg← NextEntity[msgs], NextEntity[msgs] UNTIL m=NIL DO
      ml← CONS[m, ml] ENDLOOP;
    RETURN[ml]
    END;
  RETURN[EntityQuery[pl]]
  END;

PropertyValueList: TYPE = LIST OF PropertyValue;
PropertyValue: TYPE = RECORD[
  from: Attribute, -- entity-valued attribute of a relation
  avl: AttributeValueList -- value contraints on other attributes of same relation
  ];

EntityQuery: PROC[pvl: PropertyValueList] RETURNS[LIST OF Entity] =
  -- A handy procedure for finding all the entities of a domain that have the given
  -- property values.  Finds all the entities which, for each PropertyValue in the
  -- PropertyValueList list, there exists at least one relationship whose
  -- "from" attribute equals the entity and whose other attributes satisfy the
  -- attribute value list.
  BEGIN
  f: Attribute← pvl.first.from;
  r: Relation← V2E[GetP[f, aRelationIs]];
  elNew: LIST OF Entity← NIL;
  nestedRS: RelshipSet← RelationSubset[r, pvl.first.avl];
  FOR t: Relship← NextRelship[nestedRS], NextRelship[nestedRS] UNTIL t=NIL DO
     elNew← CONS[V2E[GetF[t, f]], elNew] ENDLOOP;
  ReleaseRelshipSet[nestedRS];
  RETURN[NestedEntityQuery[elNew, pvl.rest]]
  END;

NestedEntityQuery: PROC[elOld: LIST OF Entity, pvl: PropertyValueList]
  RETURNS[LIST OF Entity] = {
  -- Finds the entities in elOld that satisfy pvl.  We do this recursively as follows.
  -- For each entity in elOld, we do a RelationSubset to find the tuples that satisfy pvl.first
  -- and also reference the entity.  If this list is not empty, we can keep that entity in a
  -- new list elNew; otherwise we toss it.  When all the entities in elOld that also
  -- satisfy pvl.first have been added to elNew in this way, we recursively call
  -- NestedEntityQuery, returning the entities in elNew which also satisfy pvl.rest. 
  IF pvl=NIL THEN RETURN[elOld] ELSE {
  f: Attribute← pvl.first.from;
  r: Relation← V2E[GetP[f, aRelationIs]];
  elNew: LIST OF Entity← NIL; -- will equal those in elOld that satisfy pvl.first
  FOR elOldT: LIST OF Entity← elOld, elOldT.rest UNTIL elOldT=NIL DO
    nestedRS: RelshipSet← RelationSubset[r, CONS[ [f, elOldT.first], pvl.first.avl]];
    -- The nestedRS contains tuples that reference e and also satisfy first element of pvl.
    -- We can keep elOldT.first in the elNew list iff this list is non-empty.
     IF NextRelship[nestedRS] #NIL THEN
       elNew← CONS[elOldT.first, elNew];
    ReleaseRelshipSet[nestedRS];
    ENDLOOP;
  RETURN[NestedEntityQuery[elNew, pvl.rest]];
  }};
    
   
END.