3.4 Basic operations on entities and relationships
In this section, we describe the basic operations on entities and relationships; we defer the operations on domains and relations to the next section.
A number of error conditions are common to all of the procedures in this section. Since values are represented as REF ANYs, all type checking must currently be done at run-time. The procedures in this section indicate illegal arguments by generating the errors IllegalAttribute, IllegalDomain, IllegalRelation, IllegalValue, IllegalEntity, and IllegalRelship, according to the type of argument expected. The error NILArgument is generated if NIL is passed to any procedure that cannot accept NIL for that argument. The error NullifiedArgument is generated if an entity or relationship is passed in after it has been deleted or rendered invalid by transaction abort or close.
DeclareEntity: PROC[
d: Domain, name: ROPE← NIL, version: Version← NewOrOld]
RETURNS [e: Entity];
DeclareEntity finds or creates an entity in domain d with the given name. The name may be omitted if desired, in which case an entity with a unique name is automatically created. If version is OldOnly and an entity with the given name does not exist, NIL is returned. If version is NewOnly and an entity with the given name already exists, the signal NonUniqueEntityName is generated.
DeclareRelship: PROC [
r: Relation, avl: AttributeValueList← NIL, version: Version← NewOrOld]
RETURNS [Relship];
DeclareRelship finds or creates a relship in r with the given attribute values. If version is NewOnly, a new relship with the given attribute values is generated. If version is OldOnly, the relship in r with the given attribute values is returned if it exists, otherwise NIL is returned. If version is NewOrOld, the relship with the given attribute values is returned if it exists, otherwise one is created. If the creation of a new relship violates the key constraints specified by DeclareAttribute, the signal NonUniqueAttributeValue is generated.
DestroyEntity: PROC[e: Entity];
DestroyEntity removes e from its domain, destroys all relationships referencing it, and destroys the entity representative itself. Any client variables that reference the entity automatically take on the null value (Null[e] returns TRUE), and cause error NullifiedArgument if passed to database system procedures. After an entity is destroyed, its old name may be re-used in creating a new one.
DestroyRelship: PROC[t: Relship];
DestroyRelship removes t from its relation, and destroys it. Any client variables that reference the relationship automatically take on the null value, and will cause error NullifiedArgument if subsequently passed to database system procedures.
SetF: PROC[t: Relship, a: Attribute, v: Value];
SetF assigns the value v to attribute a of relationship t. If the value is not of the same type as the attribute (or a subtype thereof if the attribute is entity-valued), then the error MismatchedAttributeValueType is generated. If a is not an attribute of t’s relation, IllegalAttribute is generated.
GetF: PROC[t: Relship, a: Attribute] RETURNS [Value];
GetF retrieves the value of attribute a of relationship t. If a is not an attribute of t’s relation, error IllegalAttribute is generated. The client should use the V2x routines described in the next section to coerce the value into the expected type.
If GetF is performed upon an attribute of a relationship whose value has never been assigned, a special undefined value is returned. The distinguished undefined value depends upon the type of the attribute. For example, for entity-valued attributes, it is NIL. For IntType attributes, it is the largest negative number. Clients of the database system should use the following procedures to deal with undefined values:
IsUndefined: PROC [v: Value] RETURNS [BOOL];
MakeUndefined: PROC[d: DataType] RETURNS [Value];
IsUndefined takes a value and indicates whether it is undefined, and MakeUndefined produces an undefined value of a particular type, suitable for use with SetF to make a previously defined attribute become undefined.
SetFS: PROC [t: Relship, a: Attribute, v: ROPE];
GetFS: PROC[t: Relship, a: Attribute] RETURNS [ROPE];
GetFS and SetFS provide a convenient veneer on top of GetF and SetF that provide the illusion that all relation attributes are string-valued. The effect is something like the Relational data model, and is useful for applications such as a relation displayer and editor that deal only with strings. The semantics of GetFS and SetFS depend upon the actual type of the value v of attribute a:
typeGetFS returnsSetFS assigns attribute to be
StringType
the string vthe string v
IntType
v converted to decimal stringthe string converted to decimal integer
BoolType
"TRUE" or "FALSE"true if "TRUE", false if "FALSE"
a domain D
the name of the ref’d entitythe entity with name v
(or null string if v is NIL)(or NIL if v is null string)
AnyDomainType
same, but includes domain:the entity with the given domain and name
<domain-name>:<entity-name>(or NIL if v is null string)
The same signals generated by GetF and SetF, such as IllegalAttribute, can also be generated by these procedures. The string NIL represents the undefined value. The signal NotFound is generated in the last case above if no entity with the given name is found.
NameOf: PROC [e: Entity] RETURNS [s: ROPE];
ChangeName: PROC [e: Entity, s: ROPE];
NameOf and ChangeName retrieve or change the name of an entity, respectively. They generate the signal IllegalEntity if e is not an entity.
ChangeName should be used with caution. It is not quite equivalent to destroying and re-creating an entity with the new name but the same existing relationships referencing it. ChangeName is considerably faster than that, and furthermore entity-valued variables which reference the entity are not nullified by ChangeName, though they would be by DestroyEntity. These features should be a help, not a hindrance. However, changing an entity name may invalidate references to the entity from outside the segment, e.g. in another segment or in some application-maintained file such as a log of updates.
DomainOf: PROC[e: Entity] RETURNS [Domain];
RelationOf: PROC[t: Relship] RETURNS [Relation];
DomainOf and RelationOf can be used to find the entity representative of an entity’s domain or a relationship’s relation, respectively. The signal IllegalEntity is generated if e is not an entity. The signal IllegalRelship is generated if r is not a relationship.
SegmentOf: PROC[e: Entity] RETURNS [Segment];
SegmentOf returns the segment in which an entity is stored. It can be applied to domain, relation, or attribute entities.
Eq: PROC [e1: Entity, e2: Entity] RETURNS [BOOL];
Eq returns TRUE iff the same database entity is referenced by e1 and e2. This is not equivalent to the Cedar expression "e1 = e2", which computes Cedar REF equality. If e1 and e2 are in different segments, Eq returns true iff they have the same name and their domains have the same name.
Null: PROC [x: EntityOrRelship] RETURNS [BOOL];
Null returns TRUE iff its argument has been destroyed, is NIL, or has been invalidated by abortion of the transaction under which it was created.
GetP: PROC [e: Entity, aIs: Attribute, aOf: Attribute← NIL] RETURNS [Value];
SetP: PROC [e: Entity, aIs: Attribute, v: Value, aOf: Attribute← NIL] RETURNS[Relship];
GetP and SetP are convenience routines for a common use of relationships, to represent "properties" of entities. Properties allow the client to think of values stored in relationships referencing an entity as if they are directly accessible fields (or "properties") of the entity itself. See the figure on page 15 illustrating properties. GetP finds a relationship whose from attribute is equal to e, and returns that relationship’s to attribute. The from attribute may be defaulted if the relation is binary, it is assumed to be the other attribute of to’s relation. If it is not binary, the current implementation will find the "first" other attribute, where "first" is defined by the order of the original calls calls to DeclareAttribute. SetP defaults the from attribute similarly to GetP, but operates differently depending on whether from is a key of the relation. Whether it is a key or not, any previous relationship that referenced e in the from attribute is automatically deleted. In either case, a new relationship is created whose from attribute is e and whose to attribute is v. SetP returns the relationship it creates for the convenience of the client. GetP and SetP can generate the same errors as SetF and GetF, e.g. if e is not an entity or to is not an attribute. In addition, GetP and SetP can generate the error IllegalProperty if to and from are from different relations. GetP generates the error MismatchedPropertyCardinality if more than one relationship references e in the from attribute; if no such relationships exist, it returns a null value of the type of the to attribute. SetP allows any number of existing relationships referencing e; it simply adds another one (when from is a key, of course, there will always be one relationship).
GetPList: PROC [e: Entity, to: Attribute, from: Attribute← NIL] RETURNS [LIST OF Value];
SetPList: PROC [e: Entity, to: Attribute, vl: LIST OF Value, from: Attribute← NIL];
GetPList and SetPList are similar to GetP and SetP, but they assume that any number of relationships may reference the entity e with their from attribute. They generate the signal MismatchedPropertyCardinality if this is not true, i.e. the from attribute is a key. GetPList returns the list of values of the to attributes of the relationships that reference e with their from attribute. Cedar has LISP-like list manipulation facilities. SetPList destroys any existing relationships that reference e with their from attribute, and creates Length[vl] new ones, whose from attributes reference e and whose to attributes are the elements of vl. GetPList and SetPList may generate any of the errors that GetF and SetF may generate, and the error IllegalProperty if to and from are from different relations.
Note that the semantics of SetPList are not quite consistent with the semantics of SetP. SetPList replaces the current values associated with a "property" with the new values (i.e., destroys and re-creates relationships); SetP adds a new property value, unless the aOf attribute is a key, in which case it replaces the current value. The semantics are defined in this way because this has proven the most convenient in our application programs.
Examples of the use of the property procedures for data access can be found in Section 4.3. Properties are also useful for obtaining information about the data schema. For example, GetP[a, aRelationIs] will return the attribute a’s relation, and GetPList[d, aTypeOf] will return all the attributes that can reference domain d.
E2V: PROC[e: Entity] RETURNS[v: Value];
B2V: PROC[b: BOOLEAN] RETURNS[v: Value];
I2V: PROC[i: LONG INTEGER] RETURNS[v: Value];
S2V: PROC[s: ROPE] RETURNS[v: Value];
The x2V routines convert the various Cedar types to Values. The conversion is not normally required for ropes and entities since the compiler will widen these into the REF ANY type Value.
V2E: PROC[v: Value] RETURNS[Entity];
V2B: PROC[v: Value] RETURNS[BOOLEAN];
V2I: PROC [v: Value] RETURNS[LONG INTEGER];
V2S: PROC [v: Value] RETURNS[ROPE];
The V2x routines convert Values to the various Cedar types. The MismatchedValueType error is raised if the value is of the wrong type. It is recommended that these routines be used rather than user-written NARROWs, as the representation of Values may change. Also, NARROWs of opaque types don’t yet work in the Cedar compiler.
3.5 Query operations on domains and relations
In this section we describe queries upon domains and relations: operations that enumerate entities or relationships satisfying some constraint.
RelationSubset: PROC[
r: Relation, constraint: AttributeValueList← NIL]
RETURNS [RelshipSet];
NextRelship: PROC[rs: RelshipSet] RETURNS [Relship];
PrevRelship: PROC[rs: RelshipSet] RETURNS [Relship];
ReleaseRelshipSet: PROC [rs: RelshipSet];
AttributeValueList: TYPE = LIST OF AttributeValue;
AttributeValue: TYPE = RECORD [
attribute: Attribute,
low: Value,
high: Value← NIL -- omitted where same as low or not applicable --];
The basic query operation is RelationSubset. It returns a generator of all the relationships in relation r which satisfy a constraint list of attribute values. The relationships are enumerated by calling NextRelship repeatedly; it returns NIL when there are no more relationships. PrevRelship may similarly be called repeatedly to back the enumeration up, returning the previous relationship; it returns NIL if the enumeration is at the beginning. ReleaseRelshipSet should be called when the client is finished with the query.
The constraint list may be NIL, in which case all of the relationships in r will be enumerated. Otherwise, relationships which satisfy the concatenation of constraints on attributes in the list will be enumerated. If an index exists on some subset of the attributes, the relationships will be enumerated sorted on the concatenated values of those attributes. For a StringType, IntType, or TimeType attribute a of r, the contraint list may contain a record of the form [a, b, c] where the attribute value must be greater or equal to b and less than or equal to c to satisfy the constraint. For any type of attribute, the list may contain a record of the form [a, b] where the value of the attribute must exactly equal b. The Cedar ROPE literals "" and "\377" may be used in queries as an infinitely large and infinitely small string, respectively. The signal MismatchedAttributeValueType is generated by RelationSubset if one of the low or high values in the list is of a different type than its corresponding attribute.
DomainSubset: PROC[
d: Domain,
lowName, highName: ROPE← NIL,
searchSubDomains: BOOL← TRUE,
searchSegment: Segment← NIL]
RETURNS [EntitySet];
NextEntity: PROC[EntitySet] RETURNS [Entity];
PrevEntity: PROC[EntitySet] RETURNS [Entity];
ReleaseEntitySet: PROC[EntitySet];
DomainSubset enumerates all the entities in a domain. If lowName and highName are NIL, the entire domain is enumerated, in no particular order. Otherwise, only those entities whose names are lexicographically greater than lowName and less than highName are enumerated, in lexicographic order. If searchSubDomains is TRUE, subdomains of d are also enumerated. Each subdomain is sorted separately. The searchSegment argument is currently only used if d is one of the system domains, e.g. the Domain domain. It is used to specify which segment to search.
Analogously to relation enumeration, NextEntity and PrevEntity may be used to enumerate the entities returned by DomainSubset, and ReleaseEntitySet should be called upon completion.
GetDomainRefAttributes: PROC [d: Domain] RETURNS [AttributeList];
This procedure returns a list of all attributes, of any relation defined in d’s segment, which reference domain d or one of its superdomains. The list does not include AnyDomainType attributes, which can reference any domain. GetDomainRefAttributes is implemented via queries on the data schema. GetDomainRefAttributes is useful for application-independent tools; most specific applications can code-in the relevant attributes.
GetEntityRefAttributes: PROC [e: Entity] RETURNS [AttributeList];
This procedure returns a list of all attributes in which some existing relationship actually references e, including AnyDomainType attributes.
3.6 System domains and relations
In this section we describe what one might call the schema schema, the pre-defined system domains and relations which constitute the data schema for client-defined domains and relations. The typical database application writer may skip this section, since the schema declaration operations defined in Section 3.3 are adequate when the data schema is completely defined and known at the time a program is written. The system domains and relations we describe in this section are most useful for general-purpose tools (e.g. for displaying, querying, or dumping any database), where the tools must examine the data schema "on the fly".
As noted earlier, the permanent repository for data describing user-defined data in a database is the database’s data schema, represented by schema entities and relationships. Schema entities are members of one of the pre-defined system domains: DomainDomain, RelationDomain, DatatypeDomain, and so on. Every client-defined domain, relation, or attribute contains a representative entity in these domains. Client-defined datatypes are not currently permitted, so the only entities in the DataType domain are the pre-defined IntType, StringType, and BoolType.
The information about the client-defined domains and attributes are encoded by relationships in the database. Domains participate in the system relation dSubType, which encodes a domain type hierarchy:
dSubType: Relation;
dSubTypeOf: Attribute; -- the domain in this attribute is a super-type of
dSubTypeIs: Attribute; -- the domain in this attribute
The dSubType has one element per direct domain-subdomain relationship, it does not contain the transitive closure of that relation. However, it is guaranteed to contain no cycles. That is, the database system checks that there is no set of domains d1, d2, ... dN, N>1, such that d1 is a subtype of d2, d2 is a subtype of d3, and so on to dN, and d1=dN. The dSubType may define a lattice as opposed to a tree, i.e. the sSubType attribute is not a key of the relation.
The information about attributes is encoded as binary relations, one relation for each argument to the DeclareAttribute procedure defining properties of the attribute. The names are easy to remember; for each argument, e.g. Foo, we define the aFoo relation, with attributes aFooOf and aFooIs. The aFooIs attribute is the value of that argument, and the aFooOf attribute is [the entity representative of] the attribute it pertains to. Thus we have the following relations:
aRelation: PUBLIC READONLY Relation; -- Specifies attribute - relation correspondence:
-- [aRelationOf: KEY Attribute, aRelationIs: Relation]
aRelationOf: PUBLIC READONLY Attribute; -- attribute whose relation we are specifying
aRelationIs: PUBLIC READONLY Attribute; -- the relation of that attribute

aType: PUBLIC READONLY Relation; -- Specifies types of relation attributes:
-- [aTypeOf: KEY Attribute, aTypeIs: ValueType]
aTypeOf: PUBLIC READONLY Attribute; -- the attribute
aTypeIs: PUBLIC READONLY Attribute; -- domain or datatype of the attribute

aUniqueness: PUBLIC READONLY Relation; -- Specifies attribute value uniqueness:
-- [aUniquenessOf: KEY Attribute, aUniquenessIs: INT LOOPHOLE[Uniqueness]]
aUniquenessOf: PUBLIC READONLY Attribute; -- the attribute
aUniquenessIs: PUBLIC READONLY Attribute; -- INT for Uniqueness: 0=None, 1=Key, etc.

aLength: PUBLIC READONLY Relation; -- Specifies length of attributes:
-- [aLengthOf: KEY Attribute, aLengthIs: INT]
aLengthOf: PUBLIC READONLY Attribute; -- the attribute
aLengthIs: PUBLIC READONLY Attribute; -- INT corresponding to attribute’s length

aLink: PUBLIC READONLY Relation; -- Specifies whether attribute is linked:
-- [aLinkOf: KEY Attribute, aLinkIs: INT]
aLinkOf: PUBLIC READONLY Attribute; -- the attribute
aLinkIs: PUBLIC READONLY Attribute; -- 0=unlinked, 1=linked, 2 =colocated
The final set of system relations pertain to index factors. Each index on a relation is defined to include one or more attributes of a relation. For each attribute in the index, there is an index factor entity. For each index, there is an index entity. Each index factor is associated with exactly one index and exactly one attribute. Indices may have many index factors, however, and an attribute may be associated with more than one index factor, since attributes may participate in multiple indices. The two relations pertaining to indices map indices on to their index factors, and index factors to the attributes they index:
ifIndex: PUBLIC READONLY Relation; -- Specifies the index factors for each index
-- [ifIndexOf: KEY IndexFactor, ifIndexIs: Index]
ifIndexOf: PUBLIC READONLY Attribute; -- the index factor
ifIndexIs: PUBLIC READONLY Attribute; -- index of the factor

ifAttribute: PUBLIC READONLY Relation; -- Specifies attribute index factor corresponds to
-- [ifAttributeOf: KEY IndexFactor, ifAttributeIs: Attribute]
ifAttributeOf: PUBLIC READONLY Attribute; -- the index factor
ifAttributeIs: PUBLIC READONLY Attribute; -- the attribute this factor represents
The relations on attributes, index factors, and domains can be queried with the RelationSubset or GetPList operations. For example, GetP[a, aRelationIs] returns the attribute a’s relation. GetPList[r, aRelationOf] returns the relation r’s attributes. RelationSubset[dSubType, LIST[[dSubTypeIs, d]]] will enumerate all the dSubType relationships in which d is the subtype.
As noted earlier, the data schema (attributes, relations, domains, indices, index factors, and relations pertaining to these) may only be read, not written by the database client. In order to ensure the consistency of the schema, it must be written indirectly through the schema definition procedures: DeclareDomain, DeclareRelation, DeclareAttribute, and DeclareSubType. Attempts to perform updates through operations such as SetP result in the error ImplicitSchemaUpdate.
3.7 Errors
The Cedar language provides a SIGNAL mechanism for returning control to the caller of a procedure when an exceptional condition is identified. When a database system operation invokes an error, the SIGNAL Error is generated, with an error code indicating the type of error that occured. The error code is a Cedar "enumerated type":
Error: SIGNAL [code: ErrorCode];
ErrorCode: TYPE = {
AlreadyExists, -- Entity already exists and client said version=NewOnly
BadUserPassword, -- On an OpenTransaction
DatabaseNotInitialized, -- Attempt to do operation without calling Initialize
FileNotFound, -- No existing segment found with given name
IllegalAttribute, -- Attribute not of the given relship’s Relation or not an attribute
IllegalValueType, -- Type passed DeclareAttribute is not datatype or domain
IllegalDomain, -- Argument is not actually a domain
IllegalFileName, -- No directory or machine given for segment
IllegalEntity, -- Argument to GetP, or etc., is not an Entity
IllegalRelship, -- Argument to GetF, or etc., is not a Relship
IllegalRelation, -- Argument is not a relation
IllegalSegment, -- Segment passed to DeclareDomain, or etc., not yet declared
IllegalString, -- Nulls not allowed in ROPEs passed to the database system
IllegalSuperType, -- Can’t define subtype of domain that already has entities
IllegalValue, -- Value is not REF INT, ROPE, REF BOOL, or Entity
IllegalValueType, -- Type passed DeclareAttribute is not datatype or domain
ImplicitSchemaUpdate, -- Attempt to modify schema with SetP, DeclareEntity, etc.
InternalError, -- Impossible internal state (possibly bug or bad database)
MismatchedProperty, -- aOf and aIs attribute not from the same relation
MismatchedAttributeValueType, -- Value not same type as required (SetF)
MismatchedExistingAttribute, -- Existing attribute is different (DeclareAttribute)
MismatchedExistingSegment, -- Existing segment is different (DeclareSegment)
MismatchedPropertyCardinality, -- Did GetP with aOf that is not a Key
MismatchedSegment, -- Attempt to create ref across segment boundary (SetF)
MismatchedValueType, -- value passed V2E, V2I, etc. not of expected type
MultipleMatch, -- More than one relationship satisfied avl on DeclareRelship.
NonUniqueEntityName, -- Entity in domain with that name already exists
NonUniqueKeyValue, -- Relship already exists with that value
NotFound, -- Version is OldOnly but no such Entity, Relation, or etc found
NotImplemented, -- Action requested is not yet implemented
NILArgument, -- Attempt to perform operation on NIL argument
NullifiedArgument, -- Entity or relationship has been deleted or invalidated
ProtectionViolation, -- Read or write to segment not permitted this user.
SegmentNotDeclared, -- Attempt to open transaction w/o DeclareSegment
ServerNotFound -- File server does not exist or does not respond
};
In this report, the expression "generates the error X" means that the SIGNAL Error is generated with code=X. Unless otherwise specified, the client may CONTINUE from the signal, aborting the operation in question. Signals should not be RESUMEd except by a wizard who knows the result of proceeding with an illegal operation.