DIRECTORY BasicTime USING[GMT, earliestGMT, nullGMT, latestGMT], CardTable, DBCommon, DBStorage, DBStorageField USING[NWordsInField], DBDefs, DB, DBModel, DBModelPrivate, Rope, SymTab; DBModelRelationImpl: CEDAR PROGRAM IMPORTS BasicTime, CardTable, DB, DBDefs, DBStorage, DBStorageField, DBModel, DBModelPrivate, Rope, SymTab EXPORTS DBModel, DBModelPrivate = BEGIN OPEN DBCommon, DBDefs; RelationType: TYPE = {normal, property, surrogate}; CreateRelship: PUBLIC PROC[r: Relation, init: ValueSequence] RETURNS[t: Relship] = { KeyRecord: TYPE ~ RECORD[key: Rope.ROPE, handle: IndexHandle]; keyList: LIST OF KeyRecord; CheckRelationKey[r]; IF NOT r.indicesKnown THEN DBModelPrivate.CacheIndices[r]; IF NOT r.attributesKnown THEN CacheAttributes[r]; IF init.count # r.attributes.count THEN ERROR DB.Error[WrongNumberOfFields]; FOR i: CARDINAL IN [0..init.count) DO IF NOT CheckAttribute[attr: r.attributes[i], val: init[i]] THEN ERROR DB.Error[MismatchedAttributeValueType]; ENDLOOP; IF r.isProperty THEN TRUSTED { entity: entity Value = LOOPHOLE[init[0]]; IF PropForEntity[r: r, entity: entity.value, doCheck: FALSE] # NIL THEN ERROR DB.Error[NonUniqueKeyValue] }; FOR k: LIST OF DBDefs.IndexHandle _ r.keys, k.rest UNTIL k = NIL DO thisIndex: DBDefs.FieldSequence = k.first.index; thisKey: Rope.ROPE; FOR i: CARDINAL IN [0..thisIndex.count) DO thisKey _ Rope.Concat[thisKey, DBModelPrivate.NCode[init[thisIndex[i]]]] ENDLOOP; keyList _ CONS[[key: thisKey, handle: k.first], keyList]; ENDLOOP; FOR l: LIST OF KeyRecord _ keyList, l.rest UNTIL l = NIL DO IF DBModelPrivate.CheckKeyInIndex[l.first.handle, l.first.key] THEN ERROR DB.Error[NonUniqueKeyValue]; ENDLOOP; TRUSTED BEGIN theTuple: TupleHandle = IF r.isSurrogate THEN LOOPHOLE[init[0], entity Value].value.tuple ELSE DBStorage.CreateTuple[r.tupleSet]; t _ NEW[RelshipObject _ [relation: r, handle: theTuple]]; FOR i: CARDINAL IN [IF r.isSurrogate THEN 1 ELSE 0 .. r.attributes.count) DO SetField[theTuple, r.attributes[i].fh, init[i]] ENDLOOP; IF r.isSurrogate THEN DBModelPrivate.SetTupleField[theTuple, r.surrogateGroupHandle, r.tupleSet] END; FOR l: LIST OF KeyRecord _ keyList, l.rest UNTIL l = NIL DO DBStorage.InsertIntoIndex[l.first.handle.indexHandle, l.first.key, t.handle] ENDLOOP; FOR l: LIST OF IndexHandle _ r.indices, l.rest UNTIL l = NIL DO DBModelPrivate.AddTupleToIndex[t.handle, r, l.first] ENDLOOP }; CheckTypes: PROC [r: Relation, fields: FieldSequence, val: ValueSequence] RETURNS [legal: BOOL] ~ { legal _ TRUE; IF fields.count # val.count THEN { legal _ FALSE; RETURN }; FOR i: CARDINAL IN [0..fields.count) DO legal _ legal AND CheckAttribute[r.attributes[fields[i]], val[i]] ENDLOOP }; CheckAttribute: PROC [attr: REF AttributeObject, val: Value] RETURNS [legal: BOOL] ~ { WITH val SELECT FROM v: entity Value => IF attr.type = anyDomainType THEN legal _ TRUE ELSE legal _ DBModelPrivate.CompatibleTypes[v.value.domain, attr.type]; v: null Value => legal _ TRUE; ENDCASE => legal _ ORD[val.type] = attr.type.code }; IsLegalIndex: PUBLIC PROC [r: Relation, index: Index, checkKeysOnly: BOOL _ FALSE] RETURNS [legal: BOOL] ~ { RETURN[r.key = index.key AND (NOT checkKeysOnly OR index.isKey)] }; EnsureUniqueKey: PROC [i: IndexHandle, tuple: TupleHandle, r: Relation, field: CARDINAL, v: Value] RETURNS [yes: BOOL] ~ TRUSTED { indexRope: Rope.ROPE; FOR j: CARDINAL IN [0..i.index.count) DO value: Value = IF i.index[j] = field THEN v ELSE PrimitiveGetF[tuple, r, field]; indexRope _ Rope.Concat[indexRope, DBModelPrivate.NCode[value]] ENDLOOP; yes _ NOT DBModelPrivate.CheckKeyInIndex[i, indexRope]; }; LookupRelship: PUBLIC PROC[r: Relation, key: Index, val: ValueSequence] RETURNS[t: Relship] = { keyRope: Rope.ROPE; tuple: DBStorage.TupleHandle; IF NOT r.attributesKnown THEN CacheAttributes[r]; IF NOT r.indicesKnown THEN DBModelPrivate.CacheIndices[r]; IF NOT IsLegalIndex[r: r, index: key, checkKeysOnly: TRUE] THEN ERROR DB.Error[IllegalIndex]; IF NOT CheckTypes[r, key.fields, val] THEN ERROR DB.Error[MismatchedAttributeValueType]; FOR i: CARDINAL IN [0..val.count) DO keyRope _ Rope.Concat[keyRope, DBModelPrivate.NCode[val[i]]] ENDLOOP; tuple _ DBModelPrivate.GetNamedTuple[key.index, keyRope]; RETURN[IF tuple = NIL THEN NIL ELSE NEW[RelshipObject _ [relation: r, handle: tuple]]] }; PropForEntity: PROC[r: Relation, entity: Entity, doCheck: BOOL _ TRUE] RETURNS[handle: TupleHandle] = { IF doCheck THEN { IF NOT r.isProperty THEN ERROR DB.Error[IllegalProperty]; IF NOT DBModelPrivate.CompatibleTypes[entity.domain, r.attributes[0].type] THEN ERROR DB.Error[MismatchedAttributeValueType] }; IF r.isSurrogate THEN IF DBModelPrivate.GetTupleField[entity.tuple, r.surrogateGroupHandle] # NIL THEN RETURN[entity.tuple] ELSE RETURN[NIL]; handle _ DBStorage.TupleForField[entity.tuple, r.attributes[0].fh] }; LookupProperty: PUBLIC PROC[r: Relation, e: Entity] RETURNS[relship: Relship] = { CheckRelationKey[r]; IF NOT r.attributesKnown THEN CacheAttributes[r]; BEGIN handle: TupleHandle = PropForEntity[r, e]; IF handle = NIL THEN RETURN[NIL] ELSE RETURN[NEW[RelshipObject _ [relation: r, handle: handle]]] END }; DestroyRelship: PUBLIC PROC[t: Relship] = { r: Relation = t.relation; tuple: TupleHandle = t.handle; CheckRelationKey[r]; DBModelPrivate.CheckForNull[tuple]; IF NOT r.attributesKnown THEN CacheAttributes[r]; DestroyRelshipTuple[r: r, t: tuple] }; DestroyRelshipTuple: PUBLIC PROC[t: TupleHandle, r: Relation] = { IF NOT r.indicesKnown THEN DBModelPrivate.CacheIndices[r]; FOR indices: LIST OF IndexHandle _ r.keys, indices.rest UNTIL indices = NIL DO DBModelPrivate.DeleteTupleFromIndex[t, r, indices.first] ENDLOOP; FOR indices: LIST OF IndexHandle _ r.indices, indices.rest UNTIL indices = NIL DO DBModelPrivate.DeleteTupleFromIndex[t, r, indices.first] ENDLOOP; FOR i: CARDINAL IN [IF r.isSurrogate THEN 1 ELSE 0 .. r.attributes.count) DO IF r.attributes[i].type = DBDefs.ropeType THEN DBModelPrivate.SetRopeField[t, r.attributes[i].fh, NIL] ELSE IF NOT IsPrimitive[r.attributes[i].type] THEN DBModelPrivate.NullifyTupleField[t, r.attributes[i].fh] ENDLOOP; IF NOT r.isSurrogate THEN DBStorage.DestroyTuple[t] ELSE DBModelPrivate.NullifyTupleField[t, r.surrogateGroupHandle] }; CopyRelship: PUBLIC PROC[t: Relship] RETURNS[Relship] = { IF t = NIL THEN RETURN[NIL]; RETURN[NEW[RelshipObject _ [relation: t.relation, handle: DBModelPrivate.CopyTupleHandle[t.handle]]]] }; SetF: PUBLIC PROC[t: Relship, field: CARDINAL, v: Value] = TRUSTED { IF t = NIL OR t.handle = NIL THEN ERROR DB.Error[NILArgument]; BEGIN r: Relation = t.relation; CheckRelationKey[r]; DBModelPrivate.CheckForNull[t.handle]; IF NOT r.attributesKnown THEN CacheAttributes[r]; IF field NOT IN [0..r.attributes.count) THEN ERROR DB.Error[IllegalAttribute]; IF NOT CheckAttribute[r.attributes[field], v] THEN ERROR DB.Error[MismatchedAttributeValueType]; PrimitiveSetF[t.handle, r, field, v] END }; PrimitiveSetF: PROC[tuple: TupleHandle, r: Relation, field: CARDINAL, val: Value] = { attribute: REF AttributeObject = r.attributes[field]; keyIndex: IndexHandle; IF NOT r.indicesKnown THEN DBModelPrivate.CacheIndices[r]; IF r.isProperty AND field = 0 THEN TRUSTED { entity: entity Value = LOOPHOLE[val]; IF PropForEntity[r: r, entity: entity.value, doCheck: FALSE] # NIL THEN ERROR DB.Error[NonUniqueKeyValue] } ELSE { FOR i: LIST OF IndexHandle _ attribute.indices, i.rest UNTIL i = NIL DO thisIndex: IndexHandle = i.first; IF thisIndex.isKey THEN { keyIndex _ thisIndex; EXIT } ENDLOOP; IF keyIndex # NIL AND NOT EnsureUniqueKey[keyIndex, tuple, r, field, val] THEN ERROR DB.Error[NonUniqueKeyValue] }; FOR i: LIST OF IndexHandle _ attribute.indices, i.rest UNTIL i = NIL DO DBModelPrivate.DeleteTupleFromIndex[tuple, r, i.first] ENDLOOP; IF NOT r.isSurrogate OR field # 0 THEN SetField[tuple, attribute.fh, val] ELSE TRUSTED { WITH val: val SELECT FROM entity => { DBModelPrivate.SetTupleField[val.value.tuple, r.surrogateGroupHandle, r.tupleSet]; -- add the new tuple in DBModelPrivate.NullifyTupleField[tuple, r.surrogateGroupHandle]; -- take the old one out FOR i: CARDINAL IN [1..r.attributes.count) DO SetField[val.value.tuple, r.attributes[i].fh, PrimitiveGetF[tuple, r, i]] ENDLOOP; tuple _ val.value.tuple }; ENDCASE => DB.Error[NILArgument]; }; FOR i: LIST OF IndexHandle _ attribute.indices, i.rest UNTIL i = NIL DO thisIndex: IndexHandle = i.first; DBModelPrivate.AddTupleToIndex[tuple, r, thisIndex] ENDLOOP }; SetField: PROC [t: TupleHandle, fh: DBStorage.FieldHandle, val: Value] ~ TRUSTED { WITH val: val SELECT FROM boolean => DBModelPrivate.SetBoolField[t, fh, val.value]; integer => DBModelPrivate.SetIntField[t, fh, val.value]; rope => DBModelPrivate.SetRopeField[t, fh, val.value]; time => DBModelPrivate.SetTimeField[t, fh, val.value]; entity => DBModelPrivate.SetTupleField[t, fh, val.value.tuple]; null => DBModelPrivate.NullifyTupleField[t, fh]; ENDCASE => DB.Error[NILArgument]; }; PrimitiveGetF: PROC [t: TupleHandle, r: Relation, field: CARDINAL] RETURNS [v: Value _ [null[]]] ~ TRUSTED { IF r.isSurrogate AND field = 0 THEN { domain: Domain = r.attributes[0].domain; BEGIN entity: Entity = NEW[EntityObject _ [domain: domain, tuple: t, name: DBModelPrivate.GetRopeField[t, domain.entityNameHandle]]]; RETURN[Value[entity[value: entity]]] END }; BEGIN attr: REF AttributeObject = r.attributes[field]; IF DBDefs.IsPrimitive[attr.type] THEN { SELECT attr.type FROM DBDefs.boolType => v _ [boolean[value: DBModelPrivate.GetBoolField[t, attr.fh]]]; DBDefs.intType => v _ [integer[value: LOOPHOLE[DBModelPrivate.GetIntField[t, attr.fh]]]]; DBDefs.ropeType => v _ [rope[value: DBStorage.ReadVarByte[t, attr.fh]]]; DBDefs.timeType => v _ [time[value: DBModelPrivate.GetTimeField[t, attr.fh]]]; ENDCASE => NULL } ELSE { tuple: DBCommon.TupleHandle = DBModelPrivate.GetTupleField[t, attr.fh]; IF DBStorage.NullTuple[tuple] THEN v _ [null[]] ELSE BEGIN domain: Domain = IF attr.type # anyDomainType THEN attr.domain ELSE DBModelPrivate.TupleToDomain[DBStorage.ReadTupleset[tuple], r.segment]; name: Rope.ROPE = DBModelPrivate.GetRopeField[tuple, domain.entityNameHandle]; v _ [entity[value: NEW[DBDefs.EntityObject _ [domain, name, tuple]]]] END } END }; GetF: PUBLIC PROC[t: Relship, field: CARDINAL] RETURNS [v: Value _ [null[]]] = TRUSTED { IF t = NIL OR t.handle = NIL THEN ERROR DB.Error[NILArgument]; BEGIN r: Relation = t.relation; CheckRelationKey[r]; DBModelPrivate.CheckForNull[t.handle]; IF NOT r.attributesKnown THEN CacheAttributes[r]; IF field >= r.attributes.count THEN ERROR DB.Error[IllegalAttribute] ELSE v _ PrimitiveGetF[t.handle, r, field] END }; SetP: PUBLIC PROC[e: Entity, r: Relation, field: CARDINAL, v: Value] = { CheckRelationKey[r]; DBModelPrivate.CheckForNull[e.tuple]; IF NOT r.attributesKnown THEN CacheAttributes[r]; IF field >= r.attributes.count OR NOT CheckAttribute[r.attributes[field], v] THEN ERROR DB.Error[IllegalAttribute]; BEGIN tuple: DBStorage.TupleHandle = PropForEntity[r, e]; IF tuple = NIL THEN ERROR DB.Error[NotFound]; PrimitiveSetF[tuple, r, field, v] END }; GetP: PUBLIC PROC[e: Entity, r: Relation, field: CARDINAL] RETURNS[v: Value _ [null[]]] = { CheckRelationKey[r]; DBModelPrivate.CheckForNull[e.tuple]; IF NOT r.attributesKnown THEN CacheAttributes[r]; IF field >= r.attributes.count THEN ERROR DB.Error[IllegalAttribute]; TRUSTED BEGIN tuple: DBStorage.TupleHandle = PropForEntity[r, e]; IF tuple = NIL THEN ERROR DB.Error[NotFound]; v _ PrimitiveGetF[tuple, r, field] END }; NameToField: PUBLIC PROC[r: Relation, name: ROPE] RETURNS[exists: BOOL, pos: CARDINAL] = { CheckRelationKey[r]; IF NOT r.attributesKnown THEN CacheAttributes[r]; FOR pos IN [0..r.attributes.count) DO IF Rope.Equal[name, r.attributes[pos].name] THEN {exists _ TRUE; RETURN} ENDLOOP; exists _ FALSE }; FieldDescription: PUBLIC PROC[r: Relation, pos: CARDINAL] RETURNS[field: DB.Field] = { CheckRelationKey[r]; IF NOT r.attributesKnown THEN CacheAttributes[r]; IF pos > r.attributes.count THEN DB.Error[IllegalAttribute]; field _ GetDescription[r, pos] }; GetDescription: PROC[r: Relation, pos: CARDINAL] RETURNS[field: DB.Field] = TRUSTED { field.name _ r.attributes[pos].name; field.type _ DB.TypeSpec[direct[r.attributes[pos].type]]; IF r.attributes[pos].type = ropeType THEN { field.lengthHint _ (DBStorageField.NWordsInField[r.attributes[pos].fh] * 2) - 2 } }; Fields: PUBLIC PROC[r: Relation] RETURNS[fields: DB.FieldSpec] = { CheckRelationKey[r]; IF NOT r.attributesKnown THEN CacheAttributes[r]; fields _ NEW[DB.FieldSpecObject[r.attributes.count]]; FOR i: CARDINAL IN [0..r.attributes.count) DO fields[i] _ GetDescription[r, i] ENDLOOP }; FieldCount: PUBLIC PROC[r: Relation] RETURNS[count: CARDINAL] = { CheckRelationKey[r]; IF NOT r.attributesKnown THEN CacheAttributes[r]; count _ r.attributes.count }; RelationOf: PUBLIC PROC [t: Relship] RETURNS [Relation] = { CheckRelationKey[t.relation]; RETURN[t.relation] }; IndexSequence: TYPE = REF IndexSequenceObject; IndexSequenceObject: TYPE = RECORD[ SEQUENCE count: CARDINAL OF Index]; DeclareRelation: PUBLIC PROC [name: ROPE, segment: Segment, fields: DB.FieldSpec, isProperty: BOOL] RETURNS[r: Relation] = { sh: SegmentHandle = DBModelPrivate.SegmentToHandle[segment]; relationIndex: DBStorage.IndexHandle = sh.indices[DBCommon.relationIndex]; EvalCode: PROC[ type: DB.TypeSpec ] RETURNS[ code: TypeCode ] = TRUSTED { WITH type: type SELECT FROM direct => code _ type.code; indirect => code _ type.code[] ENDCASE }; AddToCache: PROC[tuple: TupleHandle] RETURNS[r: Relation] = { relationEntry: REF RelationObject = NEW[RelationObject _ [key: sh.key, segment: segment, name: name, tupleSet: DBModelPrivate.CopyTupleHandle[tuple]]]; r _ relationEntry; [] _ SymTab.Store[x: sh.relationTable, key: name, val: relationEntry]; RETURN[r] }; DefineFields: PROC[t: TupleHandle] = { isFirst: BOOL _ TRUE; -- This is TRUE only the first time SetFieldHandle is called tupleSet: DBStorage.TuplesetHandle _ NIL; -- This is set at the first call to SetFieldHandle to the tuple set actually storing the tuples of the relation (which may be a domain if a surrogate is used) domain: Domain; attributeGroup: DBStorage.GroupScanHandle = DBStorage.OpenScanGroup[t, relationHandle, Last]; SetFieldHandle: PROC[attr: TupleHandle, field: DB.Field] RETURNS[fh: DBStorage.FieldHandle] = { type: TypeCode = EvalCode[field.type]; length: CARDINAL = field.lengthHint; descriptor: DBStorage.FieldDescriptor = SELECT type FROM boolType => DBModelPrivate.OneWordDescriptor, intType, timeType => DBStorage.FieldDescriptor[TwoWord[]], ropeType => DBStorage.FieldDescriptor[VarByte[length]], ENDCASE => DBStorage.FieldDescriptor[Group[groupID: attr]]; IF NOT IsPrimitive[type] THEN domain _ DBModel.TypeToDomain[type, segment]; IF NOT isFirst THEN { fh _ DBStorage.AddField[tupleSet, descriptor]; SetFieldField[attr, fieldHandleHandle, fh] } ELSE { isFirst _ FALSE; IF NOT isProperty OR NOT DBModelPrivate.EmptyTupleSet[domain.tupleSet] OR NOT DBModelPrivate.NoSubtypes[domain] THEN { fh _ DBStorage.AddField[t, descriptor]; tupleSet _ t; SetCardField[t, propertyHandle, IF isProperty THEN ORD[RelationType[property]] ELSE ORD[RelationType[normal]]]; SetFieldField[attr, fieldHandleHandle, fh] } ELSE { SetCardField[t, propertyHandle, ORD[RelationType[surrogate]]]; r.isSurrogate _ TRUE; tupleSet _ domain.tupleSet; DBModelPrivate.SetTupleField[t, surrogateDomainHandle, domain.tupleSet]; r.surrogateGroupHandle _ DBStorage.AddField[tupleSet, DBStorage.FieldDescriptor[Group[groupID: t]]]; SetFieldField[t: t, f: surrogateHandleHandle, val: r.surrogateGroupHandle]; r.surrogateDomain _ domain }; } }; SetCardField[t, countHandle, fields.count]; r.attributes _ NEW[AttributeSequence[fields.count]]; FOR i: CARDINAL IN [0..fields.count) DO field: DB.Field = fields[i]; type: DBDefs.TypeCode = EvalCode[field.type]; attrObj: REF AttributeObject = NEW[AttributeObject _ [name: field.name, type: type]]; attr: DBStorage.TupleHandle = DBStorage.CreateSystemPageTuple[x: AttributeTupleSet, y: t, s: segment]; DBModelPrivate.SetCardField[attr, posHandle, i]; DBModelPrivate.SetTypeField[attr, typeHandle, type]; DBModelPrivate.SetRopeField[attr, aNameHandle, field.name]; DBStorage.WriteTID[attr, attributeGroup]; attrObj.fh _ SetFieldHandle[attr, field]; attrObj.tuple _ attr.tid; IF NOT DBDefs.IsPrimitive[type] AND NOT type = anyDomainType THEN { DBModelPrivate.SetTupleField[attr, domainHandle, domain.tupleSet]; attrObj.domain _ domain }; r.attributes[i] _ attrObj; ENDLOOP; r.attributesKnown _ TRUE; DBStorage.CloseScanGroup[attributeGroup] }; r _ LookupRelation[name, segment]; IF r # NIL THEN { IF NOT r.attributesKnown THEN CacheAttributes[r]; IF fields.count # r.attributes.count THEN ERROR DB.Error[MismatchedExistingRelation]; FOR i: CARDINAL IN [0..fields.count) DO IF NOT Rope.Equal[fields[i].name, r.attributes[i].name] OR EvalCode[fields[i].type] # r.attributes[i].type THEN ERROR DB.Error[MismatchedExistingRelation] ENDLOOP; IF isProperty # r.isProperty THEN ERROR DB.Error[MismatchedExistingRelation]; RETURN[r] }; IF isProperty AND (IsPrimitive[EvalCode[fields[0].type]] OR EvalCode[fields[0].type] = anyDomainType) THEN ERROR DB.Error[IllegalProperty]; BEGIN t: TupleHandle = DBStorage.CreateSystemPageTuple[x: DBModelPrivate.RelationTupleSet, y: NIL, s: segment]; DBStorage.CreateTupleset[t]; DBStorage.WriteVarByte[t, rNameHandle, name]; DBStorage.InsertIntoIndex[relationIndex, name, t]; r _ AddToCache[t]; DefineFields[t]; r.isProperty _ isProperty; sh.schemaUpdated _ TRUE; RETURN[r] END }; LookupRelation: PUBLIC PROC[name: Rope.ROPE, segment: Segment] RETURNS[r: Relation] = { sh: SegmentHandle = DBModelPrivate.SegmentToHandle[segment]; relationIndex: DBStorage.IndexHandle = sh.indices[DBCommon.relationIndex]; AddToCache: PROC[tuple: TupleHandle] RETURNS[r: Relation] = { relationEntry: REF RelationObject = NEW[RelationObject _ [key: sh.key, segment: segment, name: name, tupleSet: DBModelPrivate.CopyTupleHandle[tuple]]]; r _ relationEntry; [] _ SymTab.Store[x: sh.relationTable, key: name, val: relationEntry]; RETURN[r] }; r _ NARROW[SymTab.Fetch[x: sh.relationTable, key: name].val]; IF r # NIL THEN RETURN[r]; BEGIN tuple: TupleHandle = DBModelPrivate.GetNamedTuple[relationIndex, name]; IF tuple = NIL THEN RETURN[NIL]; -- it's not to be found r _ AddToCache[tuple] END }; RelationsByName: PUBLIC PROC [segment: Segment, lowName, highName: ROPE, start: DBCommon.FirstLast] RETURNS[rs: RelationSet] = { iScan: DBStorage.IndexHandle = DBModelPrivate.SegmentToHandle[segment].indices[DBCommon.relationIndex]; IF highName=NIL THEN highName _ lowName; rs _ NEW[RelationSetObject _ [segment: segment, variant: index[ scanHandle: DBStorage.OpenScanIndex[iScan, [lowerBound: lowName, upperBound: highName, includeLowerBound: TRUE, includeUpperBound: TRUE, lowerBoundInfinity: lowName = NIL, upperBoundInfinity: highName = NIL], start]]]]; RETURN[rs] }; NextRelation: PUBLIC PROC [rs: RelationSet] RETURNS [Relation] = TRUSTED { name: Rope.ROPE; tuple: DBCommon.TupleHandle; WITH rs: rs SELECT FROM index => { name _ DBModelPrivate.NameFromIndexKey[DBStorage.NextScanKey[rs.scanHandle]]; tuple _ IF name # NIL THEN DBStorage.ThisScanTuple[rs.scanHandle] ELSE NIL }; relationsForDomain => { FOR tuple _ DBStorage.NextInGroup[rs.scanHandle], DBStorage.NextInGroup[rs.scanHandle] UNTIL tuple = NIL OR tuple # rs.lastTuple DO ENDLOOP; rs.lastTuple _ tuple }; ENDCASE; RETURN[TupleToRelation[tuple, rs.segment, name]] }; PrevRelation: PUBLIC PROC [rs: RelationSet] RETURNS [Relation] = TRUSTED { name: Rope.ROPE; tuple: DBCommon.TupleHandle; WITH rs: rs SELECT FROM index => { name _ DBModelPrivate.NameFromIndexKey[DBStorage.PrevScanKey[rs.scanHandle]]; tuple _ IF name # NIL THEN DBStorage.ThisScanTuple[rs.scanHandle] ELSE NIL }; relationsForDomain => { FOR tuple _ DBStorage.PrevInGroup[rs.scanHandle], DBStorage.PrevInGroup[rs.scanHandle] UNTIL tuple = NIL OR tuple # rs.lastTuple DO ENDLOOP; rs.lastTuple _ tuple }; ENDCASE; RETURN[TupleToRelation[tuple, rs.segment, name]] }; ReleaseRelationSet: PUBLIC PROC [rs: RelationSet] = TRUSTED { IF rs = NIL THEN RETURN; WITH rs: rs SELECT FROM index => { DBStorage.CloseScanIndex[rs.scanHandle]; rs.scanHandle _ NIL }; relationsForDomain => { DBStorage.CloseScanGroup[rs.scanHandle]; rs.scanHandle _ NIL }; ENDCASE }; TupleToRelation: PUBLIC PROC[t: TupleHandle, segment: Segment, name: Rope.ROPE _ NIL] RETURNS[r: Relation] = { IF t = NIL THEN RETURN[NIL]; IF name = NIL THEN name _ DBStorage.ReadVarByte[t, rNameHandle]; BEGIN sh: SegmentHandle = DBModelPrivate.SegmentToHandle[segment]; val: REF ANY = SymTab.Fetch[x: sh.relationTable, key: name].val; r _ NARROW[val]; IF r # NIL THEN RETURN[r] ELSE { relationEntry: REF RelationObject = NEW[RelationObject _ [key: sh.key, segment: segment, name: name, tupleSet: DBModelPrivate.CopyTupleHandle[t]]]; r _ relationEntry; [] _ SymTab.Store[x: sh.relationTable, key: name, val: relationEntry] } END }; FetchAttribute: PUBLIC PROC [r: Relation, name: ROPE] RETURNS[a: Attribute] = { CheckRelationKey[r]; IF NOT r.attributesKnown THEN CacheAttributes[r]; FOR i: CARDINAL IN [0..r.attributes.count) DO IF Rope.Equal[r.attributes[i].name, name] THEN RETURN[NEW[AttributeHandle _ [pos: i, relation: r]]] ENDLOOP; RETURN[NIL] }; CacheAttributes: PUBLIC PROC[r: Relation] = { sh: SegmentHandle = DBModelPrivate.SegmentToHandle[r.segment]; type: RelationType = VAL[GetCardField[r.tupleSet, propertyHandle]]; r.attributesKnown _ TRUE; BEGIN attributeGroup: DBStorage.GroupScanHandle = DBStorage.OpenScanGroup[r.tupleSet, relationHandle, First]; attributeCount: CARDINAL = GetCardField[r.tupleSet, countHandle]; attributeRecord: AttrSeqHandle = NEW[ AttributeSequence[attributeCount] ]; thisAttribute: CARDINAL _ 0; FOR t: TupleHandle _ DBStorage.NextInGroup[attributeGroup], DBStorage.NextInGroup[attributeGroup] UNTIL t = NIL DO thisAttributeObject: REF AttributeObject _ NEW[AttributeObject]; attributeRecord[thisAttribute] _ thisAttributeObject; thisAttributeObject.tuple _ t.tid; thisAttributeObject.name _ DBModelPrivate.GetRopeField[t, aNameHandle]; thisAttributeObject.type _ DBModelPrivate.GetTypeField[t, typeHandle]; BEGIN domain: TupleHandle = DBModelPrivate.GetTupleField[t, domainHandle]; IF domain # NIL THEN { thisAttributeObject.domain _ DBModelPrivate.TupleToDomain[domain, r.segment]; IF NOT thisAttributeObject.domain.detailsKnown THEN DBModelPrivate.ReadDomain[thisAttributeObject.domain] } END; thisAttributeObject.fh _ DBModelPrivate.GetFieldField[t, fieldHandleHandle]; thisAttribute _ thisAttribute + 1; ENDLOOP; r.attributes _ attributeRecord; DBStorage.CloseScanGroup[attributeGroup] END; r.isSurrogate _ type = surrogate; r.isProperty _ type # normal; IF r.isSurrogate THEN { domainTuple: DBStorage.TupleHandle _ DBStorage.ConsSystemTupleObject[]; r.surrogateGroupHandle _ GetFieldField[r.tupleSet, surrogateHandleHandle]; domainTuple.tid _ r.attributes[0].type.code; domainTuple.cacheHint _ NIL; r.surrogateDomain _ DBModelPrivate.TupleToDomain[domainTuple, r.segment] } }; CheckRelationKey: PUBLIC PROC[r: Relation] = { sh: SegmentHandle = DBModelPrivate.SegmentToHandle[r.segment]; IF sh.key # r.key THEN ERROR DB.Error[IllegalRelation] }; AttributeInfo: PUBLIC PROC [a: Attribute] RETURNS [name: ROPE, r: Relation, pos: NAT, type: TypeCode] = { CheckRelationKey[a.relation]; pos _ a.pos; BEGIN attr: REF AttributeObject = a.relation.attributes[pos]; name _ attr.name; type _ attr.type; END }; RelationEq: PUBLIC PROC [r1, r2: Relation] RETURNS[BOOL] = { IF r1 = NIL THEN RETURN[r2 = NIL]; IF r2 = NIL THEN RETURN[FALSE]; CheckRelationKey[r1]; CheckRelationKey[r2]; RETURN[r1^ = r2^] }; AttributeEq: PUBLIC PROC [a1, a2: Attribute] RETURNS[BOOL] = { IF a1 = NIL THEN RETURN[a2 = NIL]; IF a2 = NIL THEN RETURN[FALSE]; RETURN[a1.pos = a2.pos AND RelationEq[a1.relation, a2.relation]] }; NullRelation: PUBLIC PROC [r: Relation] RETURNS[BOOLEAN] = { IF r = NIL THEN RETURN[TRUE]; BEGIN sh: SegmentHandle = DBModelPrivate.SegmentToHandle[r.segment]; RETURN[r.key # sh.key] END }; NullAttribute: PUBLIC PROC [a: Attribute] RETURNS[BOOLEAN] = { RETURN[a = NIL OR NullRelation[a.relation]] }; NullRelship: PUBLIC PROC[r: Relship] RETURNS[yes: BOOL] = { IF r = NIL THEN RETURN[TRUE]; IF DBStorage.NullTuple[r.handle] THEN RETURN[TRUE]; BEGIN relation: Relation = r.relation; sh: SegmentHandle = DBModelPrivate.SegmentToHandle[relation.segment]; RETURN[relation.key # sh.key] END }; RelshipEq: PUBLIC PROC[r1: Relship, r2: Relship] RETURNS[yes: BOOL] = { IF NullRelship[r1] THEN RETURN[NullRelship[r2]]; IF NullRelship[r2] THEN RETURN[NullRelship[r1]]; RETURN[RelationEq[r1.relation, r2.relation] AND r1.handle.tid = r2.handle.tid] }; RelationSubset: PUBLIC PROC [r: Relation, index: Index, constraint: Constraint, start: FirstLast] RETURNS [rs: RelshipSet] = { CheckConstraint: PROC[] = { IF index.fields.count # constraint.count THEN ERROR DB.Error[IllegalConstraint]; FOR i: CARDINAL IN [0..constraint.count) DO thisAttributeType: TypeCode = r.attributes[index.fields[i]].type; thisConstraint: ValueConstraint = constraint[i]; IF constraint[i].type = null THEN ERROR DB.Error[IllegalConstraint]; IF IsPrimitive[thisAttributeType] THEN IF ORD[constraint[i].type] # thisAttributeType.code THEN ERROR DB.Error[IllegalConstraint] ELSE IF thisAttributeType # anyDomainType THEN { WITH thisConstraint SELECT FROM v: entity ValueConstraint => IF NOT DBModelPrivate.CompatibleTypes[v.value.domain, thisAttributeType] THEN ERROR DB.Error[IllegalConstraint]; ENDCASE } ENDLOOP }; NCodeConstraint: PROC[c: ValueConstraint, checkForNils: BOOL] RETURNS[low, high: Rope.ROPE, noDecodeNeeded: BOOL] = TRUSTED { WITH c: c SELECT FROM boolean => { low _ high _ DBModelPrivate.NCodeBool[c.value]; noDecodeNeeded _ TRUE }; integer => { low _ DBModelPrivate.NCodeInt[c.low]; high _ DBModelPrivate.NCodeInt[c.high]; noDecodeNeeded _ (c.low = c.high) OR (c.low = FIRST[INT] AND c.high = LAST[INT]) }; rope => { noDecodeNeeded _ (c.low = NIL AND c.high = NIL) OR (Rope.Equal[c.low, c.high]); low _ IF checkForNils AND c.low = NIL THEN NIL ELSE DBModelPrivate.NCodeRope[c.low]; high _ IF checkForNils AND c.high = NIL THEN NIL ELSE DBModelPrivate.NCodeRope[IF c.high = NIL THEN "\177" ELSE c.high] }; time => { IF c.low = BasicTime.nullGMT THEN c.low _ BasicTime.earliestGMT; IF c.high = BasicTime.nullGMT THEN c.high _ BasicTime.latestGMT; noDecodeNeeded _ (c.low = c.high) OR (c.low = BasicTime.earliestGMT AND c.high = BasicTime.latestGMT); low _ DBModelPrivate.NCodeTime[c.low]; high _ DBModelPrivate.NCodeTime[c.high] }; entity => { domain: Domain = c.value.domain; DBModelPrivate.CheckDomainKey[domain]; low _ high _ DBModelPrivate.NCodeTuple[c.value.tuple]; noDecodeNeeded _ TRUE } ENDCASE => NULL }; CheckRelationKey[r]; IF NOT r.attributesKnown THEN CacheAttributes[r]; IF NOT r.indicesKnown THEN DBModelPrivate.CacheIndices[r]; IF index = NIL THEN { -- scan the entire relation IF r.isSurrogate THEN { scanHandle: DBStorage.GroupScanHandle = DBStorage.OpenScanGroup[r.tupleSet, r.surrogateGroupHandle, start]; RETURN[NEW[RelshipSetObject _ [relation: r, variant: group[scanHandle]]]] } ELSE { scanHandle: DBStorage.TuplesetScanHandle = DBStorage.OpenScanTupleset[r.tupleSet, start]; RETURN[NEW[RelshipSetObject _ [relation: r, variant: tupleSet[scanHandle]]]] } }; IF NOT IsLegalIndex[r, index] THEN ERROR DB.Error[IllegalConstraint]; IF constraint = NIL THEN { scanHandle: DBStorage.IndexScanHandle = DBStorage.OpenScanIndex[index.index, DBStorage.Selection[lowerBound: NIL, upperBound: NIL, includeLowerBound: TRUE, includeUpperBound: TRUE, lowerBoundInfinity: TRUE, upperBoundInfinity: TRUE], start]; RETURN[NEW[RelshipSetObject _ [relation: r, variant: simpleScan[scanHandle]]]] }; CheckConstraint[]; IF constraint.count = 1 THEN { -- do a simple scan low, high: Rope.ROPE; scanHandle: DBStorage.IndexScanHandle; [low ~ low, high ~ high] _ NCodeConstraint[c: constraint[0], checkForNils: TRUE]; scanHandle _ DBStorage.OpenScanIndex[index.index, DBStorage.Selection[lowerBound: low, upperBound: high, includeLowerBound: TRUE, includeUpperBound: TRUE, lowerBoundInfinity: low = NIL, upperBoundInfinity: high = NIL], start]; RETURN[NEW[RelshipSetObject _ [relation: r, variant: simpleScan[scanHandle]]]] } ELSE { -- this is the complicated case low: RopeSequence _ NEW[RopeSequenceObject[constraint.count]]; high: RopeSequence _ NEW[RopeSequenceObject[constraint.count]]; lowCompleteKey, highCompleteKey: Rope.ROPE; scanHandle: DBStorage.IndexScanHandle; dense: BOOL _ TRUE; FOR i: CARDINAL IN [0..constraint.count) DO noDecodeNeeded: BOOL; [low ~ low[i], high ~ high[i], noDecodeNeeded ~ noDecodeNeeded] _ NCodeConstraint[c: constraint[i], checkForNils: FALSE]; lowCompleteKey _ Rope.Concat[lowCompleteKey, low[i]]; highCompleteKey _ Rope.Concat[highCompleteKey, high[i]]; IF i # constraint.count-1 THEN dense _ dense AND noDecodeNeeded ENDLOOP; scanHandle _ DBStorage.OpenScanIndex[index.index, DBStorage.Selection[lowerBound: lowCompleteKey, upperBound: highCompleteKey, includeLowerBound: TRUE, includeUpperBound: TRUE], start]; IF dense THEN RETURN[NEW[RelshipSetObject _ [relation: r, variant: simpleScan[scanHandle]]]] ELSE RETURN[NEW[RelshipSetObject _ [relation: r, variant: complexScan[handle: NEW[ComplexScanObject _ [scanHandle: scanHandle, low: low, high: high, thisOne: NEW[RopeSequenceObject[constraint.count]]]]]]]] } }; RelshipsForEntity: PUBLIC PROC [e: Entity] RETURNS [rs: RelshipSet] = { DBModelPrivate.CheckDomainKey[e.domain]; DBModelPrivate.CheckForNull[e.tuple]; BEGIN attributeList: LIST OF TupleHandle = DBStorage.GetGroupIDs[e.tuple]; relationTuple: TupleHandle = DBModelPrivate.GetTupleField[attributeList.first, relationHandle]; thisRelation: Relation = TupleToRelation[relationTuple, e.domain.segment]; fieldHandle: DBStorage.FieldHandle = DBModelPrivate.GetFieldField[attributeList.first, fieldHandleHandle]; thisGroupScan: DBStorage.GroupScanHandle = DBStorage.OpenScanGroup[e.tuple, fieldHandle, First]; rs _ NEW[RelshipSetObject _ [relation: NIL, variant: scanForEntity[handle: NEW[EntityScanObject _ [entityTuple: e.tuple, fields: attributeList.rest, relationsAlreadyDone: LIST[relationTuple], thisRelation: thisRelation, scanHandle: thisGroupScan]]]]] END }; FirstRelship: PUBLIC PROC[r: Relation] RETURNS[relship: Relship] = { CheckRelationKey[r]; BEGIN tuple: DBStorage.TupleHandle = DBStorage.FirstTuple[r.tupleSet]; IF tuple = NIL THEN RETURN[NIL]; RETURN[NEW[RelshipObject _ [relation: r, handle: tuple]]] END }; NextRelship: PUBLIC PROC [rs: RelshipSet] RETURNS [rship: Relship] = TRUSTED { tuple: TupleHandle; relation: Relation; WITH rs: rs SELECT FROM scanForEntity => { DO IF rs.handle.scanHandle = NIL THEN EXIT; tuple _ DBStorage.NextInGroup[rs.handle.scanHandle]; IF tuple # NIL THEN { relation _ rs.handle.thisRelation; EXIT }; DBStorage.CloseScanGroup[rs.handle.scanHandle]; BEGIN relationTuple: TupleHandle; alreadyDone: BOOL; rs.handle.fields _ rs.handle.fields.rest; DO IF rs.handle.fields = NIL THEN EXIT; -- Nothing more to do relationTuple _ DBModelPrivate.GetTupleField[rs.handle.fields.first, relationHandle]; alreadyDone _ FALSE; FOR rL: LIST OF TupleHandle _ rs.handle.relationsAlreadyDone, rL.rest UNTIL rL = NIL DO IF rL.first^ = relationTuple^ THEN {alreadyDone _ TRUE; EXIT} ENDLOOP; IF alreadyDone THEN LOOP ELSE EXIT ENDLOOP; IF rs.handle.fields = NIL THEN { rs.handle.scanHandle _ NIL; EXIT } ELSE { thisRelation: Relation = TupleToRelation[relationTuple, rs.handle.thisRelation.segment]; fieldHandle: DBStorage.FieldHandle = DBModelPrivate.GetFieldField[rs.handle.fields.first, fieldHandleHandle]; thisGroupScan: DBStorage.GroupScanHandle = DBStorage.OpenScanGroup[rs.handle.entityTuple, fieldHandle, First]; rs.handle.scanHandle _ thisGroupScan; rs.handle.fields _ rs.handle.fields.rest; rs.handle.relationsAlreadyDone _ CONS[relationTuple, rs.handle.relationsAlreadyDone]; rs.handle.thisRelation _ thisRelation; LOOP } END ENDLOOP }; onlyOne => { IF rs.tuple = NIL THEN rship _ NIL ELSE { rship _ NEW[RelshipObject _ [relation: rs.relation, handle: rs.tuple]]; rs.tuple _ NIL } }; simpleScan => { tuple: DBStorage.TupleHandle = DBStorage.NextScanIndex[rs.scanHandle]; IF tuple = NIL THEN rship _ NIL ELSE rship _ NEW[RelshipObject _ [relation: rs.relation, handle: tuple]] }; tupleSet => { tuple: DBStorage.TupleHandle = DBStorage.NextScanTupleset[rs.scanHandle]; IF tuple = NIL THEN rship _ NIL ELSE rship _ NEW[RelshipObject _ [relation: rs.relation, handle: tuple]] }; group => { tuple: DBStorage.TupleHandle = DBStorage.NextInGroup[rs.scanHandle]; IF tuple = NIL THEN rship _ NIL ELSE rship _ NEW[RelshipObject _ [relation: rs.relation, handle: tuple]] }; complexScan => { tuple: DBStorage.TupleHandle; DO thisKey: DBCommon.IndexKey = DBStorage.NextScanKey[rs.handle.scanHandle]; IF thisKey = NIL THEN EXIT; DBModelPrivate.DCode[thisKey, rs.handle.thisOne]; IF CheckRanges[rs.handle.low, rs.handle.thisOne, rs.handle.high] THEN { tuple _ DBStorage.ThisScanTuple[rs.handle.scanHandle]; EXIT } ELSE DBStorage.IgnoreEntry[rs.handle.scanHandle] ENDLOOP; IF tuple = NIL THEN rship _ NIL ELSE rship _ NEW[RelshipObject _ [relation: rs.relation, handle: tuple]] } ENDCASE }; CheckRanges: PROC [low, this, high: RopeSequence] RETURNS [inRange: BOOL] ~ { FOR i: CARDINAL IN [0..low.length) DO IF Rope.Compare[low[i], this[i]] = greater OR Rope.Compare[high[i], this[i]] = less THEN RETURN[FALSE] ENDLOOP; RETURN[TRUE] }; RelshipsWithEntityField: PUBLIC PROC[r: Relation, field: CARDINAL, val: Entity] RETURNS[rs: RelshipSet] = { IF NOT r.attributesKnown THEN CacheAttributes[r]; IF field >= r.attributes.count THEN ERROR DB.Error[IllegalAttribute]; IF NOT CheckAttribute[r.attributes[field], Value[entity[val]]] THEN ERROR DB.Error[IllegalAttribute]; DBModelPrivate.CheckForNull[val.tuple]; BEGIN fh: DBStorage.FieldHandle = r.attributes[field].fh; handle: DBStorage.GroupScanHandle = DBStorage.OpenScanGroup[val.tuple, fh, First]; RETURN[NEW[RelshipSetObject _ [relation: r, variant: group[handle]]]] END }; PrevRelship: PUBLIC PROC [rs: RelshipSet] RETURNS [rship: Relship] = TRUSTED { WITH rs: rs SELECT FROM scanForEntity => ERROR DB.Error[NotImplemented]; onlyOne => rship _ NIL; simpleScan => { tuple: DBStorage.TupleHandle = DBStorage.PrevScanIndex[rs.scanHandle]; IF tuple = NIL THEN rship _ NIL ELSE rship _ NEW[RelshipObject _ [relation: rs.relation, handle: tuple]] }; tupleSet => { tuple: DBStorage.TupleHandle = DBStorage.PrevScanTupleset[rs.scanHandle]; IF tuple = NIL THEN rship _ NIL ELSE rship _ NEW[RelshipObject _ [relation: rs.relation, handle: tuple]] }; group => { tuple: DBStorage.TupleHandle = DBStorage.PrevInGroup[rs.scanHandle]; IF tuple = NIL THEN rship _ NIL ELSE rship _ NEW[RelshipObject _ [relation: rs.relation, handle: tuple]] }; complexScan => { tuple: DBStorage.TupleHandle; DO thisKey: DBCommon.IndexKey = DBStorage.PrevScanKey[rs.handle.scanHandle]; IF thisKey = NIL THEN EXIT; DBModelPrivate.DCode[thisKey, rs.handle.thisOne]; IF CheckRanges[rs.handle.low, rs.handle.thisOne, rs.handle.high] THEN { tuple _ DBStorage.ThisScanTuple[rs.handle.scanHandle]; EXIT } ELSE DBStorage.IgnoreEntry[rs.handle.scanHandle] ENDLOOP; IF tuple = NIL THEN rship _ NIL ELSE rship _ NEW[RelshipObject _ [relation: rs.relation, handle: tuple]] } ENDCASE }; ReleaseRelshipSet: PUBLIC PROC [rs: RelshipSet] = TRUSTED { WITH rs: rs SELECT FROM scanForEntity => { DBStorage.CloseScanGroup[rs.handle.scanHandle] }; onlyOne => NULL; simpleScan => { DBStorage.CloseScanIndex[rs.scanHandle] }; tupleSet => { DBStorage.CloseScanTupleset[rs.scanHandle] }; group => { DBStorage.CloseScanGroup[rs.scanHandle] }; complexScan => { DBStorage.CloseScanIndex[rs.handle.scanHandle] } ENDCASE }; RelationsOf: PUBLIC PROC [d: Domain] RETURNS[rs: RelationSet] = { DBModelPrivate.CheckDomainKey[d]; BEGIN attributeEnumeration: DBStorage.GroupScanHandle = DBStorage.OpenScanGroup[d.tupleSet, domainHandle, First]; rs _ NEW[RelationSetObject _ [segment: d.segment, variant: relationsForDomain[scanHandle: attributeEnumeration, lastTuple: NIL]]] END }; DestroyRelation: PUBLIC PROC [r: Relation] = { sh: SegmentHandle = DBModelPrivate.SegmentToHandle[r.segment]; relationIndex: DBStorage.IndexHandle = sh.indices[DBCommon.relationIndex]; IF NOT r.indicesKnown THEN DBModelPrivate.CacheIndices[r]; IF NOT r.attributesKnown THEN CacheAttributes[r]; FOR iL: LIST OF IndexHandle _ r.indices, iL.rest UNTIL iL = NIL DO DBModelPrivate.DestroyIndexTuple[iL.first.indexHandle] ENDLOOP; r.indices _ NIL; FOR iL: LIST OF IndexHandle _ r.keys, iL.rest UNTIL iL = NIL DO DBModelPrivate.DestroyIndexTuple[iL.first.indexHandle] ENDLOOP; r.keys _ NIL; IF r.isSurrogate THEN { relationSet: DBStorage.GroupScanHandle = DBStorage.OpenScanGroup[r.surrogateDomain.tupleSet, r.surrogateGroupHandle, First]; FOR nextTuple: DBStorage.TupleHandle _ DBStorage.NextInGroup[relationSet], DBStorage.NextInGroup[relationSet] UNTIL nextTuple = NIL DO DestroyRelshipTuple[nextTuple, r]; ENDLOOP; DBStorage.CloseScanGroup[relationSet] } ELSE { relationSet: DBStorage.TuplesetScanHandle = DBStorage.OpenScanTupleset[r.tupleSet, First]; FOR nextTuple: DBStorage.TupleHandle _ DBStorage.NextScanTupleset[relationSet], DBStorage.NextScanTupleset[relationSet] UNTIL nextTuple = NIL DO DestroyRelshipTuple[nextTuple, r]; ENDLOOP; DBStorage.CloseScanTupleset[relationSet] }; BEGIN attributeSet: DBStorage.GroupScanHandle = DBStorage.OpenScanGroup[r.tupleSet, relationHandle, First]; attrList: LIST OF DBStorage.TupleHandle; FOR nextTuple: DBStorage.TupleHandle _ DBStorage.NextInGroup[attributeSet], DBStorage.NextInGroup[attributeSet] UNTIL nextTuple = NIL DO attrList _ CONS[nextTuple, attrList]; ENDLOOP; DBStorage.CloseScanGroup[attributeSet]; FOR tl: LIST OF DBStorage.TupleHandle _ attrList, tl.rest UNTIL tl = NIL DO DBStorage.WriteTIDNil[tl.first, domainHandle]; DBStorage.WriteTIDNil[tl.first, relationHandle]; DBModelPrivate.SetRopeField[tl.first, aNameHandle, NIL]; DBStorage.DestroyTuple[tl.first] ENDLOOP; DBStorage.WriteTIDNil[r.tupleSet, surrogateDomainHandle]; END; DBStorage.DeleteFromIndex[relationIndex, r.name, r.tupleSet]; DBStorage.DestroyTuple[r.tupleSet]; RemoveRelationFromCache[r] }; RemoveRelationFromCache: PROC[r: Relation] = { sh: SegmentHandle = DBModelPrivate.SegmentToHandle[r.segment]; IF r.attributesKnown THEN { FOR i: CARDINAL IN [0..r.attributes.count) DO [] _ CardTable.Delete[sh.attributeTable, r.attributes[i].tuple]; r.attributes[i].domain _ NIL; r.attributes[i].indices _ NIL ENDLOOP }; IF r.surrogateDomain # NIL THEN { r.surrogateDomain.surrogatesKnown _ FALSE; r.surrogateDomain.surrogates _ NIL }; r.surrogateDomain _ NIL; r.key _ 0; [] _ SymTab.Delete[x: sh.relationTable, key: r.name] }; DestroyStoredRelations: PUBLIC PROC[d: Domain] = { relationList: LIST OF Relation; attributeScan: DBStorage.GroupScanHandle = DBStorage.OpenScanGroup[d.tupleSet, domainHandle, First]; FOR attr: TupleHandle _ DBStorage.NextInGroup[attributeScan], DBStorage.NextInGroup[attributeScan] UNTIL attr = NIL DO relationTuple: TupleHandle = DBModelPrivate.GetTupleField[attr, relationHandle]; IF GetCardField[relationTuple, propertyHandle] = ORD[RelationType[surrogate]] THEN LOOP; BEGIN relation: Relation = TupleToRelation[relationTuple, d.segment]; FOR rL: LIST OF Relation _ relationList, rL.rest UNTIL rL = NIL DO IF relation = rL.first THEN EXIT REPEAT FINISHED => relationList _ CONS[relation, relationList] ENDLOOP END ENDLOOP; DBStorage.CloseScanGroup[attributeScan]; FOR rL: LIST OF Relation _ relationList, rL.rest UNTIL rL = NIL DO DestroyRelation[rL.first]; ENDLOOP; }; DestroySurrogates: PUBLIC PROC[d: Domain] = { IF NOT d.surrogatesKnown THEN EnumerateSurrogates[d]; FOR rl: RelationList _ d.surrogates, rl.next UNTIL rl = NIL DO DestroyRelation[rl.relation] ENDLOOP }; EnumerateSurrogates: PROC [d: Domain] ~ { surrogateScanGroup: DBStorage.GroupScanHandle = DBStorage.OpenScanGroup[d.tupleSet, surrogateDomainHandle, First]; FOR nextTuple: TupleHandle _ DBStorage.NextInGroup[surrogateScanGroup], DBStorage.NextInGroup[surrogateScanGroup] UNTIL nextTuple = NIL DO d.surrogates _ NEW[RelationListObject _ [relation: TupleToRelation[nextTuple, d.segment], next: d.surrogates]] ENDLOOP; DBStorage.CloseScanGroup[surrogateScanGroup]; d.surrogatesKnown _ TRUE }; Unlink: PUBLIC PROC[t: TupleHandle, d: Domain] = { sh: SegmentHandle = DBModelPrivate.SegmentToHandle[d.segment]; IF NOT d.surrogatesKnown THEN EnumerateSurrogates[d]; BEGIN groups: LIST OF DBStorage.TupleHandle = DBStorage.GetGroupIDs[t]; FOR aL: LIST OF DBStorage.TupleHandle _ groups, aL.rest UNTIL aL = NIL DO attrTuple: DBStorage.TupleHandle = aL.first; attr: Attribute; attr _ NARROW[CardTable.Fetch[sh.attributeTable, attrTuple.tid].val]; IF attr = NIL THEN { relationTuple: DBStorage.TupleHandle = DBModelPrivate.GetTupleField[attrTuple, relationHandle]; pos: CARDINAL = DBModelPrivate.GetCardField[attrTuple, posHandle]; relation: Relation = TupleToRelation[relationTuple, d.segment]; attr _ NEW[AttributeHandle _ [pos: pos, relation: relation]]; [] _ CardTable.Insert[x: sh.attributeTable, key: attrTuple.tid, val: attr] }; IF NOT attr.relation.attributesKnown THEN CacheAttributes[attr.relation]; BEGIN fh: DBStorage.FieldHandle = attr.relation.attributes[attr.pos].fh; tupleScan: DBStorage.GroupScanHandle = DBStorage.OpenScanGroup[t, fh, First]; FOR nextTuple: DBStorage.TupleHandle _ DBStorage.NextInGroup[tupleScan], DBStorage.NextInGroup[tupleScan] UNTIL nextTuple = NIL DO DestroyRelshipTuple[nextTuple, attr.relation] ENDLOOP; DBStorage.CloseScanGroup[tupleScan]; END ENDLOOP; END; FOR rL: RelationList _ d.surrogates, rL.next UNTIL rL = NIL DO relation: Relation = rL.relation; IF NOT relation.attributesKnown THEN CacheAttributes[relation]; DestroyRelshipTuple[t, relation]; DBStorage.WriteTIDNil[t, relation.surrogateGroupHandle] ENDLOOP }; AttributeTupleSet: DBStorage.SystemTuplesetHandle; AttributeDomain: DBCommon.TupleHandle; relationGroup: DBStorage.FieldDescriptor _ [Group[]]; attributeGroup: DBStorage.FieldDescriptor _ [Group[]]; InitializeRelationSchema: PUBLIC PROC[] = TRUSTED { [AttributeTupleSet, AttributeDomain] _ DBModelPrivate.SetSystemTable[DBModelPrivate.AttributeTSID]; relationGroup _ DBStorage.FieldDescriptor[Group[groupID: DBModelPrivate.RelationDomain]]; attributeGroup _ DBStorage.FieldDescriptor[Group[groupID: AttributeDomain]]; [] _ DBStorage.AddSystemField[DBModelPrivate.RelationTupleSet, DBModelPrivate.TupleSetDescriptor]; rNameHandle _ DBStorage.AddSystemField[DBModelPrivate.RelationTupleSet, DBModelPrivate.NameDescriptor]; countHandle _ DBStorage.AddSystemField[DBModelPrivate.RelationTupleSet, DBModelPrivate.OneWordDescriptor]; propertyHandle _ DBStorage.AddSystemField[DBModelPrivate.RelationTupleSet, DBModelPrivate.OneWordDescriptor]; surrogateHandleHandle _ DBStorage.AddSystemField[DBModelPrivate.RelationTupleSet, DBModelPrivate.HandleDescriptor]; surrogateDomainHandle _ DBStorage.AddSystemField[DBModelPrivate.RelationTupleSet, [Group[DBModelPrivate.DomainDomain]]]; aNameHandle _ DBStorage.AddSystemField[AttributeTupleSet, DBModelPrivate.NameDescriptor]; typeHandle _ DBStorage.AddSystemField[AttributeTupleSet, DBModelPrivate.TypeDescriptor]; domainHandle _ DBStorage.AddSystemField[AttributeTupleSet, attributeGroup]; relationHandle _ DBStorage.AddSystemField[AttributeTupleSet, attributeGroup]; posHandle _ DBStorage.AddSystemField[AttributeTupleSet, DBModelPrivate.OneWordDescriptor]; fieldHandleHandle_ DBStorage.AddSystemField[AttributeTupleSet, DBModelPrivate.HandleDescriptor] }; rNameHandle, countHandle, propertyHandle, surrogateDomainHandle, surrogateHandleHandle: DBStorage.FieldHandle; aNameHandle, typeHandle, domainHandle, relationHandle, posHandle, fieldHandleHandle: DBStorage.FieldHandle; SetCardField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle, val: CARDINAL] = { DBStorage.Write1Word[t, f, val] }; GetCardField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle] RETURNS[val: CARDINAL] = { val _ DBStorage.Read1Word[t, f] }; SetBoolField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle, val: BOOL] = { DBStorage.Write1Word[t, f, IF val THEN 0 ELSE 1] }; GetBoolField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle] RETURNS[val: BOOL] = { val _ DBStorage.Read1Word[t, f] = 0 }; SetTypeField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle, val: TypeCode] = { DBStorage.Write2Word[t, f, val.code] }; GetTypeField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle] RETURNS[val: TypeCode] = { RETURN[TypeCode[DBStorage.Read2Word[t, f]]] }; SetTimeField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle, val: BasicTime.GMT] = { DBStorage.Write2Word[t, f, LOOPHOLE[val]] }; GetTimeField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle] RETURNS[val: BasicTime.GMT] = { val _ LOOPHOLE[DBStorage.Read2Word[t, f]] }; SetIntField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle, val: INT] = { DBStorage.Write2Word[t, f, LOOPHOLE[val]] }; GetIntField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle] RETURNS[val: INT] = { val _ LOOPHOLE[DBStorage.Read2Word[t, f]] }; SetFieldField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle, val: DBStorage.FieldHandle] = { DBStorage.WriteNWord[t, f, val] }; GetFieldField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle] RETURNS[val: DBStorage.FieldHandle] = { val _ DBStorage.CreateFieldHandle[]; DBStorage.ReadNWord[t, f, val] }; END. ͺFile: DBModelRelationImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Contents: Implementation of DBModel, DBModelPrivate operations on relations. Last edited by: Donahue, August 20, 1986 8:32:35 am PDT 0. Constants used to encode surrogate/property information 1. Relationship creation/destruction/field access Check to see that all of the types match All the types match, now check the keys If this is a property, check to see that the tuple doesn't already exist in the relation Create the tuple and set the fields If it's a surrogate, then it needs to be added to the surrogate group for the relation Add all of the key fields for the tuple Finally, add any other index entries that should be created for the tuple Check that the value sequence specified is type-consistent with the field specification given in the Index Check that the value is type-consistent with the attribute specification Ensure that there currently is no entry in the index i for the tuple t with field f having value v Now, we have the key; check it in the index Check to see that the types of the value sequence and the key match Check the index to see that the tuple exists: note that because we are only searching keys, at most one tuple will be found Nullify all of the TID and Rope fields; if the relation is not a surrogate, then also destroy the tuple; we assume that the attribute information is cached at this point Remove the tuple from any indices that it may participate in This procedure does all of the key checking and index maintenance necessary to ensure the validity of the database. It assumes that the value and field arguments have already been tested for reasonableness Determine if a key must be maintained If this is a property and you're changing the first field, check to see that the tuple doesn't already exist in the relation Check the key indices for the relation Remove the tuple from all of the indices that are affected Have to do case split on whether this is first field of surrogate First have to split the tuple, copying all of the fields from the current relationship (need to check to see whether tuple already exists in the relation) The tuple that is added to the indices for the relation is the new one that was just created Copy all of the other fields Finally add all the index entries for the modified tuple 3. Relation creation/destruction/manipulation Creates a relation of this name or fetches it if it already exists. Turn the attribute descriptions into attributes for the relation tuple t The relation will be a surrogate if the first attribute is a key and is domain-valued and if the domain is currently empty (this final condition is necessary because of inadequacies in the Storage level currently) If the field that is being set up is entity-valued, then this will be set by SetFieldHandle This scan group is used to link together the attributes in a group for the relation If the descriptor is of some domain type (including AnyDomainType), then the groupID used is the attribute tuple itself; this allows us to do the mapping from groupIDs to field handles needed to do the Unlink operation when entities are destroyed This is the first attribute. It will be stored as a surrogate only if the attribute is a key AND the type is of some domain type AND the domain is currently empty Here we can use the surrogate optimization. We record the fact that the surrogate optimization is used and add a field to the tupleset (the domain) that will contain the group handle for the relation (which will allow us to enumerate all of the relationships in the relation) Add it to the group for the relation Check that all the attributes match (they must be given in precisely the same order) If the relation is not found, first ensure that the relation can legally be a property if it has been declared as one Create a new relation. See if you can find it in the index for the RelationDomain It's in the database, but not in the cache. Cache it; none of the details of the entry are computed yet Record it in the cache Enumerate all of the attributes of the relation tuple If the relation is a surrogate, cache the surrogate data noDecodeNeeded is TRUE iff either the constraint was single-valued (thus the search of the index will generate only tuples that have this value) or the constraint covers the entire range of values (in which case any tuple in the enumeration matching the key will have a value in range for this field) Scan the entire index If each of the constraints (except the last) is either single-valued or covers the entire range of values, then the enumeration of the index will produce only values satisfying the constaints; in this case no testing of the keys in the index to determine whether the tuple satisfies the constraints is necessary Each component of the sequence must be in the range low[i] <= this[i] <= high[i] Destroy all indices on the relation Enumerate each tuple and destroy the tuple (DestroyRelshipTuple) Destroy all of the schema information Destroy the relation tuple itself If the attribute was cached in the attribute table, then throw that entry away Remove the circularity in the list structures If this relation was a surrogate, then flush all of the cached surrogate information for its domain Two steps in this process: 1. Interrogate the database and destroy all of the relations that have attributes that refer to the domain (enumerate the attribute tupleset to find them) 2. Throw away any cache entries for relations that have been destroyed as a result (Note that the structure of the cache ensures that this can be done without further database interrogation) Check to see whether the relation already exists in the list Enumerate all of the surrogate for the domain DestroyRelation on each of them Each of these tuple ids is an attribute in the Attribute domain; we first insert each into the attributeTable if not already there Open the scan group and destroy all of the tuples that refer to this one All that is left to do is to destroy all of the surrogates that may be stored with the tuple 4. Definition of the structure of Relations in the Database The structure of the storage of the relations, attributes and index factors component of a Cypress database is reasonably complicated and takes advantage of the ability to define many groups at the storage level. A relation tuple contains the name of the relation, the number of attributes in the relation, whether the relation is a surrogate relation, and (if it is a surrogate) a field handle to be used for enumerating all of the tuples in the relation (which will be a subset of all of the tuples in the domain defined by the first attribute). A relation tuple is also the head of groups that enumerate all of the attributes of the relation and all of the indices of the relation. An attribute tuple contains the name of the attribute, the type code for the attribute, a link to the domain defining the type (if the attribute is not of one of the basic types), a link to the relation to which the attribute belongs, the cardinal position of the attribute, and the field handle to be used when retrieving or setting this field of a relation. Note that the attribute information does not depend on whether the attribute is part of a surrogate relation or not. The RelationDomain tuples are tuplesets with the following additional fields: 1. the name of the relation 2. the number of fields in the relation 3. the type of the relation (whether normal, property, or surrogate), 4. if it is a surrogate, the field handle to be used when enumerating the tuples in the relation, 5. if it is a surrogate, a link to the domain of which it is a surrogate The AttributeDomain tuples have the following fields: 1. the name of the attribute, 2. the typecode of the attribute, 3. the domain corresponding to the typecode (if a domain type), 4. the relation to which the attribute belongs, 5. the relative position of the attribute in the tuple, and 6. the field handle for the attribute Handles for the relation domain Handles for the attribute domain Each of the procedures below assume that the field handle is a OneWord handle Type codes are stored as two words Integers and times are also stored as two words These procedures manipulate field handles stored in the database Κ06˜šœ™Jšœ Οmœ1™<—JšœL™Lšœ™Icode™'—J˜šΟk ˜ Jšœ žœžœ#˜6J˜ Jšœ ˜ J˜ Jšœžœ˜$J˜Jšžœ˜J˜J˜J˜Jšœ˜J˜—šœžœž˜"Jšžœc˜jšžœ˜"J˜——Jšžœžœ˜head1™;Jšœžœ!˜3—™2šΟn œžœžœ#žœ˜TJšœ žœžœ žœ˜>Jšœ žœžœ ˜J˜Jšžœžœžœ ˜:Jšžœžœžœ˜1Jšœ(™(Jšžœ!žœžœžœ˜Lšžœžœžœž˜%Jš žœžœ5žœžœžœ%˜mJšžœ˜—šœ'™'J™Xšžœžœžœ˜Jšœžœ ˜)šžœ4žœžœž˜GJšžœžœ˜$———š žœžœžœ%žœžœž˜CJšœ0˜0Jšœžœ˜šžœžœžœž˜*JšœH˜HJšžœ˜—Jšœ žœ+˜9Jšžœ˜—š žœžœžœžœžœž˜;Jšžœ=žœžœžœ˜fJšžœ˜—Jšœ#™#šž ˜ šœžœ˜(Jšžœžœ#˜0Jšžœ#˜'—Jšœžœ2˜9š žœžœžœžœžœžœž˜LJšœ/˜/Jšžœ˜—JšœV™Všžœž˜KšœJ˜J—Jšžœ˜—Jšœ'™'š žœžœžœžœžœž˜;JšœL˜LJšžœ˜—JšœI™Iš žœžœžœ!žœžœž˜?J˜4Jšžœ˜ ——J˜šŸ œžœ:žœ žœ˜cKšœj™jKšœžœ˜ Kšžœžœ žœžœ˜;šžœžœžœž˜'Kšœžœ0˜AKšž˜—K˜—K˜š Ÿœžœžœžœ žœ˜VKšœH™Hšžœžœž˜šœžœžœ ž˜AJšžœC˜G—Jšœžœ˜Jšžœ žœ˜4——J˜šŸ œžœžœ,žœžœžœ žœ˜lKšžœžœžœžœ˜C—J˜š Ÿœžœ:žœ žœžœžœ˜‚Kšœžœ˜Kšœb™bšžœžœžœž˜(Kšœžœžœžœ ˜PKšœ?˜?Kšžœ˜—Kšœ+™+Kšœžœ.˜7K˜—J˜šŸ œžœžœ.žœ˜_Jšœžœ˜J˜Jšžœžœžœ˜1Jšžœžœžœ ˜:šžœžœ/žœ˜:Jšžœžœžœ˜"—JšœC™CJš žœžœ žœžœžœ%˜XJšœ{™{šžœžœžœž˜$K˜šž˜J˜J˜Jšœ&˜&Jšžœžœžœ˜1Jš žœžœžœžœžœžœ˜NJš žœžœ(žœžœžœ%˜`Jšœ$˜$Jšžœ˜——J˜šŸ œžœ)žœ˜UKšœΞ™ΞJšœ žœ'˜5J˜Jšžœžœžœ ˜:šœ%™%J™|šžœžœ žœžœ˜,Jšœžœ˜%šžœ4žœžœž˜GJšžœžœ˜#——šžœ˜Jšœ&™&š žœžœžœ)žœžœž˜GJ˜!Jšžœžœžœ˜6Jšžœ˜—Jšžœ žœžœžœ1žœžœžœ˜s——Jšœ:™:š žœžœžœ)žœžœž˜GJšœ6˜6Jšžœ˜—JšœA™AJšžœžœžœ žœ#˜Išžœžœ˜Jšœš™šJšœ]™]šžœ žœž˜šœ ˜ JšœSΟc˜jJšœA ˜XJšœ™šžœžœžœž˜-JšœJ˜JJšžœ˜—Jšœ˜—Jšžœžœ˜!—Jšœ˜—Jšœ8™8š žœžœžœ)žœžœž˜GJ˜!Jšœ3˜3Jšžœ˜ ——K˜šŸœžœ;žœ˜Ršžœ žœž˜Jšœ9˜9Jšœ8˜8Jšœ6˜6Jšœ6˜6Jšœ?˜?J˜0Jšžœžœ˜!—K˜—J˜š Ÿ œžœ&žœžœžœ˜lšžœžœ žœ˜%J˜(šž˜Jšœžœk˜Jšžœ˜$Jšžœ˜——šž˜Jšœžœ'˜0šžœžœ˜'šžœ ž˜JšœQ˜QJšœ&žœ+˜YJšœH˜HJšœN˜NJšžœžœ˜——šžœ˜J˜GJšžœžœ ˜/šžœž˜ Jšœžœžœ žœH˜‹Jšœ žœ?˜NJšœžœ/˜EJšžœ˜——Jšž˜—K˜—J˜š Ÿœžœžœžœžœžœ˜XJšžœžœžœ žœžœžœžœ˜>šž˜J˜J˜J˜&Jšžœžœžœ˜1Jšžœžœžœžœ˜DJšžœ&˜*Jšžœ˜——J˜šŸœžœžœ žœ˜HJšœ˜J˜%Kšžœžœžœ˜1šžœžœžœ'˜LJšžœžœžœ˜&—šž˜Jšœ3˜3Jš žœ žœžœžœžœ˜-J˜!Jšž˜—Jšœ˜—J˜š Ÿœžœžœ žœžœ˜[Jšœ˜J˜%Kšžœžœžœ˜1Jšžœžœžœžœ˜Ešž ˜ Jšœ3˜3Jš žœ žœžœžœžœ˜-Jšœ"˜"Jšž˜—Jšœ˜——™.šŸ œžœžœžœžœ žœžœ˜ZJ˜Jšžœžœžœ˜1šžœžœž˜%Jšžœ*žœ žœžœ˜HJšžœ˜—Jšœ ž˜Jšœ˜—J˜š Ÿœžœžœžœžœžœ ˜VJ˜Jšžœžœžœ˜1Jšžœžœžœ˜Jšœžœ˜Jšœ˜JšœH˜HJšœd˜dJšœK˜KJšœ˜—Jšœ˜——J˜Jšœ+˜+Jšœžœ"˜4šžœžœžœž˜'Jšœžœ˜Jšœ-˜-Jšœ žœžœ3˜UJ˜fJšœ0˜0Jšœ4˜4Jšœ;˜;Jšœ$™$Jšœ)˜)Jšœ)˜)Jšœ˜š žœžœžœžœžœ˜CJšœB˜BJšœ˜—Jšœ˜Jšžœ˜—Jšœžœ˜Jšœ+˜+—J˜Jšœ"˜"šžœžœžœ˜Jšžœžœžœ˜1JšœT™TJšžœ#žœžœžœ#˜Ušžœžœžœž˜'Jš žœžœ2žœ1žœžœžœ#˜›Jšžœ˜—Jšžœžœžœžœ#˜MJšžœ˜ —J˜Jšœu™uJš žœ žœ(žœ+žœžœžœ˜‹J˜Jšœ™šž˜JšœXžœ˜iJ˜Jšœ-˜-Jšœ2˜2J˜Jšœ˜Jšœ˜Jšœžœ˜Jšžœ˜ Jšžœ˜——J˜š Ÿœžœžœ žœžœ˜XJšœ<˜˜>Jšœžœ+˜CJšœžœ˜šž˜Jšœg˜gJšœžœ)˜AJšœ!žœ&˜JJšœžœ˜Jšœ5™5šžœ_žœžœž˜rJšœžœžœ˜@Jšœ5˜5Jšœ"˜"JšœG˜GJšœF˜Fšž˜JšœD˜Dšžœ žœžœ˜JšœM˜MJšžœžœ)žœ8˜k—Jšžœ˜—JšœL˜LJ˜"Jšžœ˜—Jšœ˜Jšœ(˜(Jšžœ˜—Jšœ8™8Jšœ!˜!J˜šžœžœ˜JšœG˜GJšœJ˜JJšœ,˜,Jšœžœ˜JšœM˜M——J˜šŸœžœžœ˜.Jšœ>˜>Jšžœžœžœžœ˜9—J˜š Ÿ œžœžœžœžœžœ˜iJšœ˜Jšœ ˜ šž˜Jšœžœ.˜7Jšœ˜Jšœ˜Jšžœ˜——J˜šŸ œž œžœžœ˜=Jš žœžœžœžœžœ˜"Jš žœžœžœžœžœ˜Jšœ+˜+Jšžœ˜—J˜š Ÿ œžœžœžœžœ˜?Jš žœžœžœžœžœ˜"Jš žœžœžœžœžœ˜Jšžœžœ)˜C—J˜šŸ œž œžœžœ˜=Jš žœžœžœžœžœ˜šž˜Jšœ>˜>Jšžœ˜Jšžœ˜——J˜š Ÿ œžœžœžœžœ˜?Jšžœžœžœ˜.—J˜š Ÿ œžœžœ žœžœ˜;Jš žœžœžœžœžœ˜Jšžœžœžœžœ˜3šž˜J˜ JšœE˜EJšžœ˜Jšžœ˜——J˜š Ÿ œžœžœžœžœ˜GJšžœžœžœ˜0Jšžœžœžœ˜0Jšžœ&žœ˜NJšœ˜—J˜šŸœžœžœGžœ˜~J˜šŸœžœ˜Jšžœ'žœžœžœ˜Pšžœžœžœž˜+JšœA˜AJšœ0˜0Jšžœžœžœžœ˜Dšžœ ž˜&Jš žœžœ.žœžœžœ˜Z—šžœžœ#žœ˜0šžœžœž˜šœ˜Jš žœžœCžœžœžœ˜p—Jšžœ˜ ——Jšžœ˜ ——J˜šŸœžœ#žœžœžœžœžœ˜}Jšœ­™­šžœžœž˜˜ J˜/Jšœžœ˜—˜ Jšœ%˜%Jšœ'˜'Jš œ"žœ žœžœžœ žœžœ˜S—˜ Jš œžœžœ žœžœ˜OJš œžœžœ žœžœžœžœ!˜TJšœžœžœ žœžœžœžœžœ žœžœžœ˜wJ˜—˜ Jšžœžœ˜@Jšžœžœ˜@Jšœ"žœ žœ˜fJšœ&˜&Jšœ*˜*—˜ J˜ J˜&J˜6Jšœžœ˜——Jšžœžœ˜—J˜J˜Jšžœžœžœ˜1Jšžœžœžœ ˜:šžœ žœžœ ˜1šžœžœ˜Jšœk˜kJšžœžœA˜K—šžœ˜JšœY˜YJšžœžœG˜Q——Jš žœžœžœžœžœ˜Ešžœžœžœ˜Jšœ™Jš œmžœžœžœžœžœžœ ˜ρJšžœžœD˜NJ˜—Jšœž˜šžœžœ ˜2Jšœžœ˜Jšœ&˜&JšœKžœ˜QJš œ|žœžœžœžœ ˜βJšžœžœF˜P—šžœ ˜&Jšœžœ'˜>Jšœžœ'˜?Jšœ&žœ˜+Jšœ&˜&Jšœžœžœ˜J™·šžœžœžœž˜+Jšœžœ˜Jšœržœ˜yJšœ5˜5Jšœ8˜8Jšžœžœžœ˜?Jšžœ˜—Jšœ’žœžœ ˜Ήšžœž˜ JšžœžœD˜N—šž˜Jšžœžœ?žœMžœ,˜Θ—J˜—Jšœ˜—J˜šŸœžœžœ žœ˜GJ˜(J˜%šž˜Jšœžœžœ.˜DJšœ_˜_JšœJ˜JJšœj˜jJšœ`˜`Jš œžœžœ!žœ]žœK˜ϊJšžœ˜——J˜šŸ œžœžœžœ˜DJ˜šž˜Jšœ@˜@Jš žœ žœžœžœžœ˜ Jšžœžœ/˜9Jšžœ˜——J˜š Ÿ œžœžœžœžœ˜NJ˜J˜šžœžœž˜šœ˜šž˜Jšžœžœžœžœ˜(Jšœ4˜4Jšžœ žœžœ&žœ˜@Jšœ/˜/šž˜J˜Jšœ žœ˜Jšœ)˜)šž˜Jš žœžœžœžœ ˜:JšœU˜UJšœžœ˜š žœžœžœ7žœžœž˜WJšžœžœžœžœ˜=Jšžœ˜—Jš žœ žœžœžœž˜"Jšžœ˜—Jš žœžœžœžœžœ˜Cšžœ˜JšœX˜XJšœm˜mJšœn˜nJšœ%˜%Jšœ)˜)Jšœ!žœ0˜UJšœ&˜&Jšžœ˜—Jšž˜—Jšžœ˜ —šœ ˜ Jšžœ žœžœ ž˜"šžœ˜Jšœžœ<˜GJšœ žœ˜——šœ˜JšœF˜FJšžœ žœžœ ž˜Jšžœ žœ;˜K—šœ ˜ JšœI˜IJšžœ žœžœ ž˜Jšžœ žœ;˜K—šœ ˜ JšœD˜DJšžœ žœžœ ž˜Jšžœ žœ;˜K—šœ˜Jšœ˜šž˜JšœI˜IJšžœ žœžœžœ˜Jšœ1˜1šžœ>˜@Jšžœ:žœ˜D—Jšžœ,˜0Jšžœ˜—Jšžœ žœžœ ž˜Jšžœ žœ:˜J———Jšžœ˜ —J˜šŸ œžœ!žœ žœ˜Mšžœžœžœž˜%KšœP™Pšžœ)žœ&˜SKšžœžœžœ˜—Kšž˜—Kšžœžœ˜ K˜—J˜š Ÿœžœžœžœžœ˜kJšžœžœžœ˜1Jšžœžœžœžœ˜EJš žœžœ9žœžœžœ˜eJ˜'šž˜Jšœ3˜3JšœR˜RJšžœžœ;˜EJšžœ˜——J˜š Ÿ œžœžœžœžœ˜Nšžœžœž˜Jšœžœžœ˜0Jšœžœ˜šœ˜JšœF˜FJšžœ žœžœ ž˜Jšžœ žœ;˜K—šœ ˜ JšœI˜IJšžœ žœžœ ž˜Jšžœ žœ;˜K—šœ ˜ JšœD˜DJšžœ žœžœ ž˜Jšžœ žœ;˜K—šœ˜Jšœ˜šž˜JšœI˜IJšžœ žœžœžœ˜Jšœ1˜1šžœ>˜@Jšžœ:žœ˜D—Jšžœ,˜0Jšžœ˜—Jšžœ žœžœ ž˜Jšžœ žœ:˜J——Jšžœ˜ —J˜šŸœžœžœžœ˜;šžœžœž˜JšœD˜DJšœ žœ˜Jšœ:˜:Jšœ;˜;Jšœ5˜5JšœA˜AJšž˜—Jšœ˜—J˜šŸ œžœžœ žœ˜AJ˜!šž˜Jšœk˜kJšœžœsžœ˜Jšž˜—Jšœ˜—J˜šŸœžœžœ˜.J˜>JšœJ˜JJšœ#™#Jšžœžœžœ ˜:Jšžœžœžœ˜1š žœžœžœ"žœžœž˜BJšœ7˜7Jšžœ˜—Jšœ žœ˜š žœžœžœžœžœž˜?Jšœ7˜7Jšžœ˜—Jšœ žœ˜ Jšœ@™@šžœžœ˜Jšœ|˜|šžœkžœ žœž˜†J˜"Jšž˜—Jšœ'˜'—šžœ˜JšœZ˜Zšžœužœ žœž˜J˜"Jšžœ˜—Jšœ(˜(J˜—Jšœ%™%šž˜Jšœe˜eJšœ žœžœ˜(šžœmžœ žœž˜ˆJšœ žœ˜%Jšžœ˜—Jšœ'˜'š žœžœžœ+žœžœž˜KJšœ.˜.Jšœ0˜0Jšœ3žœ˜8J˜ Jšžœ˜—J˜9Jšžœ˜—Jšœ!™!Jšœ=˜=Jšœ#˜#J˜Jšœ˜—J˜šŸœžœ˜.Jšœ>˜>šžœžœ˜šžœžœžœž˜-JšœN™NJšœ@˜@Jšœ-™-Jšœžœ˜Jšœž˜Jšž˜—J˜—Jšœc™cšžœžœžœ˜!Jšœ$žœ˜*Jšœžœ˜%—Jšœžœ˜J˜ J˜4Jšœ˜—J˜šŸœžœžœ˜2Jšœ™J™›J™ΏJšœžœžœ ˜Jšœd˜dšžœ`žœžœž˜vJšœP˜Pšžœ/žœ˜MJšžœžœ˜ —šž˜Jšœ?˜?Jšœ<™<š žœžœžœ"žœžœž˜BJšžœžœž˜ šž˜Jšžœžœ˜7—Jšž˜—Jšž˜—Jšžœ˜—Jšœ(˜(š žœžœžœ"žœžœž˜BJ˜Jšžœ˜—J˜—J˜šŸœžœžœ˜-Jšœ-™-Jšžœžœžœ˜5Jšœ™šžœ*žœžœž˜>J˜Jšž˜—Jšœ˜—J˜šŸœžœ˜)Jšœr˜ršžœožœ žœž˜ŠJšœžœ\˜nJšžœ˜—Jšœ-˜-Jšœžœ˜—J˜šŸœžœžœ˜2J˜>Jšžœžœžœ˜5šž˜Jšœžœžœ2˜AJšœ‚™‚š žœžœžœ)žœžœž˜IJ˜,J˜Jšœžœ8˜Ešžœžœžœ˜J˜_Jšœžœ5˜BJšœ?˜?Jšœžœ3˜=JšœM˜M—Jšžœžœžœ ˜IJšœH™Hšž˜J˜BJ˜Mšžœgžœ žœž˜‚J˜-Jšžœ˜—J˜$Jšž˜—Jšžœ˜—Jšž˜—Jšœ\™\šžœ*žœžœž˜>J˜!Jšžœžœžœ˜?J˜!Jšœ7˜7Jšž˜—Jšœ˜——šœ;™;JšœΤ™ΤJ˜JšœΨ™ΨJ˜Jšœέ™έJ˜Jšœ2˜2J˜Jšœ&˜&J˜J˜5J˜J˜6J˜šŸœžœžœžœ˜3Jšœc˜cJšœY˜YJšœL˜L™MJ™J™(J™FJ™bJ™I—Jšœb˜bJšœg˜gJšœj˜jJšœm˜mJšœs˜sJšœx˜x™5J™J™"J™@J™0J™