Page Numbers: Yes X: 530 Y: 10.5" First Page: 113
Columns: 1 Edge Margin: .6" Between Columns: .4"
Margins: Top: 1.3" Bottom: 1"
Line Numbers: No Modulus: 5 Page-relative
Even Heading:
DESIGN AND IMPLEMENTATION OF A RELATIONSHIP-ENTITY-DATUM DATA MODEL
Odd Heading: Not-on-first-page
APPENDIX
10. Appendix: Axioms and formal definitions
The definition of a database includes a set of domains D (the domain domain), a set of relations R (the relation domain), a set of attributes A (the attribute domain), and a set of datatypes T (the datatype domain). We also define a subdomain function sd mapping D to D. The function sd must define a partial ordering of D.
We define the fundamental type constraint for a database:
The Attribute Type Constraint. (a) Every relationship in a relation R must have the same number of elements. We assign identifiers for corresponding elements of the relationships belonging to R, the attributes a1, a2, a3, ... aN. (b) For any of these attributes ai, the value associated with ai for every relationship in R must be an element of the same datatype or domain di, or of a domain si in the transitive closure of sd applied to di.
Because every relationship in a relation R has the same attributes, we may unambiguously refer to these as simply R’s attributes. Every relationship in R has a value of the same type associated with an attribute A, so we define a function at mapping A to the union of D and T, and call at(A) A’s type.
Relation keys
Each data schema includes a uniqueness function au mapping A to the set {none, keypart}. If a relation R has M attributes, of which some subset a1, a2, ... aN (where N < M) have au(ai) = keypart, then no more than one relationship in R is permitted to have the same value associated with attributes a1, a2, ... aN..
Normalization
For a relation R of order N, denote the predicate whose truth in the represented world corresponds to the presence of a relationship in the relation by Rp[a1, a2, ... aN]. The arguments ai correspond to the attributes of R. We then define:
A relation R is in irreducible form iff it cannot be split into relations R1, R2, ... Rk such that for all possible states of the represented world:
Rp[x1, x2, ... xN] <-> R1p[xi1, ... xik] & ... & Rkp[xj1, ... xjl]
where
{xi1, ... xik} I {x1, x2, ... xN}
...
{xj1, ... xjl} I {x1, x2, ... xN}
A relation is in functionally irreducible form iff (1) it is in irreducible form or (2) it is the natural relational join on a key attribute of two or more binary relations, and there is a dependency between the other (non-joined) attributes a1, a2, ... aN such that the semantics of attribute ai is dependent upon the value of ai+1, for i = 1, 2, .. N-1.
Axioms
Below are the beginnings of an equational specification of the DB interface developed by Jim Donahue of the Computer Science Lab. Currently missing are complete descriptions of the schema operations (DeclareDomain, DeclareRelation, DeclareAttribute, . . .), some of the detailed description of the behavior of operations on erroneous arguments (the type-checking of RelationSubset, for instance), and any discussion of segments, transactions and initialization. A complete description will appear in a forthcoming paper.
In this style of specification, we present the semantics of the operations in terms of behaviorally equivalent programs composed of sequences of Cypress operations. Since the only way you can interrogate entities is through DomainSubset, the only important property of DeclareEntity is that it may affect the value of a later invocation of DomainSubset. The equations specify this by stating that certain sequences of Cedar statements are equivalent.
In the following, we will use the abbreviation
(s1; . . .; sn) THEN e1 ~ e2
where s1, ..., sn are Cedar statements and e1 and e2 are Cedar expressions to mean that
{ s1; ...; sn; RETURN[e1] } ~ { s1; ...; sn; RETURN[e2] }
(Quite often in what follows, the equivalences will be between Cedar procedure bodies; if Cedar were an expression language then the equivalences would be somewhat simpler to state.)
Basic Operations on Entities
The fundamental property of entities is that no two entities with the same name and same domain can exist (this is not true for relationships). The operations that manipulate entities include:
DeclareEntity -- add (or look up) an entity in a particular domain
DestroyEntity -- remove an entity from a domain
NameOf -- give the name of an entity
DomainOf -- give the domain of an entity
Eq -- compare two entities
Null -- has an entity been destroyed?
DomainSubset -- produce the subset of a domain with names in a given range
NextEntity -- sequence through an entity set
ReleaseEntitySet -- free the storage used by entity set (affects performance only)
DeclareEntity
DeclareEntity
The basic operation on entities is DeclareEntity, which either creates a new entity in a particular domain or returns a previously existing one. Its semantics is given in the following equations:
1. Declare produces an entity with the right name and domain:
(e: Entity = DeclareEntity[d, name, NewOnly]) THEN
[ DomainOf[e], NameOf[e] ] ~ [ d, name ]
2. NewOrOld is only a convenience:
DeclareEntity[d, name, NewOrOld] ~
{ e: Entity = DeclareEntity[d, name, OldOnly];
IF e = NIL THEN RETURN[DeclareEntity[d, name, NewOnly]] ELSE RETURN[e] }
3. Declare OldOnly is the same as domain subset:
DeclareEntity[d, name, OldOnly] ~
{ es: EntitySet = DomainSubset[d, name];
e: Entity = NextEntity[es]; ReleaseEntitySet[es]; RETURN[e] }
4. Declare NewOnly fails if the entity already exists:
IF DeclareEntity[d, name, OldOnly] # NIL THEN DeclareEntity[d, name, NewOnly] ~
IF DeclareEntity[d, name, OldOnly] # NIL THEN SIGNAL Error[AlreadyExists]
5. Declare doesn’t work on system domains:
DeclareEntity[DomainDomain, name] ~ SIGNAL Error[ImplicitSchemaUpdate]
DeclareEntity[RelationDomain, name] ~ SIGNAL Error[ImplicitSchemaUpdate]
DeclareEntity[AttributeDomain, name] ~ SIGNAL Error[ImplicitSchemaUpdate]
DeclareEntity[DataTypeDomain, name] ~ SIGNAL Error[ImplicitSchemaUpdate]
DeclareEntity[IndexDomain, name] ~ SIGNAL Error[ImplicitSchemaUpdate]
DeclareEntity[IndexFactorDomain, name] ~ SIGNAL Error[ImplicitSchemaUpdate]
6. DeclareEntity doesn’t work for NIL or null arguments:
DeclareEntity[NIL, name] ~ SIGNAL Error[NILArgument]
IF Null[d] THEN DeclareEntity[d, name] ~
IF Null[d] THEN SIGNAL Error[NullifiedArgument]
DestroyEntity
Declare may put something in the database; Destroy removes it.
1. An entity that is destroyed will not be found:
( d: Domain = DomainOf[e]; name: ROPE = NameOf[e]; DestroyEntity[e] ) THEN
DeclareEntity[d, name, OldOnly] ~ NIL
2. All relships that reference an entity are destroyed when the entity is destroyed:
(SetF[rship, attr, e]; DestroyEntity[e]) THEN Null[rship] ~ TRUE
3. Once destroyed, an entity becomes Null:
(DestroyEntity[e]) THEN Null[e] ~ TRUE
(DestroyEntity[e1]) THEN Eq[e1, e2] ~ Null[e2]
4. Destroy doesn’t work on NIL or null arguments (can’t destroy the same entity twice):
DestroyEntity[NIL] ~ SIGNAL Error[NILArgument]
IF Null[e] THEN DestroyEntity[e] ~
IF Null[e] THEN SIGNAL Error[NullifiedArgument]
DomainSubset
DomainSubset returns an EntitySet that, if cycled through using NextEntity, contains all of the entities in the domain having names in the specified range, and only those entities.
1. DomainSubset finds only those entities having names between low and high:
( es: EntitySet = DomainSubset[d, low, high];
e: Entity = DeclareEntity[d, name, NewOnly] ) THEN
DomainSubset[d, low, high] ~
IF name < low OR name > high THEN es ELSE DomainSubset[d, low, high]
2. DomainSubset finds all those entities having names between low and high:
( e: Entity = DeclareEntity[d, name, NewOnly] ) THEN
name >= low AND name <= high ~
{ es: EntitySet = DomainSubset[d, low, high];
FOR next: Entity ← NextEntity[es], NextEntity[es] UNTIL next = NIL DO
IF Eq[next, e] THEN {ReleaseEntitySet[es]; RETURN[TRUE]}
ENDLOOP;
ReleaseEntitySet[es]; RETURN[FALSE] }
Equality of entities
Two entities are equal iff they are both null or their names are the same and they are in the same domain.
Eq[e1, e2] ~
{ IF Null[e1] OR Null[e2] THEN RETURN[ Null[e1] AND Null[e2] ];
RETURN[ Rope.Equal[NameOf[e1], NameOf[e2]] AND
Eq[DomainOf[e1], DomainOf[e2]]] }
Basic Operations on Relationships
Relationships are tuples; the basic operations on relationships include setting and fetching the values of fields and searching the database for all tuples whose fields have values in specified ranges. The operations that manipulate relationships include:
DeclareRelship -- add (or look up) a relationship in a particular relation
DestroyRelship -- remove a relationship from a relation
RelationOf -- give the relation of a relationship
Eq -- compare two relationships
Null -- has a relationship been destroyed?
RelationSubset -- produce the subset of a relation with fields in a given range of values
NextRelship -- sequence through a relationship set
ReleaseEntitySet -- free storage used by a relationship set (affects performance only)
Unlike entities, it is possible to have the several tuples in the database with the same set of field values. (One can protect against this by declaring some of the attributes for a relation to be keys. Then checking is performed when declaring new relationships or setting the fields of existing relationships to guarantee that no other tuple exists with the same value for a key attribute. The equations given below ignore this additional complexity.) Allowing multiple tuples with the same values appears in the lack of an equation of the form "DeclareRelship followed by DeclareRelship with the same arguments and version OldOnly fails."
DeclareRelship
Most of these equations closely parallel the specification of DeclareEntity.
1. Declare produces a relationship with the proper values for each specified field:
( init: AttributeValueList = LIST[ [a1, v1], ..., [an, vn] ];
rs: Entity = DeclareRelship[r, init] ) THEN GetF[ rs, ai ] ~ vi
2. Declare NewOrOld is a convenience:
DeclareRelship[r, list, NewOrOld] ~
{ rs: DeclareRelship[r, list, OldOnly];
IF rs = NIL THEN RETURN[DeclareRelship[r, list, NewOnly]] ELSE RETURN[rs] }
3. Again, Declare OldOnly is the same as the subset operation this time, though, it
may fail because of a multiple match:
DeclareRelship[r, list, OldOnly] ~
{ rset: RelshipSet = RelationSubset[r, list];
rs: Relship = NextRelship[rs];
IF NextRelship[rset] = NIL THEN { ReleaseRelshipSet[rs]; RETURN[rs] }
ELSE { ReleaseRelshipSet[rset]; SIGNAL Error[MultipleMatch] } }
4. Declare doesn’t work on system domains:
DeclareRelship[aRelation, list] ~ SIGNAL Error[ImplicitSchemaUpdate]
DeclareRelship[aType, list] ~ SIGNAL Error[ImplicitSchemaUpdate]
DeclareRelship[aUniqueness, list] ~ SIGNAL Error[ImplicitSchemaUpdate]
DeclareRelship[aLength, list] ~ SIGNAL Error[ImplicitSchemaUpdate]
DeclareRelship[aLink, list] ~ SIGNAL Error[ImplicitSchemaUpdate]
DeclareRelship[dSubType, list] ~ SIGNAL Error[ImplicitSchemaUpdate]
5. DeclareEntity doesn’t work for NIL or null arguments:
DeclareRelship[NIL, list] ~ SIGNAL Error[NILArgument]
IF Null[r] THEN DeclareRelship[r, list] ~
IF Null[r] THEN SIGNAL Error[NullifiedArgument]
DestroyEntity
Because duplicates are allowed, the effect of Destroy is somewhat different. Declaring a relship NewOnly and destroying it has no effect if the relation is in the schema (note that the initialization of the fields of the relship is described below as applications of SetF).
1. Destroying a newly created relationship has no effect:
{ rs: Relship = DeclareRelship[r, [], NewOnly]; DestroyRelship[rs] } ~
[] ← DeclareRelation[ NameOf[r], SegmentOf[r], OldOnly ]
2. If an existing relationship is destroyed, then it will not be found:
( rs: Relship = DeclareRelship[r, list, OldOnly]; DestroyRelship[rs] ) THEN
DeclareRelship[r, list, OldOnly] ~ NIL
3. Once destroyed, an relationship becomes Null:
(DestroyRelship[rs]) THEN Null[rs] ~ TRUE
(DestroyRelship[rs1]) THEN Eq[rs1, rs2] ~ Null[rs2]
4. Destroy doesn’t work on NIL or null arguments (can’t destroy the same entity twice):
DestroyRelship[NIL] ~ SIGNAL Error[NILArgument]
IF Null[rs] THEN DestroyRelship[rs] ~
IF Null[rs] THEN SIGNAL Error[NullifiedArgument]
RelationSubset
RelationSubset returns a RelshipSet that, if cycled through using NextRelship, contains all of the relationships in the relation having field values in the specified ranges.
1. RelationSubset finds only relships satisfying the AttributeValueList:
( init: AttributeValueList = LIST[ [a1, v1], . . ., [an, vn] ]
search: AttributeValueList = LIST[ [a1, l1, h1], . . ., [an, ln, hn] ];
rset: RelshipSet = RelationSubset[ r, search ];
[] ← DeclareRelship[r, init, NewOnly ] ) THEN
RelationSubset[ r, search ] ~
IF v1 < l1 OR v1 > h1 OR . . . vm < lm OR vm > hm
THEN rset ELSE RelationSubset[ r, search ]
2. And RelationSubset finds all relships satisfying the AttributeValueList:
( init: AttributeValueList = LIST[ [a1, v1], . . ., [an, vn] ]
search: AttributeValueList = LIST[ [a1, l1, h1], . . ., [an, ln, hn] ];
rs: DeclareRelship[r, init, NewOnly];
rset: RelshipSet = RelationSubset[ r, search ] ) THEN
v1 >= l1 AND v1 <= h1 AND . . . vm >= lm AND vm <= hm ~
{ FOR next: Relship ← NextRelship[rs], NextRelship[rs] UNTIL next = NIL DO
IF Eq[ next, rs ] THEN { ReleaseRelshipSet[rs]; RETURN[TRUE] }
ENDLOOP;
ReleaseRelshipSet[rs]; RETURN[FALSE] }
SetF and GetF
SetF changes the fields of a tuple, GetF reads them. Here are also the axioms that describe the type-checking performed on assignment.
1. Setting the field of one relship changes its value for all equal relships (Eq for relships is essentially alias):
{ SetF[rship1, attr, val]; RETURN[ GetF[rship2, attr] ] } ~
{ oldVal: Value = GetF[rship2, attr];
SetF[rship1, attr, val];
RETURN[ IF Eq[rship1, rship2 ] THEN val ELSE oldVal ] }
2. Setting a field with its current value is a no-op (except for errors):
SetF[ rship, attr, GetF[rship, attr] ] ~
{ IF Eq[RelationOf[rship], aRelation] THEN SIGNAL Error[ImplicitSchemaUpdate];
IF Eq[RelationOf[rship], aType] THEN SIGNAL Error[ImplicitSchemaUpdate];
IF Eq[RelationOf[rship], aLength] THEN SIGNAL Error[ImplicitSchemaUpdate];
IF Eq[RelationOf[rship], aUniqueness] THEN SIGNAL Error[ImplicitSchemaUpdate];
IF Eq[RelationOf[rship], aLink] THEN SIGNAL Error[ImplicitSchemaUpdate];
IF Eq[RelationOf[rship], ifIndex] THEN SIGNAL Error[ImplicitSchemaUpdate];
IF Eq[RelationOf[rship], ifAttribute] THEN SIGNAL Error[ImplicitSchemaUpdate];
[] ← GetF[rship, attr] }
3. Declare NewOnly can be defined in terms of setting the fields one at a time:
DeclareRelship[ r, LIST[ [a1, v1], . . ., [an, vn] ], NewOnly ] ~
{ rship: Relship = DeclareRelship[ r, [], NewOnly ];
SetF[ rship, a1, v1 ]; . . .; SetF[ rship, an, vn ]; RETURN[rship] }
4. GetF fails if the attribute belongs to the wrong relation:
[] ← GetF[rship, attr] ~
{ IF rship = NIL OR attr = NIL THEN SIGNAL Error[NILArgument];
IF Null[rship] OR Null[attr] THEN SIGNAL Error[NullifiedArgument];
{ rel1: Relation = RelationOf[rship];
rel2: Relation = V2E[ GetP[ e: attr, aIs: aRelationIs, aOf: aRelationOf ] ];
IF NOT Eq[rel1, rel2] THEN SIGNAL Error[ IllegalAttribute ] } }
The type-checking that goes on when a SetF is performed is fairly straightforward: If the value to be assigned is from one of the primitive types, then the type of the attribute (found in the aType relation) must be identical. In the case of assigning entity values, the domain of the value must be a subtype of the domain that is the type of the attribute. We define this by a set of similar equations for each of the primitive types and one for the entity-valued case.
5. Integer type-checking:
{ rship: Relship = DeclareRelship[r, [], NewOnly];
SetF[ rship, attr, I2V[integer] ]; DestroyRelship[rship] } ~
{ [] ← GetF[rship, attr];
{ type: Entity = V2E[ GetP[ e: attr, aIs: aTypeIs, aOf: aTypeOf ] ];
IF NOT Eq[type, IntType] THEN SIGNAL Error[MismatchedAttributeValueType] }}
6. Boolean type-checking:
{ rship: Relship = DeclareRelship[r, [], NewOnly];
SetF[ rship, attr, B2V[boolean] ]; DestroyRelship[rship] } ~
{ [] ← GetF[rship, attr];
{ type: Entity = V2E[ GetP[ e: attr, aIs: aTypeIs, aOf: aTypeOf ] ];
IF NOT Eq[type, BoolType] THEN SIGNAL Error[MismatchedAttributeValueType] }}
7. String type-checking:
{ rship: Relship = DeclareRelship[r, [], NewOnly];
SetF[ rship, attr, S2V[string] ]; DestroyRelship[rship] } ~
{ [] ← GetF[rship, attr];
{ type: Entity = V2E[ GetP[ e: attr, aIs: aTypeIs, aOf: aTypeOf ] ];
IF NOT Eq[type, StringType] THEN SIGNAL Error[MismatchedAttributeValueType] }}
8. Time type-checking:
{ rship: Relship = DeclareRelship[r, [], NewOnly];
SetF[ rship, attr, T2V[time] ]; DestroyRelship[rship] } ~
{ [] ← GetF[rship, attr];
{ type: Entity = V2E[ GetP[ e: attr, aIs: aTypeIs, aOf: aTypeOf ] ];
IF NOT Eq[type, TimeType] THEN SIGNAL Error[MismatchedAttributeValueType] }}
9. Entity type-checking (AnyDomainType is a wild card):
{ rship: Relship = DeclareRelship[r, [], NewOnly];
SetF[ rship, attr, E2V[entity] ]; DestroyRelship[rship] } ~
{ [] ← GetF[rship, attr];
{ type: Entity = V2E[ GetP[ e: attr, aIs: aTypeIs, aOf: aTypeOf ] ];
IF NOT (Eq[type, AnyDomainType] OR SubType[DomainOf[entity], type])
THEN SIGNAL Error[MismatchedAttributeValueType] }}
RelationOf
RelationOf simply gives the relation of a relationship.
RelationOf[ DeclareRelship[ r, list ], NewOrOld ] ~
{ [] ← DeclareRelship[ r, list, NewOrOld ]; RETURN[r] }
Operations on the database schema
The database schema is stored in a set of system domains and relations. The operations on these objects include:
DeclareDomain -- add (or look up) a domain
DeclareRelation -- add (or look up) a relation
DeclareAttribute -- add (or look up) a field name of a relation
DestroyDomain -- destroy a domain (and all of its entities)
DestroyRelation -- destroy a relation (and all of its relationships)
DeclareSubType -- define one domain to be a subtype of another
DestroySubType -- remove the two domains from the dSubType relation
These operations behave much like DeclareEntity, DeclareRelship, DestroyEntity and DestroyRelship; it’s just that they work on the system domains and relations.
DeclareDomain/DestroyDomain
1. DeclareDomain adds a new (empty) domain:
( d: Domain = DeclareDomain[ name, segment, NewOnly ] ) THEN
{ es: EntitySet = DomainSubset[d, NIL];
e: Entity = NextEntity[es];
ReleaseEntitySet[es]; RETURN[s] } ~ NIL
2. Again, NewOrOld is a convenience:
DeclareDomain[name, segment, NewOrOld] ~
{ d: Domain = DeclareDomain[name, segment, OldOnly];
IF d = NIL THEN RETURN[DeclareDomain[name, segment, NewOnly]]
ELSE RETURN[d] }
3. Declare OldOnly is the same as domain subset on DomainDomain:
DeclareDomain[name, segment, OldOnly] ~
{ es: EntitySet = DomainSubset[d: DomainDomain,
lowName: name, searchSegment: segment];
e: Entity = NextEntity[es]; ReleaseEntitySet[es]; RETURN[e] }
4. Declare NewOnly fails if the entity already exists:
([] ← DeclareDomain[name, segment, NewOrOld]) THEN
DeclareDomain[name, segment, NewOnly] ~ SIGNAL Error[AlreadyExists]
5. Declare NewOnly changes DomainDomain:
(d: Domain = DeclareDomain[name, segment, NewOnly]) THEN
DeclareDomain[name, segment, OldOnly] ~ d
6. A domain that is destroyed will not be found:
(name: ROPE = NameOf[d]; seg: Segment = SegmentOf[d]; DestroyDomain[d]) THEN
DeclareDomain[name, segment, OldOnly] ~ NIL
7. Destroying a domain causes all of the entities in the domain to be destroyed:
( es: EntitySet = DomainSubset[d, NIL]; DestroyDomain[d] ) THEN
{ e: Entity = NextEntity[es]; ReleaseEntitySet[es]; RETURN[e] } ~ NIL
8. Destroying a domain causes entities to become Null:
( e: Entity = DeclareEntity[d, name]; DestroyDomain[d] ) THEN
Null[e] ~ TRUE
9. Destroying a domain causes relationships that reference it to be destroyed:
( SetF[rship, attr, e]; DestroyDomain[d] ) THEN Null[rship] ~ TRUE
10. Destroy doesn’t work on NIL or null arguments (can’t destroy the domain twice):
DestroyDomain[NIL] ~ SIGNAL Error[NILArgument]
IF Null[d] THEN DestroyDomain[d] ~
IF Null[d] THEN SIGNAL Error[NullifiedArgument]
DeclareRelation/DestroyRelation
1. DeclareRelation adds a new (empty) relation:
( r: Relation = DeclareRelation[ name, segment, NewOnly ] ) THEN
{ rs: RelshipSet = RelationSubset[r, NIL]; rship: Relation = NextRelship[rs];
ReleaseRelshipSet[rs]; RETURN[rship] } ~ NIL
2. Again, NewOrOld is a convenience:
DeclareRelation[name, segment, NewOrOld] ~
{ r: Relation = DeclareRelation[name, segment, OldOnly];
IF r = NIL THEN RETURN[DeclareRelation[name, segment, NewOnly]]
ELSE RETURN[r] }
3. Declare OldOnly is the same as domain subset on RelationDomain:
DeclareRelation[name, segment, OldOnly] ~
{ es: EntitySet = DomainSubset[d: RelationDomain,
lowName: name, searchSegment: segment];
e: Entity = NextEntity[es]; ReleaseEntitySet[es]; RETURN[e] }
4. Declare NewOnly fails if the entity already exists:
([] ← DeclareRelation[name, segment, NewOrOld]) THEN
DeclareRelation[name, segment, NewOnly] ~ SIGNAL Error[AlreadyExists]
5. Declare NewOnly changes RelationDomain:
(r: Relation = DeclareRelation[name, segment, NewOnly]) THEN
DeclareRelation[name, segment, OldOnly] ~ r
6. A relation that is destroyed will not be found:
(name: ROPE = NameOf[r]; seg: Segment = SegmentOf[r]; DestroyDomain[r]) THEN
DeclareRelation[name, segment, OldOnly] ~ NIL
7. Destroying a domain causes all of the relships in the domain to be destroyed:
( rs: RelshipSet = RelationSubset[r, NIL]; DestroyRelation[r] ) THEN
{ rship: Relation = NextRelship[rs]; ReleaseRelshipSet[rs]; RETURN[rship] } ~ NIL
8. Destroying a domain causes relships to become Null:
( rship: Relation = DeclareRelship[r, [], NewOnly]; DestroyRelation[r] ) THEN
Null[rship] ~ TRUE
9. Destroy doesn’t work on NIL or null arguments (can’t destroy the relation twice):
DestroyRelation[NIL] ~ SIGNAL Error[NILArgument]
IF Null[r] THEN DestroyRelation[r] ~ IF Null[r] THEN SIGNAL Error[NullifiedArgument]
DeclareAttribute
The important properties of DeclareAttribute are the changes made to the system relations aType, aLength, aUniqueness and aLink when a new attribute is declared. If a type, length, uniqueness or link is specified when the attribute is looked up (Declare with version OldOnly), then these additional arguments are checked for consistency with the values stored in the relations. Note that there is no corresponding DestroyAttribute.
1. DeclareAttribute adds a new attribute:
( a: Attribute = DeclareAttribute[r, name, type, uniqueness, length, link, NewOnly] )
THEN DeclareAttribute[r: r, name: name, version: OldOnly] ~ a
2. DeclareAttribute changes the aType relation:
( a: Attribute = DeclareAttribute[r, name, type, uniqueness, length, link, NewOnly] ) THEN
V2E[ GetP[ e: a, aIs: aTypeIs, aOf: aTypeOf ] ] ~ type
3. DeclareAttribute changes the aUniqueness relation:
( a: Attribute = DeclareAttribute[r, name, type, uniqueness, length, link, NewOnly] )
THEN V2I[ GetP[ e: a, aIs: aUniquenessIs, aOf: aUniquenessOf ] ] ~
LOOPHOLE[uniqueness]
4. DeclareAttribute changes the aLength relation:
( a: Attribute = DeclareAttribute[r, name, type, uniqueness, length, link, NewOnly] )
THEN V2I[ GetP[ e: a, aIs: aLengthIs, aOf: aLengthOf ] ] ~ length
5. DeclareAttribute changes the aLink relation:
( a: Attribute = DeclareAttribute[r, name, type, uniqueness, length, link, NewOnly] )
THEN V2B[ GetP[ e: a, aIs: aLinkIs, aOf: aLinkOf ] ] ~ link
6. Again, NewOrOld is a convenience:
DeclareAttribute[r, name, type, uniqueness, length, link, NewOrOld] ~
{ a: Attribute = DeclareAttribute[r, name, type, uniqueness, length, link, OldOnly];
IF a = NIL
THEN RETURN[DeclareAttribute[r, name, type, uniqueness, length, link, NewOnly]]
ELSE RETURN[a] }
7. Declare OldOnly is the same as domain subset on AttributeDomain:
DeclareAttribute[r: r, name: name, version: OldOnly] ~
{ es: EntitySet = DomainSubset[d: AttributeDomain,
lowName: name, searchSegment: SegmentOf[r]];
e: Entity = NextEntity[es]; ReleaseEntitySet[es]; RETURN[e] }
8. Declare OldOnly checks any supplied type, length, link, uniqueness:
( a: Attribute = DeclareAttribute[r, name, type, uniqueness, length, link, NewOnly] )
THEN DeclareAttribute[r: r, name: name, type: type’, version: OldOnly] ~
IF NOT Eq[type’, type] THEN SIGNAL Error[MismatchedExistingAttribute]
ELSE DeclareAttribute[r: r, name: name, version: OldOnly]
( a: Attribute = DeclareAttribute[r, name, type, uniqueness, length, link, NewOnly] )
THEN DeclareAttribute[r: r, name: name, uniqueness: uniqueness’, version: OldOnly]
~ IF NOT uniqueness = uniqueness’ THEN
SIGNAL Error[MismatchedExistingAttribute]
ELSE DeclareAttribute[r: r, name: name, version: OldOnly]
( a: Attribute = DeclareAttribute[r, name, type, uniqueness, length, link, NewOnly] )
THEN DeclareAttribute[r: r, name: name, length: length’, version: OldOnly] ~
IF NOT length = length’ THEN SIGNAL Error[MismatchedExistingAttribute]
ELSE DeclareAttribute[r: r, name: name, version: OldOnly]
( a: Attribute = DeclareAttribute[r, name, type, uniqueness, length, link, NewOnly] )
THEN DeclareAttribute[r: r, name: name, link: link’, version: OldOnly] ~
IF NOT link = link’ THEN SIGNAL Error[MismatchedExistingAttribute]
ELSE DeclareAttribute[r: r, name: name, version: OldOnly]
9. Declare NewOnly fails if the entity already exists:
([] ← DeclareAttribute[r, name, type, uniqueness, length, link, NewOrOld]) THEN
DeclareAttribute[r, name, type, uniqueness, length, link, NewOnly] ~
SIGNAL Error[AlreadyExists]
10. Declare doesn’t work on NIL or null arguments:
DeclareAttribute[NIL, name, type, uniqueness, length, link] ~
SIGNAL Error[NILArgument]
IF Null[r] THEN DeclareAttribute[r, name, type, uniqueness, length, link] ~
IF Null[r] THEN SIGNAL Error[NILArgument]
Index of common terms
attribute: column of relation6, 34, 57, 59
augments: mechanism for encapsulating updates21, 69
basic operations: DeclareEntity, GetF, etc.10, 68, 81
caching data schema: used in implementation77
changing entity names16, 39
conceptual data model5
data independence5
data model: type mechanism DBMS provides1, 5
data schema: types in a particular database1, 11
datatype: type of datum6, 27
datum: string, integer, etc.5, 27
domain: type of entity6
entity: database representative of an object5, 27, 57, 75
entity/relationship distinction59, 60
entity/datum distinction5, 57
errors: generated by illegal operations45
external entity: real world object8
functionally ireducible: normal form18, 65
indices, B-tree: used in implementation35, 48
internal entity: we use "entity" to mean this9
irreducible: normal form18, 65
key: unique attributes of a relation9, 34, 64
links and colocation: connecting entities & relationships35, 78
lists and sets: representation in our model49, 62
name: of an entity9, 63
normalization: of relations16, 51, 65
physical data model5, 74
properties: simulated "attributes" of entities14, 9, 36, 39, 59
query operations: DomainSubset, RelationSubset11, 39, 68, 83
referential integrity: reason for entities57
relation: type of relationship6, 34, 57
relational data model1, 9
relationship: a tuple of entity and datum values6, 7, 27, 60, 75
schema declaration operations: DeclareDomain, etc.12, 32
segments: mechanism for separating databases18, 69
simplicity and utility: criteria for data model choice4
surrogate relation optimization80
system entities, domains, relations11, 43
transaction and segment operations29
translucent attributes: accessing attributes as strings15, 38
type hierarchy: domains and subdomains6, 28, 66
undefined values: attributes never assigned38
update anomaly: reason for normalization17
value: an entity or datum6, 76
views: relations that are not stored24, 69