File: DBModelDomainImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Contents: Implementation of global operations for Model level
Last edited by:
Eric Bier on August 6, 1981 17:10:15
Rick Cattell on January 4, 1984 10:36 am
Willie-Sue on February 22, 1985 9:21:53 am PST
Donahue, August 7, 1986 12:00:34 pm PDT
Widom, September 9, 1985 5:06:22 pm PDT
DIRECTORY
CardTable,
DBCommon,
DBStorage,
DBDefs,
DB,
DBModel,
DBModelPrivate,
Rope,
SymTab;
DBModelDomainImpl: CEDAR PROGRAM
IMPORTS CardTable, DBStorage, DB, DBModelPrivate, Rope, SymTab
EXPORTS DBModel, DBModelPrivate
SHARES Rope =
BEGIN OPEN DBCommon, DBDefs;
Definition of the structure of Domains in the Database
STSVecType: TYPE = ARRAY[1..DBStorage.MaxSystemTupleID] OF TupleHandle;
systemTupleVec: REF STSVecType ← NEW[STSVecType ← ALL[NIL]];
DomainTupleSet: PUBLIC DBStorage.SystemTuplesetHandle;
DomainDomain: PUBLIC DBCommon.TupleHandle;
RelationDomain: PUBLIC DBCommon.TupleHandle;
RelationTupleSet: PUBLIC DBStorage.SystemTuplesetHandle;
IndexTupleSet: PUBLIC DBStorage.SystemTuplesetHandle;
IndexDomain: PUBLIC DBCommon.TupleHandle;
SetSystemTable: PUBLIC PROC[tid: TID] RETURNS[tupleSet: DBStorage.SystemTuplesetHandle, tuple: DBCommon.TupleHandle] = TRUSTED {
tupleSet ← DBStorage.CreateSystemTupleset[tid];
tuple ← DBStorage.ConsSystemTupleObject[];
tuple.tid ← tid;
systemTupleVec[tid] ← tuple
};
InitializeDomainSchema: PUBLIC PROC[] = TRUSTED {
DBStorage.SetSystemTupleTable[systemTupleVec];
[DomainTupleSet, DomainDomain] ← SetSystemTable[DBModelPrivate.DomainTSID];
[IndexTupleSet, IndexDomain] ← SetSystemTable[DBModelPrivate.IndexTSID];
[RelationTupleSet, RelationDomain] ← SetSystemTable[DBModelPrivate.RelationTSID];
The DomainDomain tuples are tuplesets with the following additional fields:
1. the name of the domain
2. the index ID of the index on the names of entities in the domain
3. the superType of the domain (a TID that may be empty),
4. the field handle to be used when accessing entities in the tupleset
[] ← DBStorage.AddSystemField[DomainTupleSet, DBModelPrivate.TupleSetDescriptor];
nameHandle ← DBStorage.AddSystemField[DomainTupleSet, DBModelPrivate.NameDescriptor];
indexHandle ← DBStorage.AddSystemField[DomainTupleSet, DBStorage.FieldDescriptor[Group[groupID: IndexDomain]]];
superHandle ← DBStorage.AddSystemField[DomainTupleSet, DBStorage.FieldDescriptor[Group[groupID: DomainDomain]]];
nameHandleHandle ← DBStorage.AddSystemField[DomainTupleSet, DBModelPrivate.HandleDescriptor] };
The DomainDomain Field Handles and Group Field Descriptors
nameHandle, indexHandle, superHandle, nameHandleHandle: DBStorage.FieldHandle;
The field handles for entities in the DomainDomain. The nameHandle extracts the name of the domain, indexHandle the index for the domain, superHandle the supertype of the domain, and nameHandleHandle the handle to get the field handle to read the name field of tuples stored in the tupleset for the domain
Operations on Domains
DeclareDomain: PUBLIC PROC [name: ROPE, segment: Segment] RETURNS [d: Domain] = {
Creates a domain of this name or fetches it if it already exists.
sh: SegmentHandle = DBModelPrivate.SegmentToHandle[segment];
domainIndex: DBStorage.IndexHandle = sh.indices[DBCommon.domainIndex];
AddToCache: PROC[tuple: TupleHandle] RETURNS[d: Domain] = {
cachedTuple: TupleHandle = DBModelPrivate.CopyTupleHandle[tuple];
domainEntry: REF DomainObject = NEW[DomainObject ← [key: sh.key, segment: segment, name: name, tupleSet: cachedTuple]];
d ← domainEntry;
[] ← SymTab.Store[x: sh.domainTable, key: name, val: domainEntry];
[] ← CardTable.Store[x: sh.domainsByTID, key: tuple.tid, val: domainEntry];
RETURN[d]
};
d ← LookupDomain[name, segment];
IF d # NIL THEN RETURN[d];
Create a new domain.
BEGIN
t: TupleHandle = DBStorage.CreateSystemPageTuple[x: DomainTupleSet, y: NIL, s: segment];
i: TupleHandle = DBStorage.CreateSystemPageTuple[x: IndexTupleSet, y: t, s: segment];
DBStorage.CreateTupleset[t];
DBStorage.CreateIndex[i];
DBStorage.WriteVarByte[t, nameHandle, name];
DBStorage.InsertIntoIndex[domainIndex, name, t];
SetTupleField[t, indexHandle, i];
Add the tuple to the domain cache for the segment
d ← AddToCache[t];
create the name field for the entities of this domain
d.entityNameHandle ← DBStorage.AddField[t, DBModelPrivate.NameDescriptor];
DBModelPrivate.SetFieldField[t, nameHandleHandle, d.entityNameHandle];
d.index ← CopyTupleHandle[i];
d.detailsKnown ← TRUE;
sh.schemaUpdated ← TRUE;
RETURN[d]
END
};
LookupDomain: PUBLIC PROC [name: ROPE, segment: Segment] RETURNS [d: Domain] = {
sh: SegmentHandle = DBModelPrivate.SegmentToHandle[segment];
domainIndex: DBStorage.IndexHandle = sh.indices[DBCommon.domainIndex];
AddToCache: PROC[tuple: TupleHandle] RETURNS[d: Domain] = {
cachedTuple: TupleHandle = DBModelPrivate.CopyTupleHandle[tuple];
domainEntry: REF DomainObject = NEW[DomainObject ← [key: sh.key, segment: segment, name: name, tupleSet: cachedTuple]];
d ← domainEntry;
[] ← SymTab.Store[x: sh.domainTable, key: name, val: domainEntry];
[] ← CardTable.Store[x: sh.domainsByTID, key: tuple.tid, val: domainEntry];
RETURN[d]
};
val: REF ANY = SymTab.Fetch[x: sh.domainTable, key: name].val;
d ← NARROW[val];
IF d # NIL THEN RETURN[d];
BEGIN
See if you can find it in the index for the DomainDomain
tuple: TupleHandle = GetNamedTuple[domainIndex, name];
IF tuple = NIL THEN RETURN[NIL]; -- it's not to be found
It's in the database, but not in the cache. Cache it; none of the details of the entry are computed yet
d ← AddToCache[tuple];
END };
ReadDomain: PUBLIC PROC[d: Domain] = {
d.index ← CopyTupleHandle[GetTupleField[d.tupleSet, indexHandle]];
d.entityNameHandle ← DBModelPrivate.GetFieldField[d.tupleSet, nameHandleHandle];
d.detailsKnown ← TRUE
};
CheckForNull: PUBLIC PROC[t: TupleHandle] = {
IF DBStorage.NullTuple[t] THEN DB.Error[NullifiedArgument] };
CopyTupleHandle: PUBLIC PROC[t: TupleHandle] RETURNS[new: TupleHandle] = {
new ← DBStorage.ConsSystemTupleObject[];
new.tid ← t.tid; new.cacheHint ← t.cacheHint };
EmptyTupleSet: PUBLIC PROC[ts: DBStorage.TuplesetHandle] RETURNS[yes: BOOL] = {
yes ← DBStorage.EmptyTupleSet[ts] };
GetNamedTuple: PUBLIC PROC[index: DBStorage.IndexHandle, name: ROPE] RETURNS[tuple: TupleHandle] = { tuple ← DBStorage.TupleForKey[index, name] };
SetTupleField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle, val: TupleHandle] = {
DBStorage.SetTupleField[t, f, val] };
GetTupleField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle] RETURNS[val: TupleHandle] = { val ← DBStorage.ReadTID[t, f] };
NullifyTupleField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle] = {
DBStorage.WriteTIDNil[t, f] };
GetRopeField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle] RETURNS[val: Rope.ROPE] = { val ← DBStorage.ReadVarByte[t, f] };
SetRopeField: PUBLIC PROC[t: TupleHandle, f: DBStorage.FieldHandle, val: Rope.ROPE] = {
IF val = NIL THEN val ← "";
DBStorage.WriteVarByte[t, f, val] };
CheckDomainKey: PUBLIC PROC[d: Domain] = {
sh: SegmentHandle = DBModelPrivate.SegmentToHandle[d.segment];
IF sh.key # d.key THEN ERROR DB.Error[IllegalDomain] };
CompatibleTypes: PUBLIC PROC[d: Domain, type: TypeCode] RETURNS[yes: BOOL] = {
WHILE TRUE DO
IF d.tupleSet.tid = type.code THEN RETURN[TRUE];
is the subtype the type we're looking for?
IF NOT d.superTypeKnown THEN SetSuperType[d];
IF d.superType = NIL THEN RETURN[FALSE]; -- d has no super type
d ← d.superType -- go up the chain
ENDLOOP };
SetSuperType: PROC[d: REF DomainObject] = {
Compute the SuperType of the domain d
superTuple: TupleHandle = GetTupleField[d.tupleSet, superHandle];
d.superType ← IF superTuple = NIL THEN NIL ELSE TupleToDomain[superTuple, d.segment];
d.superTypeKnown ← TRUE
};
TupleToDomain: PUBLIC PROC[t: TupleHandle, segment: Segment, name: Rope.ROPENIL] RETURNS[d: Domain] = {
IF t=NIL THEN RETURN[NIL];
BEGIN
sh: SegmentHandle = DBModelPrivate.SegmentToHandle[segment];
AddToCache: PROC[tuple: TupleHandle] RETURNS[d: Domain] = {
cachedTuple: TupleHandle = DBModelPrivate.CopyTupleHandle[tuple];
domainEntry: REF DomainObject = NEW[DomainObject ← [key: sh.key, segment: segment, name: name, tupleSet: cachedTuple]];
d ← domainEntry;
[] ← SymTab.Store[x: sh.domainTable, key: name, val: domainEntry];
[] ← CardTable.Store[x: sh.domainsByTID, key: t.tid, val: domainEntry];
RETURN[d]
};
d ← NARROW[CardTable.Fetch[sh.domainsByTID, t.tid].val];
IF d # NIL THEN RETURN;
IF name = NIL THEN name ← GetRopeField[t, nameHandle];
d ← AddToCache[t]
END
};
DestroyDomain: PUBLIC PROC [d: Domain] = {
Destroys a domain, all its entities, and all relationships that reference entities in d. To destroy a domain, we remove it from the domain from the SubType list of its SuperType, destroy all of the entities in the domain and then destroy all of the relations that had fields of the domain type
IF d = NIL THEN RETURN;
CheckDomainKey[d];
IF NOT d.detailsKnown THEN ReadDomain[d];
If d still has remaining subtypes, then it cannot be destroyed
BEGIN
subs: DomainSet = SubTypes[d];
IF NextDomain[subs] # NIL THEN {
ReleaseDomainSet[subs];
ERROR DB.Error[NotImplemented] }; -- must destroy the domain's subdomains first!
ReleaseDomainSet[subs]
END;
BEGIN
segmentHandle: SegmentHandle = DBModelPrivate.SegmentToHandle[d.segment];
TID: DBCommon.TID = d.tupleSet.tid;
remove connection to any supertype
d.superType ← NIL; NullifyTupleField[d.tupleSet, superHandle];
Destroy all of the entities in the domain
BEGIN
scanHandle: DBStorage.TuplesetScanHandle = DBStorage.OpenScanTupleset[d.tupleSet, First];
FOR t: TupleHandle ← DBStorage.NextScanTupleset[scanHandle], DBStorage.NextScanTupleset[scanHandle] UNTIL t = NIL DO
DBModelPrivate.Unlink[t, d];
SetRopeField[t, d.entityNameHandle, NIL];
DBStorage.DestroyTuple[t]
ENDLOOP;
DBStorage.CloseScanTupleset[scanHandle]
END;
Destroy the index on the domain
BEGIN
DBStorage.DestroyIndex[d.index];
DBStorage.DestroyTuple[d.index]
END;
Destroy all of the relations that reference the domain and the domain tuple itself
DBModelPrivate.DestroyStoredRelations[d];
DBModelPrivate.DestroySurrogates[d];
DBStorage.DeleteFromIndex[segmentHandle.indices[DBCommon.domainIndex], d.name, d.tupleSet];
DBStorage.DestroyTuple[d.tupleSet];
Finally, remove the information from the cache and remember that the schema has been updated
segmentHandle.schemaUpdated ← TRUE;
[] ← SymTab.Delete[segmentHandle.domainTable, d.name];
[] ← CardTable.Delete[segmentHandle.domainsByTID, TID];
d.key ← 0; -- this entry should no longer be used
END
};
DomainsByName: PUBLIC PROC [segment: Segment, lowName, highName: ROPE, start: FirstLast] RETURNS[ds: DomainSet] = {
Provides a domain set that enumerates domains in the given segment. If lowName and highName are non-NIL, enumerates only those domains whose name is lexicographically >=lowName and <=highName. If only highName is NIL, it defaults to lowName, i.e. we will search for the domain whose name exactly equals lowName.
iScan: DBStorage.IndexHandle = DBModelPrivate.SegmentToHandle[segment].indices[DBCommon.domainIndex];
IF highName = NIL THEN highName ← lowName;
ds ← NEW[DomainSetObject ← [segment: segment, variant: index[scanHandle: DBStorage.OpenScanIndex[iScan, [lowerBound: lowName, upperBound: highName, includeLowerBound: TRUE, includeUpperBound: TRUE, lowerBoundInfinity: lowName = NIL, upperBoundInfinity: highName = NIL], start]]]];
RETURN[ds] };
typeTuple: TupleHandle ← DBStorage.ConsSystemTupleObject[];
TypeToDomain: PUBLIC PROC[type: DBDefs.TypeCode, segment: Segment] RETURNS[d: Domain] = {
Turn the TID into a tuple handle and use TupleToDomain
typeTuple.tid ← type.code; typeTuple.cacheHint ← NIL;
d ← TupleToDomain[typeTuple, segment]
};
NameFromIndexKey: PUBLIC PROC[key: DBCommon.IndexKey] RETURNS[name: Rope.ROPE] = TRUSTED {
IF key = NIL OR key.length = 0 THEN RETURN[NIL];
BEGIN
indexValue: Rope.Text ← Rope.NewText[key.length];
FOR i: CARDINAL IN [0..key.length) DO indexValue.text[i] ← key.text[i] ENDLOOP;
indexValue.length ← key.length;
RETURN[indexValue]
END
};
NextDomain: PUBLIC PROC [ds: DomainSet] RETURNS [d: Domain] = TRUSTED {
IF ds = NIL THEN RETURN[NIL];
WITH ds: ds SELECT FROM
index =>
BEGIN
name: Rope.ROPE = NameFromIndexKey[DBStorage.NextScanKey[ds.scanHandle]];
tuple: DBCommon.TupleHandle = IF name # NIL THEN DBStorage.ThisScanTuple[ds.scanHandle] ELSE NIL;
IF tuple # NIL THEN d ← TupleToDomain[tuple, ds.segment, name]
END;
group => {
tuple: DBCommon.TupleHandle = DBStorage.NextInGroup[ds.scanHandle];
IF tuple # NIL THEN d ← TupleToDomain[tuple, ds.segment] }
ENDCASE };
PrevDomain: PUBLIC PROC [ds: DomainSet] RETURNS [d: Domain] = TRUSTED {
IF ds = NIL THEN RETURN[NIL];
WITH ds: ds SELECT FROM
index =>
BEGIN
name: Rope.ROPE = NameFromIndexKey[DBStorage.PrevScanKey[ds.scanHandle]];
tuple: DBCommon.TupleHandle = IF name # NIL THEN DBStorage.ThisScanTuple[ds.scanHandle] ELSE NIL;
IF tuple # NIL THEN d ← TupleToDomain[tuple, ds.segment, name]
END;
group => {
tuple: DBCommon.TupleHandle = DBStorage.PrevInGroup[ds.scanHandle];
IF tuple # NIL THEN d ← TupleToDomain[tuple, ds.segment] }
ENDCASE };
ReleaseDomainSet: PUBLIC PROC [ds: DomainSet] = TRUSTED {
Should be called when client is finished with a DomainSet.
IF ds = NIL THEN RETURN;
WITH ds: ds SELECT FROM
index => { DBStorage.CloseScanIndex[ds.scanHandle]; ds.scanHandle ← NIL };
group => { DBStorage.CloseScanGroup[ds.scanHandle]; ds.scanHandle ← NIL }
ENDCASE };
EmptyDomain: PROC[d: Domain] RETURNS[ yes: BOOL ] = {
scanHandle: DBStorage.TuplesetScanHandle = DBStorage.OpenScanTupleset[d.tupleSet, First];
nextTuple: TupleHandle = DBStorage.NextScanTupleset[scanHandle];
DBStorage.CloseScanTupleset[scanHandle];
RETURN[nextTuple = NIL] };
DeclareSubType: PUBLIC PROC [of, is: Domain] = {
Check that sub (is) does not already have a supertype, that the supertype isn't compatible with the subtype (i.e., you're not about to introduce a cycle) and that supertype is empty.
sh: SegmentHandle = DBModelPrivate.SegmentToHandle[of.segment];
CheckDomainKey[of]; CheckDomainKey[is];
IF of.segment # is.segment THEN ERROR DB.Error[IllegalSuperType];
IF NOT is.superTypeKnown THEN SetSuperType[is];
IF is.superType # NIL THEN
IF Rope.Equal[is.superType.name, of.name] THEN RETURN
ELSE ERROR DB.Error[IllegalSuperType];
IF CompatibleTypes[is, TypeForDomain[of]] THEN ERROR DB.Error[IllegalSuperType];
IF NOT EmptyDomain[of] THEN ERROR DB.Error[NotImplemented];
Currently can't define subdomain if super already has entries
Set the supertype of the domain is to be the domain of and add the domain is to the subtype list of domain of
SetTupleField[is.tupleSet, superHandle, of.tupleSet];
is.superType ← of;
Record the fact that the schema has been changed
sh.schemaUpdated ← TRUE;
};
DestroySubType: PUBLIC PROC [of, is: Domain] = {
sh: SegmentHandle = DBModelPrivate.SegmentToHandle[of.segment];
CheckDomainKey[of]; CheckDomainKey[is];
IF of.segment # is.segment THEN ERROR DB.Error[IllegalSuperType];
IF NOT is.superTypeKnown THEN SetSuperType[is];
IF is.superType = NIL OR is.superType # of THEN ERROR DB.Error[IllegalSuperType];
IF NOT EmptyDomain[is] THEN ERROR DB.Error[NotImplemented];
NullifyTupleField[is.tupleSet, superHandle];
is.superType ← NIL;
sh.schemaUpdated ← TRUE };
SuperType: PUBLIC PROC [d: Domain] RETURNS [super: Domain] = {
CheckDomainKey[d];
IF NOT d.superTypeKnown THEN SetSuperType[d];
super ← d.superType };
SubTypes: PUBLIC PROC [d: Domain] RETURNS[subs: DomainSet] = {
Returns the subtypes of a given domain (if any have been previously defined using DeclareSubType)
CheckDomainKey[d];
BEGIN
scanHandle: DBStorage.GroupScanHandle = DBStorage.OpenScanGroup[d.tupleSet, superHandle, First];
subs ← NEW[DomainSetObject ← [segment: d.segment, variant: group[scanHandle]]]
END };
NoSubtypes: PUBLIC PROC[d: Domain] RETURNS[yes: BOOL] = {
This quickly checks for no subtypes
yes ← DBStorage.TupleForField[d.tupleSet, superHandle] = NIL };
TypeForDomain: PUBLIC PROC[d: Domain] RETURNS[type: TypeCode] = {
CheckDomainKey[d];
RETURN[[code: d.tupleSet.tid]] };
Operations on Entities
DeclareEntity: PUBLIC PROC[d: Domain, name: ROPE, newOnly: BOOLFALSE] RETURNS [e: Entity] =
BEGIN
CheckDomainKey[d];
e ← LookupEntity[d, name];
IF e # NIL THEN IF newOnly THEN ERROR DB.Error[AlreadyExists] ELSE RETURN[e];
BEGIN
tuple: TupleHandle = DBStorage.CreateTuple[x: d.tupleSet];
DBStorage.InsertIntoIndex[d.index, name, tuple];
DBStorage.WriteVarByte[tuple, d.entityNameHandle, name];
e ← NEW[EntityObject ← [name: name, domain: d, tuple: tuple]]
END
END;
LookupEntity: PUBLIC PROC[d: Domain, name: ROPE] RETURNS [e: Entity] =
Checks to see if the entity already exists. If so, it returns it, if not, it returns NIL.
BEGIN
tuple: DBCommon.TupleHandle;
IF NOT d.detailsKnown THEN ReadDomain[d];
tuple ← GetNamedTuple[d.index, name];
IF tuple = NIL THEN RETURN[NIL];
RETURN[NEW[EntityObject ← [name: name, domain: d, tuple: tuple]]]
END;
DomainSubset: PUBLIC PROCEDURE[d: Domain, lowName, highName: ROPE, start: FirstLast] RETURNS [es: EntitySet] =
Used with NextEntity to index through entities in a domain. If lowName or highName are non-NIL, enumerates only those entities whose name is lexicographically >= lowName and <= highName, and the enumeration is sorted. In this case, we create an "index" variant of EntitySetObject. If both are NIL, we create an unindexed (tupleSet variant) EntitySet.
BEGIN
CheckDomainKey[d];
IF NOT d.detailsKnown THEN ReadDomain[d];
BEGIN -- Search using index
IF highName = NIL THEN highName ← lowName;
es ← NEW[EntitySetObject ← [domain: d, scanHandle: DBStorage.OpenScanIndex[d.index, [lowerBound: lowName, upperBound: highName, includeLowerBound: TRUE, includeUpperBound: TRUE, lowerBoundInfinity: lowName = NIL, upperBoundInfinity: highName = NIL], start]]];
END
END;
NextEntity: PUBLIC PROCEDURE[es: EntitySet] RETURNS[e: Entity] = TRUSTED {
IF es=NIL THEN RETURN[NIL];
CheckDomainKey[es.domain];
BEGIN
name: Rope.ROPE = NameFromIndexKey[DBStorage.NextScanKey[es.scanHandle]];
tuple: TupleHandle = IF name # NIL THEN DBStorage.ThisScanTuple[es.scanHandle] ELSE NIL;
IF tuple = NIL THEN RETURN[NIL];
RETURN[NEW[ EntityObject ← [domain: es.domain, name: name, tuple: tuple]]]
END };
PrevEntity: PUBLIC PROCEDURE[es: EntitySet] RETURNS[e: Entity] = TRUSTED {
IF es=NIL THEN RETURN[NIL];
CheckDomainKey[es.domain];
BEGIN
name: Rope.ROPE = NameFromIndexKey[DBStorage.PrevScanKey[es.scanHandle]];
tuple: TupleHandle = IF name # NIL THEN DBStorage.ThisScanTuple[es.scanHandle] ELSE NIL;
IF tuple = NIL THEN RETURN[NIL];
RETURN[NEW[ EntityObject ← [domain: es.domain, name: name, tuple: tuple]]]
END };
ReleaseEntitySet: PUBLIC PROCEDURE[es: EntitySet] =
BEGIN
IF es=NIL THEN RETURN;
IF es.scanHandle # NIL THEN {
DBStorage.CloseScanIndex[es.scanHandle]; es.scanHandle ← NIL };
END;
DestroyEntity: PUBLIC PROC[e: Entity] =
BEGIN
CheckDomainKey[e.domain]; -- make sure it's a legal entity first
CheckForNull[e.tuple];
DBModelPrivate.Unlink[e.tuple, e.domain]; -- destroy all relationships referring to the entity
Destroy the name index entry
DBStorage.DeleteFromIndex[e.domain.index, e.name, e.tuple];
Destroy the entity by setting the name field to NIL and then destroying the tuple
SetRopeField[e.tuple, e.domain.entityNameHandle, NIL];
DBStorage.DestroyTuple[e.tuple];
END;
CopyEntity: PUBLIC PROC[e: Entity] RETURNS[Entity] = {
IF e = NIL THEN RETURN[NIL];
RETURN[NEW[EntityObject ← [domain: e.domain, name: e.name, tuple: CopyTupleHandle[e.tuple]]]] };
DomainEq: PUBLIC PROC [d1, d2: Domain] RETURNS[BOOL] = {
IF d1 = NIL THEN RETURN[d2 = NIL];
IF d2 = NIL THEN RETURN[FALSE];
CheckDomainKey[d1]; CheckDomainKey[d2];
RETURN[d1^ = d2^] };
NullDomain: PUBLIC PROC[d: Domain] RETURNS[BOOLEAN] = {
IF d = NIL THEN RETURN[TRUE];
BEGIN
sh: SegmentHandle = DBModelPrivate.SegmentToHandle[d.segment];
RETURN[d.key # sh.key]
END };
NullEntity: PUBLIC PROC[e: Entity] RETURNS[yes: BOOL] = {
IF e = NIL THEN RETURN[TRUE];
IF DBStorage.NullTuple[e.tuple] THEN RETURN[TRUE];
BEGIN
d: Domain = e.domain;
sh: SegmentHandle = DBModelPrivate.SegmentToHandle[d.segment];
RETURN[d.key # sh.key]
END };
EntityEq: PUBLIC PROC[e1: Entity, e2: Entity] RETURNS[yes: BOOL] = {
IF NullEntity[e1] THEN RETURN[NullEntity[e2]];
IF NullEntity[e2] THEN RETURN[NullEntity[e1]];
RETURN[DomainEq[e1.domain, e2.domain] AND e1.tuple.tid = e2.tuple.tid]
};
EntityInfo: PUBLIC PROC [e: Entity] RETURNS [name: ROPE, domain: Domain] = {
IF e = NIL THEN RETURN;
CheckDomainKey[e.domain];
RETURN[e.name, e.domain] };
ToUnderType: PUBLIC PROC[e: Entity] = {
IF e = NIL THEN RETURN;
CheckDomainKey[e.domain];
CheckForNull[e.tuple];
BEGIN
domaintuple: TupleHandle = DBStorage.ReadTupleset[e.tuple];
IF domaintuple.tid = e.domain.tupleSet.tid THEN RETURN;
e.domain ← TupleToDomain[domaintuple, e.domain.segment]
END };
ChangeName: PUBLIC PROC [of: Entity, to: Rope.ROPE] ~ {
IF of = NIL THEN RETURN;
IF to = NIL THEN ERROR DB.Error[NILArgument];
CheckDomainKey[of.domain];
CheckForNull[of.tuple];
BEGIN
d: Domain = of.domain;
IF GetNamedTuple[d.index, to] # NIL THEN DB.Error[AlreadyExists];
DBStorage.DeleteFromIndex[d.index, of.name, of.tuple];
DBStorage.InsertIntoIndex[d.index, to, of.tuple];
DBStorage.WriteVarByte[of.tuple, d.entityNameHandle, to];
of.name ← to
END
};
END.