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; 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] = 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] = 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; CheckRelation[r]; IF CheckAVList[constraint] THEN RETURN; IF r=dSubType AND constraint=NIL THEN ERROR Error[NotImplemented]; relshipSetCount_ relshipSetCount+1; 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; 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; [indexScan, reducedList]_ FindIndexedConstraint[r, constraint, start]; IF indexScan#NIL THEN BEGIN rs_ NEW[RelshipSetObject[index] _ [index [ serialCheck: reducedList, scanHandle: indexScan, surrogate: surrogateR ] ]]; RETURN; END; [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 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] = 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 &ÀFile: DBModelSetImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Contents: Implementation of aggregate / set operations on entities and tuples Last edited by: Eric Bier on 18-Aug-81 14:51:15 Rick Cattell on December 15, 1983 2:54 pm Willie-Sue on February 20, 1985 11:26:41 am PST Counts for balancing Subset/Release calls opaque type objects: exported DBTuplesConcrete => DBModel REFS to opaque type objects Simple narrowing conversions. Since a tuple is a REF, explicit narrowing is sometimes necessary. Searching the database Used with NextEntity to index through entities in a domain. searchSubDomains says whether to search any Domains related to d through the SubType relation. If segment is non-NIL, then domain d is only searched in the given segment; for non-system domains, this argument is currently ignored. If lowName and highName are non-NIL, enumerates only those entities whose name is lexicographically >= lowName and <=highName, and the enumeration is sorted. In this case, we create an "index" variant of DBTuplesConcrete.EntitySetObject. If only highName is NIL, it defaults to lowName, i.e. we will search for the entity whose name equals lowName. If both are NIL, we create an unindexed (tupleSet variant) EntitySet. Here so that don't include system or nested DomainSubsets Enumerate entire domain using tupleset scan Relation r must be a system or dictionary relation, and constraints only on its attributes. The full RelationSubset algorithm is described in the model level design document. Briefly, there are 7 cases, corresponding to the legal combinations of several orthogonal choices: (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. In both the surrogate and normal case, we try for an index scan before trying for a group scan, and try for a group scan before resorting to a tupleset scan. We handle almost all of the surrogate and normal cases with one flow of control, by independently setting the surrogate relation field in the index, tupleset, or group scan. Exception: if r is surrogate and (a) is the case, can use a "justOne" scan. Note system relations are handled just as dictionary relations. In this case, the constraint values determine what segment will be searched, as the relation and constraint attributes give no hint! The group scan which results, however, searches only the appropriate segment, e.g. in RelationSubset[aType, LIST[[aTypeIs, foo]]] searches only the segment of foo. (0) Check that valid parameters IF searchSegment#NIL THEN ERROR Error[NotImplemented]; (1) Determine whether surrogate or normal relation there is not actually a relation, tuples are targetted to the domain ref'd by 1st attrib (2) Try for one-element scan on the target attribute if a surrogate relation (3) Try for index scan (4) Try for group scan (5) Resort to scanning the whole tupleset, either the relation r or if r is surrogate then the domain ref'd by r's 1st attrib; NextRelship will serially check constraint Looks for an entity-valued constraint in the AttributeValueList on which a group scan could be done (i.e., must be a linked attribute). Used by QRelationSubset above. Must explicitly check for system attributes, which GetCachedAttributeInfo doesn't handle. Looks for an index to search to satisfy the constraint, if any. Returns [NIL, constraint] if none. Else returns an appropriate index scan handle for the index, and the reduced constraint list (those constraint elements that are not indexed by the B-tree). Tries all indexes that involve the first attribute in the constraint, returns the first one that indexes one or more of the attributes in the constraint. Used by QRelationSubset. Determines whether the index factors of the given index, when compared in order to the attribute matches in the given tm, (a) differ at any point, (b) are identical and are equal in number, (c) are identical as far as they go but the index includes additional factors not in tm, or (d) are identical but tm includes additional matches. Used by FindIndexedConstraint above. Find all the attributes in which some relationship references e. We do this just by calling DBStorage.GetGroupIDs to get attributes that reference in other tuples (al3 below), and combining the result with any surrogate relation attributes that are actually stored as fields of e itself (al2), and any unlinked relation attributes that might reference e (al1). We don't actually check to see if the latter reference e, because we don't want to pay the cost of the RelationSubset; the client can check. Note: DBModelPrivateImpl.DestroyLinksTo depends on the result of this proc to destroy references to entities. It must therefore return unlinked attributes, and it must find client attributes referencing dictionary (Domain, Relation, etc.) entities. Used to find all attrs reffing d or a superdomain of d. This differs from above because it gets ALL, not just ones that ref a particular entity. Returns ALL attributes whose type is d (surrogate, linked, or unlinked), but not ones whose type is a supertype of d. Returns attributes that appear to reference d, but are actually stored as fields of d's entities. Returns attributes that reference d, but by entity name instead of explicit (group) link. Enumeration operations Find the next element from the scan ordering that also matches the es.serialMatch. Operations depend on type of scan, see DBTuplesConcrete for explanation of the EntitySetObject variants. We can handle: (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) Invariant maintained by this procedure and PrevEntity: currentEntitySet is always valid for nested and segment EntitySets. Similar to QNextEntity, but backs up to previous entity. Not yet implemented for all cases. Note: on nested EntitySets, we assume we are scanning es.previousDomains.first, regardless of whether we are doing PrevEntity or NextEntity calls. Thus, when we run out of entries in the current entity set going backward, the next guy to scan is es.previousDomains.rest.first, and we must move es.previousDomains.first to es.remainingDomains so that we'll get it if we change directions again. If previousDomains is NIL, we've tried to back up past beginning and the currentEntitySet is NIL; it signals to QNextEntity to start again with remainingDomains. All done with currentEntitySet now, move one of previousDomains to remainingDomains. If previousDomains is NIL now, nothing left to scan, and currentEntitySet empty. Find the next element from the scan ordering (which is an index, group, or tupleset scan, see RelationSubset) that also matches the rs.serialCheck. The latter match is done by the following two internal routines. These two routines (MatchingRelship, MatchingAttribute) are for internal use only, they assume their arguments are reasonable. An index, group, or tupleset scan can be on a domain tupleset rather than relation tupleset if the relation is a surrogate relation. In that case we use ConsSurrogate below to create the surrogate relship corresponding to the entity the index, group, or tupelset scan returned. We reach here iff there were no remaining tuples in the index or tupleset scan which satisfied the serialMatch constraints. Similar to QNextRelship, but goes backward to previous; not implemented for all cases. We reach here iff there were no remaining tuples in the index or tupleset scan which satisfied the serialMatch constraints. The following two routines used only by Next/PrevRelship above, they assume ok args. Returns TRUE iff every attribute of t satisfies alh's constraints. Returns TRUE iff t satisfies alh's constraint (on alh.attribute). typeOfalhLo: DataType_ TypeOf[alh.lo]; IF NOT Eq[vt, typeOfalhLo] THEN SIGNAL InternalError; General support routines Checks to make sure types of attributes and lo/hi's match. Returns TRUE if should abort operation This procedure only checks for system entities right now. abort if any system tuples, attributes cannot have system-tuple values Encodes the attribute values specified in tm (using lowValues if extreme = low, else highValues) as required by index i. Uses appropriate extreme values for attributes among the index factors of i but not in tm. Omits attributes in tm but not among the index factors. A null byte is inserted between each attribute value to preserve the lexicographic ordering of the attributes. If extreme=high then either one or four 377C bytes are added on the end for any attributes in the index but not given values in tm, to insure that the result will be greater or equal to any value for these attributes in the index. Find index factor for tmL.first and concatenate the corresponding value No more index factors but there are more attributes in tm: remaining attributes will be checked by serial match, and all tm attributes are bounded by s (above or below). Indexed attributes are superset of match's: append extreme value for leftover index factors Returns TRUE iff s1 is lexically greater or equal to s2. made Cedar, added tioga formatting Ê¿˜šœ™Jšœ Ïmœ1™<—JšœN™NJšœ™Jšœ™Jšœ)™)Jšœ/™/J˜šÏk ˜ Jšœžœ˜"J˜ J˜Jšžœ˜J˜J˜J˜J˜—šœžœž˜Jšžœ žœ˜4Jšžœžœ˜$J˜—Jšžœžœžœ˜'J˜Jšžœžœžœ˜head2šœ)™)Jšœžœžœ˜Jšœžœžœ˜—šœ9™9Jšœ žœžœ ˜8Jšœžœžœ˜(Jšœžœžœ˜)J˜Jšœžœžœ$˜@Jšœžœžœ%˜B—šœ™Jšœ%žœžœ ˜J˜—Jšžœ˜—Jšžœ˜J˜—šŸœžœ!˜;Jšžœ?žœ˜PJšœP™PJšœV™VJšœY™YJšž˜Jšœ žœ˜Jšœžœ˜ šžœ1žœžœž˜GJ˜$Jšœ ˜*Jšœ ˜'Jšžœ žœ%˜8Jšžœ5˜9šžœžœžœž˜>Jšœžœ˜—šž˜Jšœ žœ˜+—šž˜˜šž˜šœžœ˜ šžœ0žœžœž˜FJšœ žœ˜+—Jšž˜—Jšžœ˜——Jš žœ žœžœžœ -˜O—Jšžœ˜—Jšž˜Jšžœ˜J˜—šŸœžœ@˜[JšžœC˜JJšœZ™ZJšœV™VJšœO™OJšœ[™[JšœW™WJšž˜J˜ Jšœžœžœ ˜Jšœžœ˜Jš žœ žœžœ žœžœžœ˜>J˜Ešžœžœžœž˜'J˜'šžœž˜&˜ Jšžœ˜—˜J˜/J˜1Jšœ žœ˜Jšžœžœ˜—J˜˜J˜/J˜1Jšœ žœ˜Jšžœžœ˜—J˜˜J˜/J˜1J˜Jšžœžœ˜—J˜Jšžœžœ˜—šž˜˜šžœ˜Jšœžœžœ˜7——šžœ˜ Jšžœžœ˜——Jšžœ˜—Jšžœ˜J˜—šŸ œž œ"˜8Jšžœ8˜?JšœV™VJšœ^™^JšœY™YJšœ?™?Jšœ$™$Jšž˜Jšœžœ˜Jšœžœžœ-˜9šžœ'žœžœž˜:Jš žœžœžœ žœžœ˜2J˜Jšžœ.žœžœ˜IJšžœžœ;žœžœ ˜XJ˜Jšžœ˜—Jšžœžœžœžœ ˜!Jšžœžœ˜Jšžœ˜J˜—šŸœžœž œ žœ˜NJšœT™TJšœ[™[JšœQ™QJšœ]™]Jšœ\™\Jšœ9™9JšœU™UJšœR™RJšœP™PJšžœžœžœ žœ˜,J˜šžœžœ žœ˜J˜2J˜5—J˜Jšžœ˜$Jšžœ˜J˜—šŸœžœžœ žœ˜LJšœ7™7JšœX™XJšžœ˜Jšžœ žœžœ˜0J˜$š žœžœžœ&žœžœž˜DJšœ6žœ˜>—Jšžœ˜ Jšžœ˜J˜—šŸœžœ žœ˜JJšœL™LJšœ(™(Jšœžœ ˜'J˜—šŸœžœ žœ˜MJšœa™aJšœžœ"˜)J˜—šŸœžœ žœ˜LJšœY™YJšœžœ$˜+——šœ™šŸ œžœž œžœ ˜AJšœR™RJšœN™NJšœ)™)JšœC™CJšœV™VJšœY™YJšœP™PJšœH™HJšœ;™;Jšœ?™?JšœW™WJšœ"™"Jšžœ˜Jš žœžœžœžœžœ˜Jšžœ˜ šžœ žœž˜˜ J˜.—˜ šž˜Jšžœ(žœžœžœ˜=Jš žœžœžœžœžœ˜-Jšœ) ˜H˜$JšœBžœ˜I—Jšœžœ2˜KJ˜0Jšžœ˜ ——˜ šž˜Jšœžœžœ ˜Jšžœ(žœžœžœ˜=Jš žœžœžœžœžœ˜.Jšœ) ˜H˜$J˜HJ˜—J˜QJ˜@Jšžœ˜ ——˜J˜+—˜Jšœžœ˜—Jšžœžœ˜J˜—šžœ˜J˜——šŸ œžœž œžœ ˜AJšœ\™\Jšž˜Jš žœžœžœžœžœ˜Jšžœ˜ šžœ žœž˜˜ J˜.—˜ Jšœ]™]JšœY™YJšœ]™]JšœX™XJšœ™šž˜Jšžœ(žœžœžœ˜=JšœH™HJšœX™XJš žœžœžœžœžœ˜,J˜(JšœT™TJšœžœ2˜LJ˜.JšœP™PJš žœžœžœžœžœžœ˜I˜$Jšœ<žœ˜C—Jšžœ˜ ——˜ šž˜Jšœžœžœ ˜Jšžœ(žœžœžœ˜=Jš žœžœžœžœžœ˜-Jšœ) ˜H˜$J˜HJ˜—J˜NJ˜BJšžœ˜ ——˜J˜+—˜Jšœžœ˜—Jšžœžœ˜J˜—Jšžœ˜J˜—šŸ œžœž œžœ˜DJšœS™SJšœT™TJšœR™RJšœY™YJšœ[™[Jšœ[™[Jšœ^™^Jšž˜šŸ œžœžœ˜:Jšœ:žœ˜H—Jš žœžœžœžœžœ˜Jšžœ˜ šžœ žœž˜˜ šžœ1žœž˜Jš žœžœ$žœžœžœžœ˜E—Jšžœžœ˜ Jšžœ˜J˜—šŸœž œ$žœžœ˜WJšœA™AJš  œž˜Jšœžœ#˜*Jšœžœ˜šœžœ˜ Jšžœžœžœ˜Jšžœ˜—J˜4Jšœ&™&Jšœ5™5Jšœžœ˜;Jšžœ˜J˜—šŸœžœž œ˜4Jšž˜J˜#Jšžœžœžœžœ˜Jšžœ˜ šžœ žœž˜JšœJžœ˜OJšœDžœ˜IJšœJžœ˜OJšœKžœ˜PJšœ žœ˜Jšžœžœ˜J˜—Jšžœ˜J˜—šŸœžœž œ˜6Jšž˜J˜%Jšžœžœžœžœ˜Jšžœ˜ šžœ žœž˜JšœDžœ˜IJšœJžœ˜OJšœDžœ˜IJšœNžœ˜SJšœ žœ˜Jšžœžœ˜J˜—Jšžœ˜——šœ™š Ÿœžœžœžœžœžœ ˜KJšž˜J˜šžœžœž˜ Jšœžœžœ˜$—šž˜Jšžœžœ˜&—Jšžœ˜J˜—š Ÿœžœžœžœžœžœ ˜NJšž˜J˜šžœžœž˜ Jšœžœžœ˜%—šž˜Jšžœžœ˜'—Jšžœ˜J˜—šŸ œžœžœ žœ˜CJšœ:™:Jšœ&™&Jšœ9™9Jšž˜šžœ*žœžœž˜>J˜!šžœžœž˜JšœF™FJš œ žœ žœžœžœ˜-Jšžœ˜—Jšžœ˜—Jšžœžœ˜ Jšžœ˜J˜—šŸœž œ˜Jšœ8žœžœ˜GJšœO™OJšœM™MJšœR™RJšœR™RJšœV™VJšœS™SJšœS™SJšœ"™"Jšž˜Jšœžœ˜Jšœžœ˜ Jšœ žœ˜J˜J˜Jšœžœžœ-˜9šžœ'žœžœž˜:JšœG™Gšžœžœž˜JšœT™TJšœT™TJšžœ˜ —J˜šžœžœ;ž˜EJšžœ ;˜P—Jšžœ.žœžœ˜IJš œžœ žœžœžœžœ˜LJ˜!J˜Jšžœ˜—šžœž˜Jšœ[™[šžœžœžœž˜'J˜J˜Ašžœžœž˜)J˜—šžœ ˜J˜—Jšžœ˜——Jšžœ˜ Jšžœ˜J˜—š Ÿœžœ žœžœžœ˜6Jšœ8™8Jšžœžœžœ˜.J˜—šŸœžœ žœžœ žœžœžœ ˜>Jšžœžœžœ ˜Jšžœžœžœžœ˜Jš žœžœ žœžœžœ˜1J˜ Jšžœ˜ Jšžœ˜J˜——Jšžœ˜J˜J˜J˜J˜GJ˜JšœZžœ˜vJ˜J˜UJ˜J˜EJ˜J˜GJ˜JšœKžœt˜ÂJ˜Jšœòžœ˜øJ˜Jšœ2žœK˜€J˜Jš œ4žœžœ¨žœžœG˜¾J˜Jšœ®žœS˜„J˜Jšœ7žœ žœ©˜óJ˜JšœVžœžœ˜cJ˜J˜bJ˜J˜íJ˜J˜¬J˜JšœAžœÛ˜ J˜J˜/J˜J˜^J˜J˜oJ˜JšœBžœr˜·J˜J˜ŠJ˜J˜ÕJ˜J˜lJ˜J˜UJ˜J˜ŽJ˜Jšœlžœ˜pJ˜JšœNžœ žœ˜wJ˜J˜ÏJ˜J˜­J˜˜*J™"—J˜—…—Tº–9