File: DBModelPrivateImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Contents: Implementation of miscellaneous internal procedures for Model level
Last edited by:
Rick Cattell on September 15, 1983 4:40 pm
Willie-Sue, April 25, 1985 2:52:14 pm PST
Donahue, September 30, 1985 9:34:53 am PDT
Widom, September 13, 1985 4:27:39 pm PDT
DIRECTORY
Ascii USING[Upper],
Basics,
BasicTime,
ConvertUnsafe,
DBCommon,
DBStorage,
DBStats,
DBDefs,
DB,
DBModel,
DBModelPrivate,
DBModelSchema,
Rope;
DBModelPrivateImpl: CEDAR PROGRAM
IMPORTS Ascii, Basics, BasicTime, ConvertUnsafe, Rope,
DBStorage, DB, DBDefs, DBModel, DBModelPrivate, DBModelSchema
EXPORTS DBModelPrivate =
BEGIN OPEN DB, DBCommon, DBDefs, DBModel, DBModelPrivate;
desperate: PUBLIC BOOLFALSE;
MaxDataTypeID: LONG CARDINAL = 25;
Checking procedures
IsDomainType: PUBLIC PROC [vt: DataType] RETURNS [BOOL] =
BEGIN
SELECT vt FROM
IntType, RopeType, BoolType, TimeType, RecordType => RETURN[FALSE];
ENDCASE => RETURN[TRUE]
END;
String manipulation
ConvertToUpper: PUBLIC PROC[s: ROPE] RETURNS [ROPE] =
BEGIN pos: INT← -1;
upperProc: SAFE PROC RETURNS [CHAR] = CHECKED
{RETURN[Ascii.Upper[s.Fetch[pos← pos+1]]]};
RETURN[Rope.FromProc[s.Length[], upperProc]]
END;
NCode: PUBLIC PROC[v: Value] RETURNS[ROPE] = TRUSTED
Encodes value v as an index key. All routines constructing index
keys or doing key comparisons should call this routine.
BEGIN
WITH v: v SELECT FROM
null => RETURN[NIL]; -- pass NILs right through for now
rope =>
BEGIN CheckForNulls[v.value]; RETURN[ConvertToUpper[v.value]] END;
integer =>
BEGIN i: LONG CARDINALLOOPHOLE[v.value, LONG CARDINAL]+20000000000B;
s: STRING← [4]; s.length← 4;
s[0]← HighByte[Basics.HighHalf[i]];
s[1]← LowByte[Basics.HighHalf[i]];
s[2]← HighByte[Basics.LowHalf[i]]; s[3]← LowByte[Basics.LowHalf[i]];
RETURN[ConvertUnsafe.ToRope[s]]
END;
time => -- same as above but don't turn off top bit
BEGIN i: LONG CARDINAL← BasicTime.ToPupTime[v.value];
s: STRING← [4]; s.length← 4;
s[0]← HighByte[Basics.HighHalf[i]]; s[1]← LowByte[Basics.HighHalf[i]];
s[2]← HighByte[Basics.LowHalf[i]]; s[3]← LowByte[Basics.LowHalf[i]];
RETURN[ConvertUnsafe.ToRope[s]]
END;
entity => { -- we index entity-valued attributes by the entity name
IF NullEntity[v.value] THEN RETURN[NIL];-- pass NILs right through for now
RETURN[ConvertToUpper[GetCachedEntityInfo[SegmentOf[v.value], v.value].name]] };
boolean =>
IF v.value THEN RETURN["TRUE"] ELSE RETURN["FALSE"];
ENDCASE => ERROR Error[NotImplemented]; -- for now
END;
CheckForNulls: PROC[s: ROPE] =
INLINE BEGIN
i: LONG CARDINAL ← 0;
last: LONG CARDINAL ← Rope.Size[s];
WHILE (i ← i+1) < last DO
IF Rope.Fetch[s,i]=0C OR Rope.Fetch[s,i]=377C THEN ERROR DB.Error[IllegalString] ENDLOOP;
END;
NCodeForTuple: PROC[t: Relship, i: Index] RETURNS [ROPE] =
Encodes the attributes of t required as factors of index i as ropes, concatenates
the ropes in the order required by the index, and returns the result (for use in
using DBStorage indices, which require a single rope key). A null byte is placed
between the encoded attribute ropes to preserve the lexicographic priorities of
the attributes. Note this means null chars may not appear in attributes of type string
if proper orderings are to result.
BEGIN
s: ROPE← "";
FOR ifs: LIST OF IndexFactor← VL2TL[QGetPList[i, ifIndexOf]], ifs.rest UNTIL ifs=NIL DO
if: IndexFactor← ifs.first;
s← Rope.Cat[s, NCode[QGetF[t, DBModelSchema.TupleToAttribute[V2E[QGetP[if, ifAttributeIs]]]]], "\000"];
ENDLOOP;
RETURN[s];
END;
MakeNullValueOfType: PUBLIC PROC [vt: DataType] RETURNS[Value] =
BEGIN
SELECT vt FROM
RopeType => {gdfc: ROPE← ""; RETURN[S2V[gdfc]]};
IntType => RETURN[I2V[0]];
TimeType => RETURN[T2V[BasicTime.nullGMT]];
BoolType => RETURN[B2V[FALSE]];
ENDCASE => RETURN[[null[]]];
END;
Index-related procedures
GetDomainIndex: PUBLIC PROC[s: SegmentHandle] RETURNS [DBStorage.IndexHandle] =
Returns the domain index for the segment
{RETURN[DBStorage.RootIndicesFromSegment[s.segment][DBCommon.domainIndex]]};
GetRelationIndex: PUBLIC PROC[s: SegmentHandle] RETURNS [DBStorage.IndexHandle] =
Returns the relation index for the segment
{RETURN[DBStorage.RootIndicesFromSegment[s.segment][DBCommon.relationIndex]]};
CreateEntityIndexEntries: PUBLIC PROC [e: Entity] =
Finds the name index for e's Domain and insert e into it.
Also creates index entries for any surrogate relations that are actually stored in e.
BEGIN
name: ROPE; d: Domain;
nameIndex: Index;
surrRelns: LIST OF Relation;
dt: TupleHandle;
[name, d] ← GetCachedEntityInfo[SegmentOf[e], e];
dt ← DBModelSchema.GetDomainTuple[d];
nameIndex ← PV2E[SafeGetP[dt, dIndexProp]];
DBStorage.InsertIntoIndex[nameIndex, NCode[S2V[name]], e];
FOR surrRelns← GetIndexedSurrogates[dt], surrRelns.rest UNTIL surrRelns=NIL DO
r: Relation← surrRelns.first;
fakeSurrogateRelship: SurrogateRelshipHandle← SurrogateCreateRelship[r];
fakeSurrogateRelship.entity← e;
CreateAllIndexEntries[fakeSurrogateRelship];
ENDLOOP;
END;
DestroyEntityIndexEntries: PUBLIC PROC [e: Entity] =
Find the name index for e's Domain and remove e from it. Does NOT destroy
any surrogate relation index entries; that can be done by DestroyLinksTo.
BEGIN
name: ROPE; d: Domain;
nameIndex: Index;
[name, d] ← GetCachedEntityInfo[SegmentOf[e], e];
nameIndex ← PV2E[SafeGetP[DBModelSchema.GetDomainTuple[d], dIndexProp]];
IF nameIndex#NIL THEN
Non-NIL unless we are destroying this entity in the process of destroying domain
DBStorage.DeleteFromIndex[nameIndex, NCode[S2V[name]], e];
END;
CreateIndexEntries: PUBLIC PROC [t: Relship, indexList: LIST OF Index] =
BEGIN
th: TupleHandle← GetTupleHandle[t];
FOR is: LIST OF Index ← indexList, is.rest UNTIL is=NIL DO
DBStorage.InsertIntoIndex[is.first, NCodeForTuple[t, is.first], th];
ENDLOOP;
END;
CreateAllIndexEntries: PUBLIC PROC [t: Relship] =
Find all existing indexes for t's tupleset and insert t into them.
BEGIN
i: Index;
th: TupleHandle← GetTupleHandle[t];
al: LIST OF Attribute ← QRelationOf[t].attributes;
FOR il: LIST OF Index← GetRelationIndices[al], il.rest UNTIL il=NIL DO
i← il.first;
DBStorage.InsertIntoIndex[i, NCodeForTuple[t, i], th];
ENDLOOP;
END;
DestroyIndexEntries: PUBLIC PROC [t: Relship, indexList: LIST OF Index] =
BEGIN
th: TupleHandle← GetTupleHandle[t];
FOR is: LIST OF Index ← indexList, is.rest UNTIL is=NIL DO
DBStorage.DeleteFromIndex[is.first, NCodeForTuple[t, is.first], th];
ENDLOOP;
END;
DestroyAllIndexEntries: PUBLIC PROC [t: Relship, r: Relation] =
Find all relevant indexes for t and delete t from them. To do this, find the attributes of
t's relation, find index factors involving those attributes, find indices for those index factors,
and destroy index entries for them. The relationship t may be an ordinary or surrogate
relationship; GetTupleHandle below returns the entity handle in the latter case, the
relationship handle in the former case, so the right thing happens.
BEGIN i: Index; th: TupleHandle← GetTupleHandle[t];
FOR il: LIST OF Index← GetRelationIndices[r.attributes], il.rest UNTIL il=NIL DO
i← il.first;
DBStorage.DeleteFromIndex[i, NCodeForTuple[t, i], th];
ENDLOOP;
END;
DestroyLinksTo: PUBLIC PROC[e: Entity] =
Destroys any references to e by following backlinks (groups) to the tuples whose
fields point to t, and destroying those tuples. Also destroys tuples that do NOT
reference e via a group (i.e., with aUnlinkedIs TRUE); we get these because
QGetAllRefAttributes returns unlinked attributes as well.
Note: DBModelGlobalImpl.DestroyDictionaryEntity depends upon this procedure
working for dictionary entities as well as ordinary client entities.
BEGIN
al: LIST OF Attribute;
rs: RelshipSet; rsi: CARDINAL;
r: Relship;
ev: Value ← E2V[e];
al← QGetAllRefAttributes[e];
IF al # NIL AND DBModelSchema.InvalidAttribute[al.first] THEN ERROR Error[InvalidSchema];
[rs, rsi] ← GetNewRelshipSet[];
FOR alT: LIST OF Attribute← al, alT.rest UNTIL alT=NIL DO
rs← QRelationSubset[alT.first.relation, LIST[[alT.first, ev]], First, rs];
WHILE (r← QNextRelship[rs])#NIL DO
QDestroyRelship[r] ENDLOOP;
QReleaseRelshipSet[rs];
ENDLOOP;
ReturnRelshipSet[rsi];
END;
GetRelationIndices: PROC [attributes: LIST OF Attribute] RETURNS [LIST OF Index] =
BEGIN il: LIST OF Index← NIL;
FOR al: LIST OF Attribute← attributes, al.rest UNTIL al=NIL DO
ifl: LIST OF IndexFactor← VL2TL[QGetPList[DBModelSchema.GetAttributeTuple[al.first], ifAttributeOf]];
FOR iflT: LIST OF IndexFactor← ifl, ifl.rest UNTIL iflT=NIL DO
IF iflT.first#NIL THEN il← AppendIfNew[V2E[QGetP[iflT.first, ifIndexIs]], il];
ENDLOOP;
ENDLOOP;
RETURN[il]
END;
GetIndexedSurrogates: PROC [t: TupleHandle]
RETURNS [indexedSurrogates: LIST OF Relation] =
TRUSTED BEGIN
surrogates: LIST OF Relation← GetSurrogates[t];
indexedSurrogates← NIL;
FOR surrogatesT: LIST OF Relation← surrogates, surrogatesT.rest UNTIL surrogatesT=NIL DO
surrAttrs: LIST OF Attribute ← surrogatesT.first.attributes;
FOR surrAttrs← surrAttrs, surrAttrs.rest UNTIL surrAttrs=NIL DO
v: PrivateValue = SafeGetP[DBModelSchema.GetAttributeTuple[surrAttrs.first], ifAttributeOf];
WITH v: v SELECT FROM
public =>
WITH v.val SELECT FROM
null => NULL;
ENDCASE => GOTO ThisRelation;
ENDCASE => GOTO ThisRelation;
ENDLOOP;
REPEAT
ThisRelation=> indexedSurrogates← CONS[surrogatesT.first, indexedSurrogates];
ENDLOOP;
END;
GetSurrogates: PROC [t: TupleHandle] RETURNS [LIST OF Relation] =
Returns the list of surrogate relations that have been targetted to domain d.
First we find the attributes that have been targetted to domain d using a group scan on
aDomainOf, then we find their relations, eliminating duplicates.
BEGIN rl: LIST OF Relation← NIL;
FOR al: LIST OF Attribute← VL2AL[QGetPList[t, aDomainOf]], al.rest UNTIL al=NIL DO
IF DBModelSchema.InvalidAttribute[al.first] THEN ERROR Error[InvalidSchema];
rl← AppendIfNewR[al.first.relation, rl];
ENDLOOP;
RETURN[rl]
END;
AppendIfNew: PROC [e: Entity, il: LIST OF Index] RETURNS [LIST OF Index] =
Add entity e to list el if it is not already in the list.
BEGIN ilT: LIST OF Index;
FOR ilT← il, ilT.rest UNTIL ilT=NIL DO
IF EntityEq[ilT.first, e] THEN RETURN[il] ENDLOOP;
RETURN[CONS[e, il]];
END;
AppendIfNewR: PROC [r: Relation, rl: LIST OF Relation] RETURNS [LIST OF Relation] =
Add relation r to list rl if it is not already in the list.
BEGIN rlT: LIST OF Relation;
FOR rlT← rl, rlT.rest UNTIL rlT=NIL DO
IF QRelationEq[rlT.first, r] THEN RETURN[rl] ENDLOOP;
RETURN[CONS[r, rl]];
END;
Handy list and set procedures
EmptyDomain: PUBLIC PROC [d: Domain] RETURNS [b: BOOLEAN] =
BEGIN
es: EntitySet; esi: CARDINAL;
[es, esi] ← GetNewEntitySet[];
es ← QDomainSubset[d: d, es: es];
b← NullEntity[QNextEntity[es]];
QReleaseEntitySet[es];
ReturnEntitySet[esi];
END;
EmptyRelation: PUBLIC PROC [r: Relation] RETURNS [b: BOOLEAN] =
BEGIN
rs: RelshipSet; rsi: CARDINAL;
[rs, rsi] ← GetNewRelshipSet[];
rs ← QRelationSubset[r: r, rs: rs];
b← NullRelship[QNextRelship[rs]];
QReleaseRelshipSet[rs];
ReturnRelshipSet[rsi];
END;
NumberOfAttributes: PUBLIC PROC [r: Relation] RETURNS [n: CARDINAL] =
BEGIN count: INT← 0;
IF DBModelSchema.InvalidRelation[r] THEN ERROR Error[InvalidSchema];
FOR al: LIST OF Attribute ← r.attributes, al.rest UNTIL al=NIL DO
count← count+1 ENDLOOP;
RETURN[count]
END;
GetFirstAttribute: PUBLIC PROC [
of: Relation, notCounting: Attribute← NIL] RETURNS [a: Attribute] =
Returns the first attribute of relation "of" except for "notCounting".
BEGIN ENABLE Error => TRUSTED {IF code=NILArgument THEN {a← NIL; CONTINUE}};
IF QAttributeEq[notCounting, of.attributes.first] THEN RETURN[of.attributes.rest.first]
ELSE RETURN[of.attributes.first];
END;
Miscellaneous procs
GetTypeAndLink: PUBLIC PROC [a: TupleHandle] RETURNS [type: DataType, link: LinkType] =
The use of aTypeIs is unusual. If aTypeCodeProp > 0, the type is the system entity whose
tid is the aTypeCodeProp. Else the type is the domain referenced by aTypeEntityProp,
and aTypeCodeProp= - LOOPHOLE[LinkType, INTEGER], distinguishable since <= 0.
Must check explicitly for system attribute because SafeGetP won't handle aTypeEntityProp
and aTypeCodeProp on system entities (it would be too inefficient to NEW INTs and BOOLs).
BEGIN
vtTid: INT; pos: LONG CARDINAL;
vtTid← PV2I[SafeGetP[a, aTypeCodeProp]];
IF vtTid<=0 THEN
RETURN[EntityToDataType[PV2E[SafeGetP[a, aTypeEntityProp]]], LOOPHOLE[Basics.LowHalf[-vtTid]]]
ELSE IF (pos← vtTid)<=MaxDataTypeID THEN
RETURN[EntityToDataType[DBModelPrivate.systemTupleVec[pos]], Linked]
ELSE ERROR InternalError;
END;
SetTypeAndLink: PUBLIC PROC [a: TupleHandle, type: DataType, link: LinkType] =
See comment above about encoding of aTypeCodeProp and aTypeEntityProp.
Also note: if attribute is not linked, must set aUnlinked to domain so can find later.
BEGIN
segment: SegmentHandle ← IF IsSystem[a] THEN NIL ELSE SegmentOf[a];
typeEntity: Entity ← DataTypeToEntity[type, segment];
SELECT type FROM
RopeType, IntType, TimeType, BoolType, AnyDomainType, RecordType =>
[]← SafeSetP[a, aTypeCodeProp, I2PV[typeEntity.tid]];
ENDCASE => {
Attribute defined on client-defined domain. Store domain in the aTypeEntity field.
[]← SafeSetP [a, aTypeCodeProp, I2PV[- LOOPHOLE[link, INTEGER]]];
[]← SafeSetP[a, aTypeEntityProp, E2PV[typeEntity]];
IF link=Unlinked OR link=Remote THEN -- set aUnlinked so can find refs by group scan
[]← SafeSetP[a, aUnlinkedIs, E2PV[typeEntity]] };
END;
SegmentOf: PUBLIC PROC[t: TupleHandle] RETURNS [SegmentHandle] = {
CheckNullified[t];
IF IsSystem[t] THEN RETURN[NIL];
RETURN[SegmentToHandle[DBStorage.SegmentFromTuple[t]]] };
CheckNullified: PROC [p: TupleHandle] = INLINE {
IF p=NIL THEN ERROR DB.Error[NILArgument]
ELSE IF p.tid=NoTID THEN ERROR DB.Error[NullifiedArgument] };
GetTupleHandle: PROC [t: Relship] RETURNS [TupleHandle] =
Returns the target entity's tuple handle if t is surrogate, else just returns t.
TRUSTED BEGIN
WITH t SELECT FROM
t1: TupleHandle => RETURN[t1];
t2: SurrogateRelshipHandle => RETURN[t2.entity];
ENDCASE => ERROR InternalError;
END;
MakeFD: PUBLIC PROC [vt: DataType, length: INT← 0, link: BOOLTRUE, a: TupleHandle← NIL]
RETURNS [DBStorage.FieldDescriptor] =
Takes information about an attribute, returns a field descriptor for values of the given
attribute.
BEGIN
len: CARDINAL← Basics.LowHalf[length]; -- sigh
SELECT vt FROM
BoolType => RETURN[[OneWord[]]];
IntType => RETURN[[TwoWord[]]];
TimeType => RETURN[[TwoWord[]]];
RopeType => RETURN[[VarByte[lengthHint: len]]];
RecordType => RETURN[[NWord[length: len]]];
ENDCASE => { -- some entity reffing field
IF a=NIL THEN ERROR;
IF link THEN RETURN[[Group[groupID: a]]]
ELSE RETURN[[VarByte[lengthHint: len]]];
};
END;
VL2AL: PROC[vl: LIST OF Value] RETURNS [LIST OF Attribute] = {
IF vl = NIL THEN RETURN[NIL]
ELSE RETURN[CONS[DBModelSchema.TupleToAttribute[V2E[vl.first]], VL2AL[vl.rest]]] };
LowByte: PROC [n: CARDINAL] RETURNS [CHAR] = TRUSTED INLINE
{RETURN[LOOPHOLE[LOOPHOLE[n, Basics.BytePair].low]]};
HighByte: PROC [n: CARDINAL] RETURNS [CHAR] = TRUSTED INLINE
{RETURN[LOOPHOLE[LOOPHOLE[n, Basics.BytePair].high]]};
END.