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
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 CARDINAL← LOOPHOLE[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:
BOOL←
TRUE, 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]]};