<> <> <> <> <> <> <> DIRECTORY DBEnvironment USING [TupleObject], DBStorage, DBTuplesConcrete, DB, DBModel, DBModelPrivate, Rope; DBModelSetImpl: CEDAR PROGRAM IMPORTS DBStorage, DB, DBModel, DBModelPrivate, Rope EXPORTS DB, DBEnvironment, DBModel = BEGIN OPEN DB, DBModelPrivate, DBModel; ROPE: TYPE = Rope.ROPE; <> entitySetCount: PUBLIC INT_ 0; relshipSetCount: PUBLIC INT_ 0; < DBModel>> TupleObject: PUBLIC TYPE = DBTuplesConcrete.TupleObject; EntityObject: PUBLIC TYPE = TupleObject; RelshipObject: PUBLIC TYPE = TupleObject; EntitySetObject: PUBLIC TYPE = DBTuplesConcrete.EntitySetObject; RelshipSetObject: PUBLIC TYPE = DBTuplesConcrete.RelshipSetObject; <> Tuple, TupleSet, Index, IndexFactor: TYPE = REF TupleObject; Domain, Relation, Entity, Attribute, DataType: PUBLIC TYPE = REF EntityObject; Relship: PUBLIC TYPE = REF RelshipObject; SystemATuple: TYPE = REF attribute TupleObject; SystemSTuple: TYPE = REF surrogate TupleObject; SystemTSTuple: TYPE = REF tupleSet TupleObject; EntitySet: PUBLIC TYPE = REF EntitySetObject; RelshipSet: PUBLIC TYPE = REF RelshipSetObject; <> T2SAT: PROCEDURE[t: Tuple] RETURNS [SystemATuple] = INLINE BEGIN RETURN [NARROW[t]] END; T2ST: PROCEDURE[t: Tuple] RETURNS [SystemTSTuple] = INLINE BEGIN RETURN [NARROW[t]] END; T2SST: PROC[t: Tuple] RETURNS [SystemSTuple] = INLINE BEGIN RETURN [NARROW[t]] END; <> QDomainSubset: PUBLIC PROCEDURE[ d: Domain, lowName, highName: ROPE_ NIL, start: FirstLast_ First, searchSubDomains: BOOL_ TRUE, segment: Segment_ NIL] RETURNS[es: EntitySet] = <> <> <> <> <> <= lowName and <=highName, and the enumeration is sorted.>> <> <> <> <> BEGIN dl: LIST OF Domain; CheckDomain[d]; entitySetCount_ entitySetCount+1; IF IsSystem[d] THEN IF start=First THEN RETURN[SystemDomainSubset[d, lowName, highName, segment]] ELSE ERROR Error[NotImplemented]; IF searchSubDomains AND (dl_ GetCachedDomainInfo[d].subDomains)#NIL THEN <> BEGIN -- Nested search of d and its subdomains es_ NEW[EntitySetObject[nested]_ [nested[ previousDomains: IF start=Last THEN CONS[d, dl] ELSE NIL, remainingDomains: IF start=First THEN Reverse[CONS[d, dl]] ELSE NIL, lowName: lowName, highName: highName, start: start, currentEntitySet: NIL ]]]; RETURN[es] END; IF lowName#NIL THEN BEGIN -- Search using index i: Index_ GetCachedDomainInfo[d].nameIndex; lowName_ NCode[lowName]; highName_ IF highName=NIL THEN lowName ELSE NCode[highName]; es_ NEW[EntitySetObject[index] _ [ index [ DBStorage.OpenScanIndex[i, [lowName, highName, TRUE, TRUE], start ]]] ]; END ELSE <> es_ NEW[EntitySetObject[tupleSet] _ [tupleSet [DBStorage.OpenScanTupleset[d, start] ]] ]; RETURN[es]; END; Reverse: PUBLIC PROC [list: LIST OF Entity] RETURNS [val: LIST OF Entity] = { val _ NIL; UNTIL list = NIL DO val _ CONS[list.first, val]; list _ list.rest; ENDLOOP; RETURN[val]; }; QRelationSubset: PUBLIC PROCEDURE[ r: Relation, constraint: AttributeValueList, start: FirstLast_ First] RETURNS [rs: RelshipSet] = <> <> <> <<(1) Is r a surrogate relation? If so, will be doing index, group, or tupleset scan on domain.>> <<(2) Special-case r surrogate with first attribute constraint on entity in r's target domain.>> <<(3) Is there a useful index on r? This can be true whether r is surrogate or not.>> <<(4) Is one of the constraints entity-valued (group-scannable)? r can be surrogate or not.>> <<(5) If all else fails, use a tupleset scan (even if r is surrogage), NextRelship will filter.>> <> <> <> <> <> <> <> <> <> <> BEGIN surrogateR: Relation; -- NIL if not surrogate relation targetAttr: Attribute; -- target attribute if surrogate relation targetTupleset: DBStorage.TuplesetHandle; -- target domain if r is surrogate relation, else r entityConstraint: AttributeValue; -- entity-valued constraint if group scannable reducedList: AttributeValueList; -- remainder of constraint if group or index scannable recVal: DBStorage.FieldHandle; -- handle to pass to storage level for group scans groupScannable: BOOL; indexScan: DBStorage.IndexScanHandle; <<(0) Check that valid parameters>> CheckRelation[r]; IF CheckAVList[constraint] THEN RETURN; <> IF r=dSubType AND constraint=NIL THEN ERROR Error[NotImplemented]; relshipSetCount_ relshipSetCount+1; <<(1) Determine whether surrogate or normal relation>> IF V2B[SafeGetP[r, r1to1Prop]] THEN <> BEGIN surrogateR_ r; targetAttr_ GetFirstAttribute[r]; targetTupleset_ V2E[SafeGetP[targetAttr, aTypeIs]]; END ELSE -- normal relation BEGIN surrogateR_ NIL; targetTupleset_ r END; <<(2) Try for one-element scan on the target attribute if a surrogate relation>> IF surrogateR#NIL AND constraint#NIL AND Eq[constraint.first.attribute, targetAttr] THEN BEGIN -- no search necessary, want tuple that corresponds to one entity in targetDomain t: Relship_ SurrogateCreateRelship[r]; T2SST[t].vEntity_ V2E[constraint.first.lo]; IF constraint.rest=NIL OR MatchingRelship[t, constraint.rest] THEN rs_ NEW[RelshipSetObject[justOne]_ [justOne [hereItIs: t]]] ELSE rs_ NIL; RETURN END; <<(3) Try for index scan>> [indexScan, reducedList]_ FindIndexedConstraint[r, constraint, start]; IF indexScan#NIL THEN BEGIN rs_ NEW[RelshipSetObject[index] _ [index [ serialCheck: reducedList, scanHandle: indexScan, surrogate: surrogateR ] ]]; RETURN; END; <<(4) Try for group scan>> [entityConstraint, reducedList, groupScannable]_ FindEntityConstraint[constraint]; IF groupScannable THEN BEGIN recVal_ V2Rec[SafeGetP[entityConstraint.attribute, aHandleProp]]; rs_ NEW[RelshipSetObject[group] _ [group [ serialCheck: reducedList, scanHandle: DBStorage.OpenScanGroup [V2E[entityConstraint.lo], recVal, start], surrogate: surrogateR ] ]]; END <<(5) Resort to scanning the whole tupleset, either the relation r or if r is surrogate then the>> <> ELSE BEGIN rs_ NEW[RelshipSetObject[tupleSet]_ [tupleSet [ serialCheck: constraint, scanHandle: DBStorage.OpenScanTupleset[targetTupleset, start], surrogate: surrogateR ]]]; END; END; FindEntityConstraint: PROC [constraint: AttributeValueList] RETURNS [con: AttributeValue, reducedList: AttributeValueList, found: BOOLEAN] = <> <> <> BEGIN reducedList_ NIL; found_ FALSE; FOR conL: AttributeValueList_ constraint, conL.rest UNTIL conL = NIL DO a: Attribute = conL.first.attribute; type: ValueType; -- type of this attribute link: LinkType; -- whether it is linked IF IsSystem[a] THEN {type_ T2SAT[a].vType; link_ Linked} ELSE [type: type, link: link]_ GetCachedAttributeInfo[a]; IF IsDomainType[type] AND (link=Linked OR link=Colocated) THEN {con_ conL.first; GOTO Found} ELSE reducedList_ CONS[conL.first, reducedList]; REPEAT Found => BEGIN found_ TRUE; FOR conT: AttributeValueList_ conL.rest, conT.rest UNTIL conT = NIL DO reducedList_ CONS[conT.first, reducedList]; ENDLOOP END; FINISHED => con_ [NIL, NIL, NIL]; -- return found=FALSE and dummy values. ENDLOOP; RETURN END; FindIndexedConstraint: PROC[r: Relation, constraint: AttributeValueList, start: FirstLast] RETURNS [is: DBStorage.IndexScanHandle, reducedList: AttributeValueList] = <> <> <> <> <> BEGIN i: Index; ifs: LIST OF IndexFactor; lowKey, highKey: ROPE; IF constraint=NIL OR IsSystem[r] THEN RETURN[NIL, constraint]; ifs_ GetCachedAttributeInfo[constraint.first.attribute].indexFactors; FOR ifs_ ifs, ifs.rest UNTIL ifs=NIL DO i_ V2E[SafeGetP[ifs.first, ifIndexIs]]; SELECT CanUseIndex[i, constraint] FROM different => NULL; identical => { lowKey_ NCodeForTupleMatch[constraint, i, low]; highKey_ NCodeForTupleMatch[constraint, i, high]; reducedList_ NIL; GO TO FoundAnIndex; }; indexSuperset => { lowKey_ NCodeForTupleMatch[constraint, i, low]; highKey_ NCodeForTupleMatch[constraint, i, high]; reducedList_ NIL; GO TO FoundAnIndex; }; matchSuperset => { lowKey_ NCodeForTupleMatch[constraint, i, low]; highKey_ NCodeForTupleMatch[constraint, i, high]; reducedList_ constraint; GO TO FoundAnIndex; }; ENDCASE => ERROR; REPEAT FoundAnIndex => RETURN[DBStorage.OpenScanIndex[ i, [lowKey, highKey, TRUE, TRUE], start], reducedList]; FINISHED => RETURN[NIL, constraint]; ENDLOOP; END; CanUseIndex: PROCEDURE[i: Index, tm: AttributeValueList] RETURNS[{different, identical, indexSuperset, matchSuperset}] = <> <> <> <> <> BEGIN if: IndexFactor; count: INT_ 0; ifs: LIST OF IndexFactor_ VL2EL[QGetPList[i, ifIndexOf]]; FOR tmL: AttributeValueList_ tm, tmL.rest UNTIL tmL=NIL DO IF ifs=NIL AND count>0 THEN RETURN[matchSuperset]; if_ ifs.first; ifs_ ifs.rest; IF count#V2I[SafeGetP[if, ifOrdinalPositionIs]] THEN ERROR InternalError; IF NOT Eq[V2E[SafeGetP[if, ifAttributeIs]], tmL.first.attribute] THEN RETURN[different]; count_ count+1; ENDLOOP; IF ifs=NIL THEN RETURN[identical] ELSE RETURN[indexSuperset]; END; QGetAllRefAttributes: PUBLIC PROCEDURE[e: Entity] RETURNS[al: AttributeList] = <> <> <> <> <> <> <> <> <> BEGIN al1, al2, al3: LIST OF Attribute_ NIL; CheckEntity[e]; IF NOT IsSystem[e] THEN { al1_ GetDomainUnlinkedRefAttributes[QDomainOf[e]]; al2_ GetDomainSurrogateRefAttributes[QDomainOf[e]] }; al3_ DBStorage.GetGroupIDs[e]; RETURN[Nconc[Nconc[al1, al2], al3]]; END; QGetDomainRefAttributes: PUBLIC PROC[d: Domain] RETURNS[al: AttributeList] = <> <> BEGIN CheckDomain[d]; IF IsSystem[d] THEN ERROR Error[NotImplemented]; al_ GetDomainDirectRefAttributes[d]; FOR dl: LIST OF Domain_ FindSuperDomains[d], dl.rest UNTIL dl=NIL DO al_ Nconc[al, GetDomainDirectRefAttributes[dl.first]] ENDLOOP; RETURN[al] END; GetDomainDirectRefAttributes: PROC[d: Domain] RETURNS[al: AttributeList] = <> <> {RETURN[VL2EL[QGetPList[d, aTypeOf]]]}; GetDomainSurrogateRefAttributes: PROC[d: Domain] RETURNS[al: AttributeList] = <> {RETURN[VL2EL[QGetPList[d, aDomainOf]]]}; GetDomainUnlinkedRefAttributes: PROC[d: Domain] RETURNS[al: AttributeList] = <> {RETURN[VL2EL[QGetPList[d, aUnlinkedOf]]]}; <> QNextEntity: PUBLIC PROCEDURE[es: EntitySet] RETURNS[e: Entity] = <> <> <> <<(1) tupleSet scan simply chaining through ALL entities in a domain,>> <<(2) nested scan through currentEntitySet till exhausted, then next in remainingDomains>> <<(3) segment scan through currentEntitySet till exhausted, then next in remainingSegments >> <<(4) group scan finds all entities reffing some entity (used on surrogate fields)>> <<(5) attributeSet goes through all relations, and all attributes for each>> <<(6) system scan follows linked list through system entities>> <<(7) empty scan always returns NIL (used for some special cases)>> <> <> BEGIN IF es=NIL THEN RETURN[NIL]; TRUSTED { WITH es1: es SELECT FROM tupleSet => e_ DBStorage.NextScanTupleset[es1.scanHandle]; nested => { DO IF (e_ QNextEntity[es1.currentEntitySet])#NIL THEN RETURN[e]; IF es1.remainingDomains=NIL THEN RETURN[NIL]; QReleaseEntitySet[es1.currentEntitySet]; -- all done with this, get next es1.currentEntitySet_ QDomainSubset[ es1.remainingDomains.first, es1.lowName, es1.highName, es1.start, FALSE]; es1.previousDomains_ CONS[es1.remainingDomains.first, es1.previousDomains]; es1.remainingDomains_ es1.remainingDomains.rest; ENDLOOP }; segment => { DO oldOne: LIST OF Segment; IF (e_ QNextEntity[es1.currentEntitySet])#NIL THEN RETURN[e]; IF es1.remainingSegments=NIL THEN RETURN[NIL]; QReleaseEntitySet[es1.currentEntitySet]; -- all done with this, get next es1.currentEntitySet_ QDomainSubset[ es1.domain, es1.lowName, es1.highName, es1.start, es1.searchSubDomains, es1.remainingSegments.first ]; oldOne_ es1.remainingSegments; es1.remainingSegments_ es1.remainingSegments.rest; oldOne.rest_ es1.previousSegments; es1.previousSegments_ oldOne; ENDLOOP }; index => e_ DBStorage.NextScanIndex[es1.scanHandle]; empty => e_ NIL; ENDCASE => ERROR InternalError; }; END; QPrevEntity: PUBLIC PROCEDURE[es: EntitySet] RETURNS[e: Entity] = <> BEGIN IF es=NIL THEN RETURN[NIL]; TRUSTED { WITH es1: es SELECT FROM tupleSet => e_ DBStorage.PrevScanTupleset[es1.scanHandle]; nested => { <> <> <> <> <> DO IF (e_ QPrevEntity[es1.currentEntitySet])#NIL THEN RETURN[e]; <> <> IF es1.previousDomains=NIL THEN RETURN[NIL]; QReleaseEntitySet[es1.currentEntitySet]; <> es1.remainingDomains_ CONS[es1.previousDomains.first, es1.remainingDomains]; es1.previousDomains_ es1.previousDomains.rest; <> IF es1.previousDomains=NIL THEN {es1.currentEntitySet_ NIL; RETURN[NIL]}; es1.currentEntitySet_ QDomainSubset[ es1.previousDomains.first, es1.lowName, es1.highName, Last, FALSE]; ENDLOOP }; segment => { DO oldOne: LIST OF Segment; IF (e_ QPrevEntity[es1.currentEntitySet])#NIL THEN RETURN[e]; IF es1.previousSegments=NIL THEN RETURN[NIL]; QReleaseEntitySet[es1.currentEntitySet]; -- all done with this, get next es1.currentEntitySet_ QDomainSubset[ es1.domain, es1.lowName, es1.highName, es1.start, es1.searchSubDomains, es1.previousSegments.first ]; oldOne_ es1.previousSegments; es1.previousSegments_ es1.previousSegments.rest; oldOne.rest_ es1.remainingSegments; es1.remainingSegments_ oldOne; ENDLOOP }; index => e_ DBStorage.PrevScanIndex[es1.scanHandle]; empty => e_ NIL; ENDCASE => ERROR InternalError; }; END; QNextRelship: PUBLIC PROCEDURE[rs: RelshipSet] RETURNS[t: Relship] = <> <> <> <> <> <> <> BEGIN ConsSurrogate: PROC[r: Relation] RETURNS [newT: Relship] = {newT_ SurrogateCreateRelship[r]; T2SST[newT].vEntity_ t; RETURN[newT]}; IF rs=NIL THEN RETURN[NIL]; TRUSTED { WITH rs1: rs SELECT FROM tupleSet => WHILE (t_ DBStorage.NextScanTupleset[rs1.scanHandle])#NIL DO IF rs1.surrogate#NIL THEN t_ ConsSurrogate[rs1.surrogate]; IF MatchingRelship[t, rs1.serialCheck] THEN RETURN ENDLOOP; group => WHILE (t_ DBStorage.NextInGroup[rs1.scanHandle])#NIL DO IF rs1.surrogate#NIL THEN t_ ConsSurrogate[rs1.surrogate]; IF MatchingRelship[t, rs1.serialCheck] THEN RETURN ENDLOOP; index => WHILE (t_ DBStorage.NextScanIndex[rs1.scanHandle])#NIL DO IF rs1.surrogate#NIL THEN t_ ConsSurrogate[rs1.surrogate]; IF MatchingRelship[t, rs1.serialCheck] THEN RETURN ENDLOOP; justOne => IF rs1.hereItIs#NIL THEN {t_ rs1.hereItIs; rs1.hereItIs_ NIL; RETURN[t]}; ENDCASE => ERROR InternalError; }; <> <> RETURN[NIL]; END; QPrevRelship: PUBLIC PROCEDURE[rs: RelshipSet] RETURNS[t: Relship] = <> BEGIN ConsSurrogate: PROC[r: Relation] RETURNS [newT: Relship] = {newT_ SurrogateCreateRelship[r]; T2SST[newT].vEntity_ t; RETURN[newT]}; IF rs=NIL THEN RETURN[NIL]; TRUSTED { WITH rs1: rs SELECT FROM tupleSet => WHILE (t_ DBStorage.PrevScanTupleset[rs1.scanHandle])#NIL DO IF rs1.surrogate#NIL THEN t_ ConsSurrogate[rs1.surrogate]; IF MatchingRelship[t, rs1.serialCheck] THEN RETURN ENDLOOP; group => WHILE (t_ DBStorage.PrevInGroup[rs1.scanHandle])#NIL DO IF rs1.surrogate#NIL THEN t_ ConsSurrogate[rs1.surrogate]; IF MatchingRelship[t, rs1.serialCheck] THEN RETURN ENDLOOP; index => WHILE (t_ DBStorage.PrevScanIndex[rs1.scanHandle])#NIL DO IF rs1.surrogate#NIL THEN t_ ConsSurrogate[rs1.surrogate]; IF MatchingRelship[t, rs1.serialCheck] THEN RETURN ENDLOOP; justOne => ERROR Error[NotImplemented]; ENDCASE => ERROR InternalError; }; <> <> RETURN[NIL]; END; <> MatchingRelship: PROCEDURE[rel: Relship, alh: AttributeValueList] RETURNS[BOOLEAN] = <> --INLINE-- BEGIN FOR alhL: AttributeValueList_ alh, alhL.rest UNTIL alhL=NIL DO IF NOT MatchingAttribute[rel, alhL.first] THEN RETURN[FALSE] ENDLOOP; RETURN[TRUE] END; MatchingAttribute: PROCEDURE[rel: Relship, alh: AttributeValue] RETURNS[ans: BOOLEAN] = <> --INLINE-- BEGIN s: ROPE_ NCode[QGetF[rel, alh.attribute]]; low: ROPE_ NCode[alh.lo]; high: ROPE_ IF alh.hi=NIL THEN low ELSE NCode[alh.hi]; vt: DataType_ V2E[SafeGetP[alh.attribute, aTypeIs]]; <> <> ans_ GreaterEqString[s, low] AND GreaterEqString[high, s]; END; QReleaseEntitySet: PUBLIC PROCEDURE[es: EntitySet] = BEGIN entitySetCount_ entitySetCount - 1; IF es=NIL THEN RETURN; TRUSTED { WITH es1: es SELECT FROM nested => {QReleaseEntitySet[es1.currentEntitySet]; es1.currentEntitySet_ NIL}; index => {DBStorage.CloseScanIndex[es1.scanHandle]; es1.scanHandle_ NIL}; tupleSet => {DBStorage.CloseScanTupleset[es1.scanHandle]; es1.scanHandle_ NIL}; segment => {QReleaseEntitySet[es1.currentEntitySet]; es1.currentEntitySet_ NIL}; empty => NULL; ENDCASE => ERROR; }; END; QReleaseRelshipSet: PUBLIC PROCEDURE[rs: RelshipSet] = BEGIN relshipSetCount_ relshipSetCount - 1; IF rs=NIL THEN RETURN; TRUSTED { WITH rs1: rs SELECT FROM group => {DBStorage.CloseScanGroup[rs1.scanHandle]; rs1.scanHandle_ NIL}; tupleSet => {DBStorage.CloseScanTupleset[rs1.scanHandle]; rs1.scanHandle_ NIL}; index => {DBStorage.CloseScanIndex[rs1.scanHandle]; rs1.scanHandle_ NIL}; segment => {QReleaseRelshipSet[rs1.currentRelshipSet]; rs1.currentRelshipSet_ NIL}; justOne => NULL; ENDCASE => ERROR; }; END; <> QEntitySetToList: PUBLIC PROC[es: EntitySet] RETURNS [el: LIST OF Entity] = BEGIN e: Entity_ QNextEntity[es]; IF e=NIL THEN {QReleaseEntitySet[es]; RETURN[NIL]} ELSE RETURN[CONS[e, QEntitySetToList[es]]]; END; QRelshipSetToList: PUBLIC PROC[rs: RelshipSet] RETURNS [el: LIST OF Relship] = BEGIN r: Relship_ QNextRelship[rs]; IF r=NIL THEN {QReleaseRelshipSet[rs]; RETURN[NIL]} ELSE RETURN[CONS[r, QRelshipSetToList[rs]]]; END; CheckAVList: PROC[avl: AttributeValueList] RETURNS [abort: BOOL] = <> <> <> BEGIN FOR avlT: AttributeValueList_ avl, avlT.rest UNTIL avlT=NIL DO alh: AttributeValue = avl.first; WITH alh.lo SELECT FROM <> t: Tuple => IF IsSystem[t] THEN RETURN[TRUE]; ENDCASE; ENDLOOP; RETURN[FALSE] END; NCodeForTupleMatch: PROCEDURE[ tm: AttributeValueList, i: Index, extreme: {high, low}] RETURNS[ROPE] = <> <> <> <> <> <> <> <> BEGIN count: INT_ 0; s: ROPE_ ""; v: Value_ NIL; if: IndexFactor; ifType: DataType; ifs: LIST OF IndexFactor_ VL2EL[QGetPList[i, ifIndexOf]]; FOR tmL: AttributeValueList_ tm, tmL.rest UNTIL tmL=NIL DO <> IF ifs=NIL THEN <> <> RETURN[s]; if_ ifs.first; ifs_ ifs.rest; IF NOT Eq[V2E[SafeGetP[if, ifAttributeIs]], tmL.first.attribute] THEN ERROR InternalError; -- CanUseIndex only succeeds if same order as index factors IF count#V2I[SafeGetP[if, ifOrdinalPositionIs]] THEN ERROR InternalError; v_ IF extreme=low OR tmL.first.hi=NIL THEN tmL.first.lo ELSE tmL.first.hi; s_ Rope.Cat[s, NCode[v], "\000"]; count_ count+1; ENDLOOP; IF extreme=high THEN <> FOR ifs_ ifs, ifs.rest UNTIL ifs=NIL DO if_ ifs.first; ifType_ V2E[SafeGetP[V2E[SafeGetP[if, ifAttributeIs]], aTypeIs]]; IF ifType=IntType OR ifType=TimeType THEN s_ s.Concat["\377\377\377\377"] ELSE -- RopeType or entity name s_ s.Concat["\377"]; ENDLOOP; RETURN[s]; END; GreaterEqString: PROC[s1, s2: ROPE] RETURNS[BOOLEAN] = <> BEGIN RETURN[Rope.Compare[s1, s2] # less] END; Nconc: PROC[l1, l2: LIST OF Entity] RETURNS [LIST OF Entity] = BEGIN lp: LIST OF Entity_ l1; IF l1=NIL THEN RETURN[l2]; FOR lp_ l1, lp.rest UNTIL lp.rest=NIL DO ENDLOOP; lp.rest_ l2; RETURN[l1]; END; END. Last edited by: Cattell previous to 24-Nov-81 11:40:52: lots of edits for DBView level. Cattell on 24-Nov-81 11:40:52: QRelationSubset on relations with surrogate tuples. Allow N-attribute surrogates, too. Cattell 24-Nov-81 18:04:57: forgot to check for new surrogates in QReleaseRelshipSet. Cattell 25-Nov-81 9:32:20: reformatted some procs, fixed up comments Cattell 25-Nov-81 16:52:30: QGetEntityByName should use version OldOnly Cattell 26-Nov-81 11:03:22: Amazing. QDomainSubset wasn't checking for hi=NIL in nameConstraint so it couldn't have been working, and it was assuming NameProp was the first in constraintList. Cattell 26-Nov-81 12:14:14: More amazing. Guess Eric checked even less of his code than i thought. ConstraintOfProp and EntityValuedPLH didn't produce reducedList correctly under any circumstances, and EntityValuedPLH always returned found=FALSE. Cattell 26-Nov-81 13:17:22: NCodeName needn't put NUL on the end; still needs to be coded to work with multi-part names, though. Cattell 28-Dec-81 8:56:32: Changed NCode to return NIL for NIL arg and MatchingEntity doesn't check types because it's inadequate to deal with subdomains, etc., right now. Really need a CompatibleType[actual, required: DataType] RETURNS [BOOLEAN] that special-cases aTypeProp, the built-in datatypes, and subdomains. Cattell 29-Dec-81 14:03:53: Wow! Spent parts of four days trying to figure out this one 'cause the behavior led me elsewhere. the assignment to es1.attributes in QNextEntity didn't work because es was copied in the discrimination. Changed to an old-style variant discrimination instead of new-style REF discrimination. Here's that bug you couldn't find at the end of the summer, Eric! Cattell 29-Dec-81 16:37:46: More bugs in QNextEntity. WHILE (rel_ ...) DO {loop with no ref to rel!!?}. Just rewrote it all, changed variants around alot. Don't need system variant, Eric intended group variant for IndexTS and IndexFactorTS. Cattell 6-Jan-82 12:57:18: Fixed TransitiveClosure: needs both from and to till have N-to-M props. Cattell April 27, 1982 8:59 pm [10]: Fixed TransitiveClosure: was storing the Relship in thisSub. Cattell April 28, 1982 8:25 am: Changed NCode to convert to upper case. Presumably all index entry, lookup, and conversion for index comparison goes through NCode, thus this will result in indexes requiring upper-case equivalence only. Cattell April 29, 1982 5:15 pm [100]: Bier also had the same bug fixed 26-Nov-81 12:14:14 in FindEntityConstraint. Sigh. Also removed RelshipsReffing, which wasn't used. Cattell April 29, 1982 7:19 pm [100]: Ouch. QRelationSubset[r, LIST[[foo, baz]]], when foo is an attribute targetted to baz's domain, was enumerating the entire domain! Changed to simply return a surrogate tuple for baz. This required inventing a new RelshipSet variant named justOne. Cattell April 29, 1982 8:22 pm: Added DBStats. Cattell May 4, 1982 9:13 am: Allow lookup of DomainDomain, etc., independent of case of name. Cattell May 22, 1982 2:40 pm: Made Fetch1Entity work with system Datatypes and Subtype relation and attributes. Cattell August 1, 1982 1:21 pm: Changed CheckAVList to check for NIL passed as value of anything besides a RopeType field. Previously added CheckAVList, forgot to add to change log. Cattell October 11, 1982 3:57 pm: Implemented timing of critical DBView operations. Removed GetRefProps. Changed some errors to signals. Cattell November 4, 1982 11:30 am: Changes for new properties and indices. Mainly changed the algorithms used by RelationSubset and NextRelship. Algorithm more completely described in View Level Design document. Cattell December 17, 1982 4:06 pm: New segments. Changed DomainSubset, RelationSubset, etc. Moved DBStats. Cattell January 13, 1983 9:42 am: Changed GetAllRefAttributes to find aUnlinked ones. Cattell January 28, 1983 12:29 pm: Fixed NCodeForTupleMatch to handle IntType and DateType fields properly: must concatenate four bytes of 377C's for any leftover index items not in the attribute value list if extreme=high; only use one if string or entity type attribute. FindIndexedConstraint had a minor bug: don't need to concatenate 0C's or 377C's there since NCodeForTupleMatch handles them. Cattell on February 2, 1983 4:46 pm: Fixed QRelationSubset to deal with surrogate case when cosntraint.rest#NIL. Cattell on February 8, 1983 11:39 am: Oops! Forgot to remove cosntraint.rest=NIL check in IF statment in above change. Cattell on March 14, 1983 2:37 pm: Changed FindEntityConstraint to use GetCachedAttributeInfo, to speed it up getting the type and link fields of attributes in the list. Similarly for FindIndexedConstraint. Cattell on March 15, 1983 2:28 pm: Oops. Above changes caused bug: GetCachedAttributeInfo doesn't work on system attributes. Fixed FindEntityConstraint to check for them. Changed by Willie-Sue on February 15, 1985 <>