<> <> <> <> <> 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; <<0. Constants used to encode surrogate/property information>> RelationType: TYPE = {normal, property, surrogate}; <<1. Relationship creation/destruction/field access>> 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 }; <<3. Relation creation/destruction/manipulation>> 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] = { <> <<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)>> 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 }; <<4. Definition of the structure of Relations in the Database>> <> <> <> 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]]; <> <<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>> [] _ 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]]]; <> <<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>> 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.