DIRECTORY DBCommon, DBStats USING [Inc], DBStorage, DBDefs, DB, DBModel, DBModelPrivate, DBModelSchema, Rope; DBModelSetImpl: CEDAR PROGRAM IMPORTS DBStats, DBStorage, DB, DBDefs, DBModel, DBModelPrivate, DBModelSchema, Rope EXPORTS DBModel, DBModelPrivate = BEGIN OPEN DB, DBCommon, DBDefs, DBModel, DBModelPrivate; ROPE: TYPE = Rope.ROPE; numberOfEntitySetObjects: CARDINAL = 4; numberOfRelshipSetObjects: CARDINAL = 4; numberOfDomainSetObjects: CARDINAL = 2; numberOfRelationSetObjects: CARDINAL = 2; eSetList: ARRAY [0..numberOfEntitySetObjects) OF EntitySet; rSetList: ARRAY [0..numberOfRelshipSetObjects) OF RelshipSet; dSetList: ARRAY [0..numberOfDomainSetObjects) OF DomainSet; rnSetList: ARRAY [0..numberOfRelationSetObjects) OF RelationSet; eSetFree: PACKED ARRAY [0..numberOfEntitySetObjects) OF BOOLEAN _ ALL[TRUE]; rSetFree: PACKED ARRAY [0..numberOfRelshipSetObjects) OF BOOLEAN _ ALL[TRUE]; dSetFree: PACKED ARRAY [0..numberOfDomainSetObjects) OF BOOLEAN _ ALL[TRUE]; rnSetFree: PACKED ARRAY [0..numberOfRelationSetObjects) OF BOOLEAN _ ALL[TRUE]; firstFreeESet: [0..numberOfEntitySetObjects] _ 0; firstFreeRSet: [0..numberOfRelshipSetObjects] _ 0; firstFreeDSet: [0..numberOfDomainSetObjects] _ 0; firstFreeRnSet: [0..numberOfRelationSetObjects] _ 0; QDomainSubset: PUBLIC PROCEDURE[ d: Domain, lowName, highName: ROPE_ NIL, start: FirstLast_ First, es: EntitySet, searchSubDomains: BOOL _ TRUE] RETURNS [EntitySet] = BEGIN dl: LIST OF Domain; dt: TupleHandle; IF QNullDomain[d] THEN RETURN[NIL]; IF DBModelSchema.InvalidDomain[d] THEN ERROR Error[InvalidSchema]; IF d.isSystem THEN ERROR Error[NotImplemented]; dt _ DBModelSchema.GetDomainTuple[d]; IF searchSubDomains AND (dl_ QSubTypes[d])#NIL THEN BEGIN -- Nested search of d and its subdomains es.scan _ nested; es.previousDomains _ IF start=Last THEN CONS[d, dl] ELSE NIL; es.remainingDomains _ IF start=First THEN Reverse[CONS[d, dl]] ELSE NIL; es.lowName _ lowName; es.highName _ highName; es.start _ start; es.currentEntitySet _ NIL; RETURN[es] END; IF lowName#NIL THEN BEGIN -- Search using index nameIndex: Index _ PV2E[SafeGetP[dt, dIndexProp]]; lowName_ NCode[S2V[lowName]]; highName_ IF highName=NIL THEN lowName ELSE NCode[S2V[highName]]; es.scan _ index; es.indexScanHandle _ DBStorage.OpenScanIndex[nameIndex, [lowName, highName, TRUE, TRUE], start ]; END ELSE { es.scan _ tupleSet; es.tSetScanHandle _ DBStorage.OpenScanTupleset[dt, start] }; RETURN[es]; END; Reverse: PROC [list: LIST OF Domain] RETURNS [val: LIST OF Domain] = { 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, rs: RelshipSet] RETURNS [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 _ [NIL, [null[]], [null[]]]; -- 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; IF QNullRelation[r] THEN RETURN[NIL]; IF DBModelSchema.InvalidRelation[r] THEN ERROR Error[InvalidSchema]; IF CheckAVList[constraint] THEN { rs.scan _ empty; RETURN[rs] }; IF RelationEq[r, dSubType] AND constraint=NIL THEN ERROR Error[NotImplemented]; IF r.is1to1 THEN BEGIN surrogateR_ r; targetAttr_ GetFirstAttribute[r] END ELSE -- normal relation surrogateR_ NIL; IF surrogateR#NIL AND constraint#NIL AND QAttributeEq[constraint.first.attribute, targetAttr] THEN BEGIN -- no search necessary, want tuple that corresponds to one entity in targetDomain t: SurrogateRelshipHandle_ SurrogateCreateRelship[r]; t.entity_ V2E[constraint.first.lo]; IF constraint.rest=NIL OR MatchingRelship[t, constraint.rest] THEN { rs.scan _ justOne; rs.hereItIs _ t } ELSE rs.scan _ empty; RETURN[rs] END; [indexScan, reducedList]_ FindIndexedConstraint[r, constraint, start]; IF indexScan#NIL THEN BEGIN rs.serialCheck _ reducedList; rs.scan _ index; rs.indexScanHandle _ indexScan; rs.surrogate _ surrogateR; RETURN[rs]; END; [entityConstraint, reducedList, groupScannable]_ FindEntityConstraint[constraint]; IF groupScannable THEN BEGIN recVal _ entityConstraint.attribute.fh; rs.serialCheck _ reducedList; rs.scan _ group; rs.groupScanHandle _ DBStorage.OpenScanGroup[V2E[entityConstraint.lo], recVal, start]; rs.surrogate _ surrogateR; RETURN[rs] END ELSE BEGIN rs.serialCheck _ constraint; rs.scan _ tupleSet; IF r.is1to1 THEN targetTupleset_ DataTypeToEntity[targetAttr.type, r.segment] ELSE targetTupleset_ DBModelSchema.GetRelationTuple[r]; rs.tSetScanHandle _ DBStorage.OpenScanTupleset[targetTupleset, start]; rs.surrogate _ surrogateR; RETURN[rs] END; END; FindEntityConstraint: PROC [constraint: AttributeValueList] RETURNS [con: AttributeValue _ [NIL, [null[]]], reducedList: AttributeValueList, found: BOOLEAN] = BEGIN reducedList_ NIL; found_ FALSE; FOR conL: AttributeValueList_ constraint, conL.rest UNTIL conL = NIL DO a: Attribute = conL.first.attribute; link: LinkType; -- whether it is linked IF a.isSystem THEN link_ Linked ELSE link _ a.link; IF IsDomainType[a.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, [null[]]]; -- 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 r.isSystem THEN RETURN[NIL, constraint]; ifs_ VL2TL[QGetPList[DBModelSchema.GetAttributeTuple[constraint.first.attribute], ifAttributeOf]]; FOR ifs_ ifs, ifs.rest UNTIL ifs=NIL DO i_ PV2E[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_ VL2TL[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#PV2I[SafeGetP[if, ifOrdinalPositionIs]] THEN ERROR InternalError; IF NOT EntityEq[PV2E[SafeGetP[if, ifAttributeIs]], DBModelSchema.GetAttributeTuple[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: LIST OF Attribute] = BEGIN al1, al2, al3: LIST OF Attribute _ NIL; IF NOT IsSystem[e] THEN { d: Domain _ GetCachedEntityInfo[SegmentOf[e], e].domain; dt: TupleHandle _ DBModelSchema.GetDomainTuple[d]; al1_ GetDomainUnlinkedRefAttributes[dt]; al2_ GetDomainSurrogateRefAttributes[dt] }; al3 _ TL2AHL[DBStorage.GetGroupIDs[e]]; RETURN[Nconc[Nconc[al1, al2], al3]]; END; QGetDomainRefAttributes: PUBLIC PROC[d: Domain] RETURNS[al: LIST OF Attribute] = BEGIN IF QNullDomain[d] THEN RETURN[NIL]; IF DBModelSchema.InvalidDomain[d] THEN ERROR Error[InvalidSchema]; IF d.isSystem 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; FindSuperDomains: PROC[d: Domain] RETURNS [LIST OF Domain] = {RETURN[TransitiveClosure[DBModelSchema.GetDomainTuple[d], dSubTypeIs, dSubTypeOf]]}; GetDomainDirectRefAttributes: PROC[d: Domain] RETURNS[al: LIST OF Attribute] = {RETURN[VL2AHL[QGetPList[DBModelSchema.GetDomainTuple[d], aTypeOf]]]}; GetDomainSurrogateRefAttributes: PROC[d: TupleHandle] RETURNS[al: LIST OF Attribute] = {RETURN[VL2AHL[QGetPList[d, aDomainOf]]]}; GetDomainUnlinkedRefAttributes: PROC[d: TupleHandle] RETURNS[al: LIST OF Attribute] = {RETURN[VL2AHL[QGetPList[d, aUnlinkedOf]]]}; QNextEntity: PUBLIC PROCEDURE[es: EntitySet] RETURNS[e: Entity] = BEGIN QNextEntityRec: PROCEDURE[es: EntitySet, esi: CARDINAL] RETURNS[e: Entity] = { IF es=NIL THEN RETURN[NIL]; TRUSTED { SELECT es.scan FROM tupleSet => e_ DBStorage.NextScanTupleset[es.tSetScanHandle]; nested => { DO IF (e_ QNextEntityRec[es.currentEntitySet, esi])#NIL THEN { QReleaseEntitySet[es.currentEntitySet]; ReturnEntitySet[esi]; RETURN[e] }; IF es.remainingDomains=NIL THEN { QReleaseEntitySet[es.currentEntitySet]; ReturnEntitySet[esi]; RETURN[NIL] }; QReleaseEntitySet[es.currentEntitySet]; -- all done with this, get next ReturnEntitySet[esi]; [es.currentEntitySet, esi] _ GetNewEntitySet[]; es.currentEntitySet_ QDomainSubset[ es.remainingDomains.first, es.lowName, es.highName, es.start, es.currentEntitySet, FALSE]; es.previousDomains_ CONS[es.remainingDomains.first, es.previousDomains]; es.remainingDomains_ es.remainingDomains.rest; ENDLOOP }; index => e_ DBStorage.NextScanIndex[es.indexScanHandle]; empty => e_ NIL; ENDCASE => ERROR InternalError; } }; RETURN[QNextEntityRec[es, numberOfEntitySetObjects]]; END; QPrevEntity: PUBLIC PROCEDURE[es: EntitySet] RETURNS[e: Entity] = BEGIN QPrevEntityRec: PROCEDURE[es: EntitySet, esi: CARDINAL] RETURNS[e: Entity] = { IF es=NIL THEN RETURN[NIL]; TRUSTED { SELECT es.scan FROM tupleSet => e_ DBStorage.PrevScanTupleset[es.tSetScanHandle]; nested => { DO IF (e_ QPrevEntityRec[es.currentEntitySet, esi])#NIL THEN { QReleaseEntitySet[es.currentEntitySet]; ReturnEntitySet[esi]; RETURN[e] }; IF es.previousDomains=NIL THEN { QReleaseEntitySet[es.currentEntitySet]; ReturnEntitySet[esi]; RETURN[NIL] }; QReleaseEntitySet[es.currentEntitySet]; ReturnEntitySet[esi]; es.remainingDomains_ CONS[es.previousDomains.first, es.remainingDomains]; es.previousDomains_ es.previousDomains.rest; IF es.previousDomains=NIL THEN { QReleaseEntitySet[es.currentEntitySet]; ReturnEntitySet[esi]; RETURN[NIL] }; [es.currentEntitySet, esi] _ GetNewEntitySet[]; es.currentEntitySet_ QDomainSubset[ es.previousDomains.first, es.lowName, es.highName, Last, es.currentEntitySet, FALSE]; ENDLOOP }; index => e_ DBStorage.PrevScanIndex[es.indexScanHandle]; empty => e_ NIL; ENDCASE => ERROR InternalError; } }; RETURN[QPrevEntityRec[es, numberOfEntitySetObjects]]; END; QNextRelship: PUBLIC PROCEDURE[rs: RelshipSet] RETURNS[t: Relship] = BEGIN ConsSurrogate: PROC[r: Relation] RETURNS [newT: SurrogateRelshipHandle] = { IF QNullRelation[r] THEN ERROR Error[IllegalRelation]; IF DBModelSchema.InvalidRelation[r] THEN ERROR Error[InvalidSchema]; newT _ SurrogateCreateRelship[r]; newT.entity_ NARROW[t]; RETURN[newT] }; IF rs=NIL THEN RETURN[NIL]; TRUSTED { SELECT rs.scan FROM tupleSet => WHILE (t_ DBStorage.NextScanTupleset[rs.tSetScanHandle])#NIL DO IF rs.surrogate#NIL THEN t_ ConsSurrogate[rs.surrogate]; IF MatchingRelship[t, rs.serialCheck] THEN RETURN ENDLOOP; group => WHILE (t_ DBStorage.NextInGroup[rs.groupScanHandle])#NIL DO IF rs.surrogate#NIL THEN t_ ConsSurrogate[rs.surrogate]; IF MatchingRelship[t, rs.serialCheck] THEN RETURN ENDLOOP; index => WHILE (t_ DBStorage.NextScanIndex[rs.indexScanHandle])#NIL DO IF rs.surrogate#NIL THEN t_ ConsSurrogate[rs.surrogate]; IF MatchingRelship[t, rs.serialCheck] THEN RETURN ENDLOOP; justOne => IF rs.hereItIs#NIL THEN {t_ rs.hereItIs; rs.hereItIs_ NIL; RETURN[t]}; empty => RETURN[NIL]; ENDCASE => ERROR InternalError; }; RETURN[NIL]; END; QPrevRelship: PUBLIC PROCEDURE[rs: RelshipSet] RETURNS[t: Relship] = BEGIN ConsSurrogate: PROC[r: Relation] RETURNS [newT: SurrogateRelshipHandle] = { IF QNullRelation[r] THEN ERROR Error[IllegalRelation]; IF DBModelSchema.InvalidRelation[r] THEN ERROR Error[InvalidSchema]; newT _ SurrogateCreateRelship[r]; newT.entity_ NARROW[t]; RETURN[newT] }; IF rs=NIL THEN RETURN[NIL]; TRUSTED { SELECT rs.scan FROM tupleSet => WHILE (t_ DBStorage.PrevScanTupleset[rs.tSetScanHandle])#NIL DO IF rs.surrogate#NIL THEN t_ ConsSurrogate[rs.surrogate]; IF MatchingRelship[t, rs.serialCheck] THEN RETURN ENDLOOP; group => WHILE (t_ DBStorage.PrevInGroup[rs.groupScanHandle])#NIL DO IF rs.surrogate#NIL THEN t_ ConsSurrogate[rs.surrogate]; IF MatchingRelship[t, rs.serialCheck] THEN RETURN ENDLOOP; index => WHILE (t_ DBStorage.PrevScanIndex[rs.indexScanHandle])#NIL DO IF rs.surrogate#NIL THEN t_ ConsSurrogate[rs.surrogate]; IF MatchingRelship[t, rs.serialCheck] THEN RETURN ENDLOOP; justOne => ERROR Error[NotImplemented]; empty => RETURN[NIL]; 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-- TRUSTED BEGIN s: ROPE_ NCode[QGetF[rel, alh.attribute]]; low: ROPE_ NCode[alh.lo]; high: ROPE_ IF NullValue[alh.hi] THEN low ELSE NCode[alh.hi]; ans_ GreaterEqString[s, low] AND GreaterEqString[high, s]; END; QReleaseEntitySet: PUBLIC PROCEDURE[es: EntitySet] = BEGIN IF es=NIL THEN RETURN; TRUSTED { SELECT es.scan FROM nested => {QReleaseEntitySet[es.currentEntitySet]; es.currentEntitySet_ NIL}; index => { IF es.indexScanHandle # NIL THEN { DBStorage.CloseScanIndex[es.indexScanHandle]; es.indexScanHandle_ NIL}}; tupleSet => { IF es.tSetScanHandle # NIL THEN { DBStorage.CloseScanTupleset[es.tSetScanHandle]; es.tSetScanHandle_ NIL}}; empty => NULL; ENDCASE => ERROR; }; es.scan _ empty; END; QReleaseRelshipSet: PUBLIC PROCEDURE[rs: RelshipSet] = BEGIN IF rs=NIL THEN RETURN; TRUSTED { SELECT rs.scan FROM group => { IF rs.groupScanHandle # NIL THEN { DBStorage.CloseScanGroup[rs.groupScanHandle]; rs.groupScanHandle_ NIL}}; tupleSet => { IF rs.tSetScanHandle # NIL THEN { DBStorage.CloseScanTupleset[rs.tSetScanHandle]; rs.tSetScanHandle_ NIL}}; index => { IF rs.indexScanHandle # NIL THEN { DBStorage.CloseScanIndex[rs.indexScanHandle]; rs.indexScanHandle_ NIL}}; justOne => NULL; empty => NULL; ENDCASE => ERROR; }; rs.scan _ empty; 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] = TRUSTED BEGIN FOR avlT: AttributeValueList_ avl, avlT.rest UNTIL avlT=NIL DO alh: AttributeValue = avl.first; WITH v: alh.lo SELECT FROM entity => IF IsSystem[v.value] 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_ ""; if: IndexFactor; ifType: DataType; ifs: LIST OF IndexFactor_ VL2TL[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 EntityEq[PV2E[SafeGetP[if, ifAttributeIs]], DBModelSchema.GetAttributeTuple[tmL.first.attribute]] THEN ERROR InternalError; -- CanUseIndex only succeeds if same order as index factors IF count#PV2I[SafeGetP[if, ifOrdinalPositionIs]] THEN ERROR InternalError; { v: Value _ IF extreme=low OR NullValue[tmL.first.hi] 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 _ DBModelSchema.TupleToAttribute[PV2E[SafeGetP[if, ifAttributeIs]]].type; 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; NullValue: PROC[v: Value] RETURNS[BOOLEAN] = TRUSTED { WITH v SELECT FROM null => RETURN[TRUE]; ENDCASE => RETURN[FALSE] }; GreaterEqString: PROC[s1, s2: ROPE] RETURNS[BOOLEAN] = BEGIN RETURN[Rope.Compare[s1, s2] # less] END; Nconc: PROC[l1, l2: LIST OF Attribute] RETURNS [LIST OF Attribute] = BEGIN lp: LIST OF Attribute _ l1; IF l1=NIL THEN RETURN[l2]; FOR lp_ l1, lp.rest UNTIL lp.rest=NIL DO ENDLOOP; lp.rest_ l2; RETURN[l1]; END; GetNewEntitySet: PUBLIC PROC[] RETURNS [es: EntitySet, esi: CARDINAL] = { IF firstFreeESet = numberOfEntitySetObjects THEN { DBStats.Inc[EntitySetObjectAllocation]; RETURN[NEW[EntitySetObject], numberOfEntitySetObjects] }; es _ eSetList[firstFreeESet]; esi _ firstFreeESet; eSetFree[firstFreeESet] _ FALSE; UNTIL (firstFreeESet _ firstFreeESet + 1) = numberOfEntitySetObjects OR eSetFree[firstFreeESet] DO ENDLOOP; RETURN[es, esi] }; GetNewRelshipSet: PUBLIC PROC[] RETURNS [rs: RelshipSet, rsi: CARDINAL] = { IF firstFreeRSet = numberOfRelshipSetObjects THEN { DBStats.Inc[RelshipSetObjectAllocation]; RETURN[NEW[RelshipSetObject], numberOfRelshipSetObjects] }; rs _ rSetList[firstFreeRSet]; rsi _ firstFreeRSet; rSetFree[firstFreeRSet] _ FALSE; UNTIL (firstFreeRSet _ firstFreeRSet + 1) = numberOfRelshipSetObjects OR rSetFree[firstFreeRSet] DO ENDLOOP; RETURN[rs, rsi] }; GetNewDomainSet: PUBLIC PROC[] RETURNS [ds: DomainSet, dsi: CARDINAL] = { IF firstFreeDSet = numberOfDomainSetObjects THEN { DBStats.Inc[DomainSetObjectAllocation]; RETURN[NEW[DomainSetObject], numberOfDomainSetObjects] }; ds _ dSetList[firstFreeDSet]; dsi _ firstFreeDSet; dSetFree[firstFreeDSet] _ FALSE; UNTIL (firstFreeDSet _ firstFreeDSet + 1) = numberOfDomainSetObjects OR dSetFree[firstFreeDSet] DO ENDLOOP; RETURN[ds, dsi] }; GetNewRelationSet: PROC[] RETURNS [rs: RelationSet, rsi: CARDINAL] = { IF firstFreeRnSet = numberOfRelationSetObjects THEN { DBStats.Inc[RelationSetObjectAllocation]; RETURN[NEW[RelationSetObject], numberOfRelationSetObjects] }; rs _ rnSetList[firstFreeRnSet]; rsi _ firstFreeRnSet; rnSetFree[firstFreeRnSet] _ FALSE; UNTIL (firstFreeRnSet _ firstFreeRnSet + 1) = numberOfRelationSetObjects OR rnSetFree[firstFreeRnSet] DO ENDLOOP; RETURN[rs, rsi] }; ReturnEntitySet: PUBLIC PROC[i: CARDINAL] = { IF i < numberOfEntitySetObjects THEN { eSetList[i].scan _ empty; eSetFree[i] _ TRUE; IF i < firstFreeESet THEN firstFreeESet _ i } }; ReturnRelshipSet: PUBLIC PROC[i: CARDINAL] = { IF i < numberOfRelshipSetObjects THEN { rs: RelshipSet _ rSetList[i]; rs.scan _ empty; rs.surrogate _ NIL; rSetFree[i] _ TRUE; IF i < firstFreeRSet THEN firstFreeRSet _ i } }; ReturnDomainSet: PUBLIC PROC[i: CARDINAL] = { IF i < numberOfDomainSetObjects THEN { dSetFree[i] _ TRUE; IF i < firstFreeDSet THEN firstFreeDSet _ i } }; ReturnRelationSet: PROC[i: CARDINAL] = { IF i < numberOfRelationSetObjects THEN { rnSetFree[i] _ TRUE; IF i < firstFreeRnSet THEN firstFreeRnSet _ i } }; InitializeSetObjects: PUBLIC PROC[] = { i: CARDINAL; FOR i IN [0..numberOfEntitySetObjects) DO eSetList[i] _ NEW[EntitySetObject]; ENDLOOP; FOR i IN [0..numberOfRelshipSetObjects) DO rSetList[i] _ NEW[RelshipSetObject]; ENDLOOP; FOR i IN [0..numberOfDomainSetObjects) DO dSetList[i] _ NEW[DomainSetObject]; ENDLOOP; FOR i IN [0..numberOfRelationSetObjects) DO rnSetList[i] _ NEW[RelationSetObject]; ENDLOOP }; TL2AHL: PUBLIC PROC[tl: LIST OF TupleHandle] RETURNS [LIST OF Attribute] = { IF tl = NIL THEN RETURN[NIL] ELSE RETURN[CONS[DBModelSchema.TupleToAttribute[tl.first], TL2AHL[tl.rest]]] }; VL2AHL: PUBLIC PROC[vl: LIST OF Value] RETURNS [LIST OF Attribute] = { IF vl = NIL THEN RETURN[NIL] ELSE RETURN[CONS[DBModelSchema.TupleToAttribute[V2E[vl.first]], VL2AHL[vl.rest]]] }; END. )JFile: 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 Widom, September 12, 1985 11:29:51 am PDT SetObject Preallocation 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 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 (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 super-Domains of d but not d itself 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 DBObjects 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). 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. Return a pointer to a free EntitySetObject, including the index if it comes from the preallocated array Return a pointer to a free RelshipSetObject, including the index if it comes from the preallocated array Return a pointer to a free DomainSetObject, including the index if it comes from the preallocated array Return a pointer to a free RelationSetObject, including the index if it comes from the preallocated array Release preallocated EntitySetObject with index i; if i >= numberOfEntitySetObjects then the EntitySetObject was newly allocated Release preallocated RelshipSetObject with index i; if i >= numberOfRelshipSetObjects then the RelshipSetObject was newly allocated Release preallocated DomainSetObject with index i; if i >= numberOfDomainSetObjects then the DomainSetObject was newly allocated Release preallocated RelationSetObject with index i; if i >= numberOfRelationSetObjects then the RelationSetObject was newly allocated Preallocate set objects ÊÖ˜šœ™Jšœ Ïmœ1™<—JšœN™Nšœ™Jšœ™Jšœ)™)Jšœ/™/J™)—J˜šÏk ˜ J˜ Jšœžœ˜J˜ J˜Jšžœ˜J˜J˜J˜J˜J˜—šœžœž˜Jšžœžœ6˜TJšžœ˜!J˜—Jšžœžœžœ,˜9J˜Jšžœžœžœ˜head1™Jšœžœ˜'Jšœžœ˜(Jšœžœ˜'Jšœžœ˜)J˜Jšœ žœžœ ˜;Jšœ žœ žœ ˜=Jšœ žœžœ ˜;Jšœ žœ!žœ ˜@J˜Jš œ žœžœžœžœžœžœ˜LJš œ žœžœ žœžœžœžœ˜MJš œ žœžœžœžœžœžœ˜LJš œ žœžœ!žœžœžœžœ˜OJ˜Jšœ1˜1Jšœ2˜2Jšœ1˜1Jšœ4˜4—šœ™šÏn œžœž œ˜ Jš œžœžœ<žœžœžœ˜…JšœM™MJšœM™MJšœK™KJšœ;™;JšœN™NJšœN™NJšœ>™>JšœS™SJšœV™VJšœ ™ Jšž˜Jšœžœžœ˜J˜Jšžœžœžœžœ˜#Jšžœ žœžœ˜BJšžœ žœžœ˜/J˜%šžœžœžœž˜3Jšœ9™9JšžœÏc(˜.J˜Jš œžœ žœžœžœžœ˜=Jš œžœ žœ žœ žœžœ˜HJ˜J˜J˜Jšœžœ˜Jšžœ˜ Jšžœ˜—šžœ žœž˜Jšžœ ˜J˜2J˜Jš œ žœ žœžœ žœ˜AJ˜JšœLžœžœ ˜aJšž˜—šžœ˜Jšœ+™+Jšœ˜Jšœ<˜<—Jšžœ˜ Jšžœ˜J˜—šŸœžœžœžœ žœžœžœ ˜FJšœžœ˜ šžœžœž˜Jšœžœ˜J˜Jšžœ˜—Jšžœ˜ J˜——˜šŸœžœž œ˜"JšœU˜UJšžœ˜Jšœ[™[Jšœ\™\JšœY™YJšœ^™^Jšœ\™\JšœR™RJšœZ™ZJšœ]™]JšœS™SJšœI™IJšœT™TJšœY™YJšœK™KJšœN™NJšœR™RJšœT™TJšœZ™ZJšœ™Jšž˜Jšœ  ˜6Jšœ )˜@Jšœ* 3˜]Jšœ$žœ .˜lJšœ" 6˜XJšœ  2˜RJšœžœ˜J˜%Jšœ™Jšžœžœžœžœ˜%Jšžœ"žœžœ˜Dšžœžœ˜!Jšœžœ˜—Jš žœžœ žœžœžœ˜OJšœ2™2šžœ ž˜JšœX™XJšž˜J˜J˜ Jšž˜—šžœ ˜Jšœ žœ˜—JšœL™Lš žœ žœžœ žœžœ6ž˜bJšžœ Q˜WJ˜5J˜#šžœžœžœ%žœ˜DJšœ˜Jšœ˜—Jšžœ˜Jšžœ˜ Jšžœ˜—Jšœ™J˜Fšžœ žœž˜šž˜J˜J˜J˜J˜Jšžœ˜ —Jšžœ˜—Jšœ™J˜Ršžœž˜šž˜Jšœ'˜'J˜J˜J˜VJ˜Jšžœ˜ —Jšž˜—Jšœ^™^JšœJ™Jšž˜šž˜Jšœ˜Jšœ˜šžœ žœ=˜MJšžœ3˜7—JšœF˜FJšœ˜Jšžœ˜ —Jšžœ˜—Jšžœ˜J˜—šŸœžœ!˜;Jšžœžœ5žœ˜bJšœP™PJšœV™VJšœY™YJšž˜Jšœ žœ˜Jšœžœ˜ šžœ1žœžœž˜GJšœ$˜$Jšœ ˜'Jšžœ žœžœ˜3šžœžœžœž˜@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šœb˜bšžœžœžœž˜'J˜(šžœž˜&˜ Jšžœ˜—˜J˜/J˜1Jšœ žœ˜Jšžœžœ˜—J˜˜J˜/J˜1Jšœ žœ˜Jšžœžœ˜—J˜˜J˜/J˜1J˜Jšžœžœ˜—J˜Jšžœžœ˜—šž˜˜Jšžœ.žœžœ˜V—šžœ˜ Jšžœžœ˜——Jšžœ˜—Jšžœ˜J˜—šŸ œž œ"˜8Jšžœ8˜?JšœV™VJšœ^™^JšœY™YJšœ?™?Jšœ$™$Jšž˜Jšœžœ˜Jšœžœžœ-˜9šžœ'žœžœž˜:Jš žœžœžœ žœžœ˜2J˜Jšžœ/žœžœ˜JJšžœžœcžœžœ ˜€J˜Jšžœ˜—Jšžœžœžœžœ ˜!Jšžœžœ˜Jšžœ˜J˜—š Ÿœžœž œ žœžœžœ ˜RJšœT™TJšœ[™[JšœQ™QJšœ]™]Jšœ\™\Jšœ9™9JšœU™UJšœR™RJšœP™PJšžœžœžœ žœ˜-šžœžœ žœ˜Jšœ8˜8J˜2J˜(J˜+—Jšœ'˜'Jšžœ˜$Jšžœ˜J˜—š Ÿœžœžœ žœžœžœ ˜PJšœ7™7JšœX™XJšž˜Jšžœžœžœžœ˜#Jšžœ žœžœ˜BJšžœ žœžœ˜/J˜$š žœžœžœ&žœžœž˜DJšœ6žœ˜>—Jšžœ˜ Jšžœ˜J˜—š Ÿœžœ žœžœžœ ˜Jš žœžœ$žœžœžœžœ˜E—Jšžœžœ˜ Jšžœ˜J˜—šŸœž œ$žœžœ˜WJšœA™AJš  œžœž˜Jšœžœ#˜*Jšœžœ˜šœžœ˜ Jšžœžœ˜Jšžœ˜—Jšœžœ˜;Jšžœ˜J˜—šŸœžœž œ˜4Jšž˜Jšžœžœžœžœ˜šžœ˜ šžœ ž˜JšœHžœ˜Mšœ ˜ šžœžœžœ˜"JšœBžœ˜H——šœ ˜ šžœžœžœ˜!JšœCžœ˜I——Jšœ žœ˜Jšžœžœ˜—J˜—Jšœ˜Jšžœ˜J˜—šŸœžœž œ˜6Jšž˜Jšžœžœžœžœ˜šžœ˜ šžœ ž˜šœ ˜ šžœžœžœ˜"JšœBžœ˜H——šœ ˜ šžœžœžœ˜!JšœCžœ˜I——šœ ˜ šžœžœžœ˜"JšœBžœ˜H——Jšœ žœ˜Jšœ žœ˜Jšžœžœ˜—J˜—J˜Jšžœ˜——šœ™š Ÿœžœžœžœžœžœ ˜KJšž˜J˜šžœžœž˜ Jšœžœžœ˜$—šž˜Jšžœžœ˜&—Jšžœ˜J˜—š Ÿœžœžœžœžœžœ ˜NJšž˜J˜šžœžœž˜ Jšœžœžœ˜%—šž˜Jšžœžœ˜'—Jšžœ˜J˜—šŸ œžœžœ žœ˜CJšœ:™:Jšœ&™&Jšœ9™9Jšžœž˜ šžœ*žœžœž˜>Jšœ!˜!šžœ žœž˜JšœF™FJš œ žœžœžœžœ˜1Jšžœ˜—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šœžœžœ-˜9šžœ'žœžœž˜:JšœG™Gšžœžœž˜JšœT™TJšœT™TJšžœ˜ —J˜šžœžœcž˜mJšžœ ;˜P—Jšžœ/žœžœ˜JJš œ žœ žœžœžœ˜]J˜'J˜Jšžœ˜—šžœžœ˜Jšœ[™[šžœžœžœž˜'J˜JšœP˜Pšžœžœž˜)J˜—šžœ ˜J˜—Jšžœ˜ ——Jšžœ˜ Jšžœ˜J˜—š Ÿ œžœ žœžœžœ˜6šžœžœž˜Jšœžœžœ˜Jšžœžœžœ˜——J˜š Ÿœžœ žœžœžœ˜6Jšœ8™8Jšžœžœžœ˜.J˜—šŸœžœ žœžœ žœžœžœ ˜DJšžœžœžœ˜!Jšžœžœžœžœ˜Jš žœžœ žœžœžœ˜1J˜ Jšžœ˜ Jšžœ˜J˜—š Ÿœžœžœžœžœ˜IJ™gšžœ*žœ˜2J˜'Jšžœžœ/˜9—Jšœ˜Jšœ˜Jšœžœ˜ šžœ@žœž˜bJšžœ˜—Jšžœ ˜—J˜š Ÿœžœžœžœžœ˜KJ™hšžœ+žœ˜3J˜(Jšžœžœ1˜;—Jšœ˜Jšœ˜Jšœžœ˜ šžœ@žœž˜cJšžœ˜—Jšžœ ˜—J˜š Ÿœžœžœžœžœ˜IJ™gšžœ*žœ˜2J˜'Jšžœžœ/˜9—Jšœ˜Jšœ˜Jšœžœ˜ šžœ@žœž˜bJšžœ˜—šžœ ˜J˜——šŸœžœžœžœ˜FJ™išžœ-žœ˜5J˜)Jšžœžœ3˜=—Jšœ˜Jšœ˜Jšœžœ˜"šžœDžœž˜hJšžœ˜—šžœ ˜J˜——šŸœžœžœžœ˜-Jšœ€™€šžœžœ˜&J˜Jšœžœ˜Jšžœžœ˜0——J˜šŸœžœžœžœ˜.Jšœƒ™ƒšžœžœ˜'Jšœ˜J˜Jšœžœ˜Jšœžœ˜Jšžœžœ˜0—J˜—šŸœžœžœžœ˜-Jšœ€™€šžœžœ˜&Jšœžœ˜Jšžœžœ˜0—J˜—šŸœžœžœ˜(Jšœ†™†šžœ žœ˜(Jšœžœ˜Jšžœžœ˜2—J˜—šŸœž œ˜'Jšœ™Jšœžœ˜ šžœžœž˜)Jšœžœ˜#Jšžœ˜—šžœžœ ž˜*Jšœžœ˜$Jšžœ˜—šžœžœž˜)Jšœžœ˜#Jšžœ˜—šžœžœ!ž˜+Jšœžœ˜&Jšžœ˜ J˜——šŸœžœžœžœžœžœžœžœ˜Lš žœžœžœžœžœ˜Jšžœžœžœ?˜O—J˜—šŸœžœžœžœžœžœžœžœ˜Fš žœžœžœžœžœ˜JšžœžœžœD˜T—J˜——Jšžœ˜J˜—…—PĘä