Incremental Express

The Incremental Express Language is intended to allow model modifications to be specified as incremental changes, independent of the model to which they might be applied. Note that, although the specification is independent of any base model, there is an 'applicability dependency'.

Why Incremental Express

This language has multiple goals. The language allows various tools to be developed that can verify and apply incremental updates to existing data stores in place and identify incompatibilities. Also, the language defines a convenient, human readable, format that makes understanding (incremental) release changes much easier. One could also think of using this language to compare particular incremental releases with specified footprints/subsets that particular applications/implementations use and identify if the application is effected at all and if so where.

Overview of the Syntax

A more formal and complete specification of the grammar is given elsewhere. The grammar is designed to be close to the original express specification. All 'new' specifications will follow the exact specifications of 'normal' express. The grammar for this will not be specified here. To establish a context for the addition, some modifications to the syntax were required. Some examples will probably help more than words.

For example, imagine we want to create a new entity 'e' with an attribute 'a1' of type 'integer'.  This simple addition would look like this:

change incr_1;
add entity e
  a1 : integer;
end_entity;
end_change;
Similarly, assume we want to add an attribute a2 of type real, a uniqueness rule 'u' and a where clause 'w' to existing entity 'x':
change incr_2;
alter entity x;
  add a2 : real;
unique
  add u : a2;
where
  add w : a2 > 0.0;
end_entity;
end_change;
Now, assume we want to delete attribute 'a2', uniqueness rule 'u' and where clause 'w' from existing entity 'y' and remove entity 'z' completely:
change incr_3;
alter entity x;
  drop a2;
unique
  drop u;
where
  drop w;
end_entity;
drop entity z;
end_change;
Finally, the last 'simple' type of change, assume we want to rename attribute 'a2' to 'a3', uniqueness rule 'u' to 'v' and where clause 'w' to 'x' in existing entity 'y' and rename entity z to zz:
change incr_4;
alter entity y;
  alter a2 as a3;
unique
  alter u as v;
where
  alter w as x;
end_entity;
alter entity z as zz;
end_change;

Changing entity hierarchy and/or abstract status

Here is the grammar for an entity header that allows hierarchy modifications:

alter entity entity_name [(drop|add) subtype of entity_name { , entity_name }]
                                             [(drop|add) supertype of entity_name { , entity_name }]
                                             [(drop|add) abstract];

To make entity 'super' a supertype of 'sub' and make 'super' abstract and 'sub' non-abstract (assuming it's a supertype of something), the following two 'alter entity' statements would be equivalent:

(* Note that the schema context has been left off for convenience! *)
(* Variation 1 *)
alter entity super add supertype of (sub) add abstract;
end_entity;
alter entity sub drop abstract;
end_entity;
(* Variation 2 *)
alter entity sub add subtype of (super) drop abstract;
end_entity;
alter entity super add abstract;
end_entity;

Notes:

Changing attributes

These are probably the most complicated. Here are some things we want to be able to do and how to do them:
  1. Change the optionality
  2. Change the bounds of the aggregate
  3. Change the uniqueness of the aggregate (applies to LIST and ARRAY only)
  4. Change the type of aggregate
  5. Remove the aggregate
  6. Add an aggregate
  7. Change the type of the attribute
  8. Change the precision of integers, strings and reals
  9. Invert forward and inverse attributes
  10. Change the name of the forward attribute of an inverse
Here are some examples to show how:

1. Make attribute 'o' optional and 'm' mandatory in entity 'e' (note that this does not apply to inverse attributes):

alter entity e;
  alter o add optional;
  alter m drop optional;
end_entity

2. For attribute 'a', make lower bound '1' and upper bound '?':

alter entity e;
  alter a alter lower_bound as 1 alter upper_bound as ?;
end_entity

3. For attribute 'a' make its aggregate unique (assuming it was 'list[0:?] of activity' it would become 'list[0:?] of unique activity') and vice versa for attribute 'b':

alter entity e;
  alter a add unique;
  alter b drop unique;
end_entity

4. Change the aggregate from SET to LIST for attribute 'a':

alter entity e;
  alter a alter aggregate as list;
end_entity

5. Drop the aggregate of attribute 'a' (e.g. 'list[0:?] of activity' becomes 'activity'):

alter entity e;
  alter a drop aggregate;
end_entity

6. Add aggregate of LIST[0:?] to attribute 'a' (inverse of 5.)

alter entity e;
  alter a add aggregate list[0:?];
end_entity

7.Change the type of attribute 'a' (from 'ndt_name' for example) to 'ndt_long_name':

alter entity e;
  alter a alter type as ndt_long_name;
end_entity

8. Change the precision specification of attribute 'a' to '80' (e.g. 'string(40)' becomes 'string(80)'):

alter entity e;
  alter a alter precision as 80;
end_entity

9. Invert attributes (forward becomes inverse and vice versa):

alter entity e;
  invert a;
end_entity

If it were already an inverse then the following would apply.

alter entity e;
inverse
  invert a;
end_entity

10. Change the name of the forward attribute for inverse attribute 'a' to 'b' (e.g. 'a: activity for a;' becomes 'a: activity for b;'):

alter entity e;
inverse
  alter a alter forward as b;
end_entity

Changing types

All of the items 2-10 for attributes apply for type definitions.
In addition, enumerations must be handled. They are handled similar to uniqueness rules, except that order of enumerated values is unimportant:

Here is a syntax for enumeration changes;

add enumeration enum_value { , enum_value }
drop enumeration enum_value { , enum_value }
alter enumeration enum_value as enum_value

To add an enumerated value of 'new' and drop one of 'old' and change the name for 'two' to 'middle' defined for type 'enum_type' (e.g. 'type_def enum_type = enumeration (one,two,three,old);' becomes 'type_def enum_type = enumeration (one,middle,three,new);'):

alter type enum_type add enumeration new drop enumeration old alter two as middle;
end_type;

If this were attribute 'a' it would be:

alter entity e;
  alter a add enumeration new drop enumeration old alter two as middle;
end_entity

As far as the other types of changes allowed to types, see the description about attributes. Here is one example to get an idea:

Change the type of type 'ndt_name' (from 'string(40)', for example) to 'string(80)':

alter type ndt_name alter precision as 80;
end_type;

Note that 'select' types and aggregates of aggregates are not handled!

Changing uniqueness rules

Since the attribute list of a uniqueness rule is ordered, we need to be able to do the following:
  1. Add an attribute at a specific place in the existing list (i.e. at the beginning, end or before or after an existing attribute)
  2. Remove an attribute
  3. Move an attribute from one position to the other
Here is the syntax;

alter  unique_label [drop attribute_name {, attribute_name}]
                                    [add attribute_name {, attribute_name} (before | after) attribute_name]
                                    [add attribute_name {, attribute_name} (first | last)]
                                    [alter attribute_name (before | after) attribute_name]
                                    [alter attribute_name (first | last)];

1. Add attribute 'a' to the beginning, 'b' to the end and 'c' before 'd' in uniqueness rule 'u' (e.g. 'u : x,y,d,z;' becomes 'u: a,x,y,c,d,z,b;'):

alter entity e;
unique
  alter u add a first add b last add c before d;
end_entity

2. Remove attribute 'a' from uniqueness rule 'u' (e.g. u: a,b,c;' becomes 'u: b,c;'):

alter entity e;
unique
  alter u drop a;
end_entity

3. Move attribute 'a' in uniqueness rule 'u' to the beginning (e.g. 'u: b,c,a;' becomes 'u: a,b,c;'):

alter entity e;
unique
  alter u alter a first;
end_entity

Changing where rules

This will be limited to simply replacing the whole expression:

alter  where_label alter as where_expression ;

Context keyword

The context keyword after the declaration of a change schema is intended to set a reference to the express that was used to develop the change. Basically, it means that it is known to work on this version, although it can be applied and possibly correctly on another version.  For example:

change c;
context epicentre_2_2;
...
end_change;

Tags

In order to allow us to put in additional information that is required for documentation/projection purposes, the following embedded remark syntax has been established (derived from the EXPRESS Version 2 proposal):

 '(*'  [ '<' tag_name '>' ]  [ '(' tag_parameter ')' ]  text '*)'

The following tags are currently recognized:

 
Embedded Remark Tag Table
 tag name
tag description
desc Description to be included with the documentation of the item. Multiple consecutive white spaces will be eliminated. The only allowed parameter is either  first or last. Inclusions of one of these parameters declare that the existing description is to be modified by inserting the text at the beginning of the existing text or by appending it to the existing text. For example, (*<desc>(last) This text will be appended to the existing text.*). Note the space at the beginning of the appended text. If a parameter is not specified then all existing text will be replaced.
remark A remark that will only be visible in the Incremental Express file. That is, it will not be visible in either the documentation or the generated Express. For example, (*<remark>Not ready for prime time.*).
change A change comment related to the current context (e.g., entity, attribute, rule, etc). This comment (qualified to the context) will be included with the change documentation in addition to the overall <desc> for the change.
verb The verb phrase on a relationship. For example, (*<verb>be a property of*). This will be used on diagrams and to generate a line of description that conforms to the following template: Each from_entity_name [may|must] verb_phrase [one|one or more]  to_entity_name.
population Population scope. Enumeration of one of : fixed, open or local. For example, (*<population>open*). This tag is constrained to refer to entities that have reference behavior (i.e., attribute source).
request Request number. The value is the POSC assigned request number that this change addresses. For example, (*<request>2398*). This will be used to produce documentation which cross-references changed items with change requests.
status Status to be included in the documentation of the item. Enumeration of one of: deprecated or extension. For example, (*<status>deprecated*). Deprecated implies that the item may be dropped in a future version. Extension states that the item is a local extension.
ruletype The type of where rule. Enumeration of one of: dri, sri, mse, ose or val. For example, (*<ruletype>ose*). Constrained to where rules. This will be used to label the where rules in the documentation. See Rule Type Table.
projmeth Projection method. For entities, enumeration of one of: ent_table, ent_consol or ent_replic. For example, (*<projmeth>tab*).  ent_table declares that the entity should become a table. ent_consol declares that the attributes of the entity should be consolidated up to a higher-level entity that will become a table. ent_replic declares that the attributes of the entity should be replicated down to all subtypes that will become tables.  This tag is constrained to refer to entities. 
For attributes, enumeration of one of a list which can be provided. Note that appropriate defaults are currently defined for all Epicentre types (i.e., this should rarely be necessary).
projname Projected name. This is used to override the generic abbreviation scheme. The entity or attribute will be projected using the specified name. For attributes only, a suffix may be automatically added based on  its type (e.g., _s, _u). For example, (*<projname>replacement*) could result in an attribute being assigned the column name replacement_s.
projindex Projected index. This is used to define a non-unique index in the relational projection. The value will be an ordered list of attributes separated by columns. For example, (*<projindex>identifier, wellbore*) would create an index on the columns represented by attributes identifier and wellbore. This can only be defined on an entity.
projnk Use natural key in the projection. A value of yes indicates that the natural key should be used as the foreign key. This can only be used when the uniqueness rule of an entity has a single attribute of type string. A value of no indicates that the surrogate key should be used as the foreign key. For example, (*<projnk>yes*) indicates that the natural key should be used. The default is no unless already set to yes (i.e., we are updating a previously assigned value). This can be applied to an entity or to individual relationships to an entity.
Rule Type Table
Tag Documentation Label Description Express Patterns
dri Different Related Instance Relationship paths must point to different instances. self :<>: path2 ;
path1 :<>: path2 ;
not ( self in agg_att ) ; (*Where agg_att is an aggregate attribute.*)
not ( path1 in agg_att ) ;
sri Same Related Instance Relationship paths must point to the same instance. path1 :=: path2 ;
sizeof ( query ( id <* agg_att |id.att :=: path2 ) ) > 0 ;
mse Mandatory Relationship Set Mandatory select. One and only one relationship must be populated. exists ( att1 ) xor exists ( att2 ) ; (*This pattern can be extended to handle any number of attributes.*)
exists ( att1 ) xor ( exists ( att2 ) xor exists( att3 ) ) ; (*Pattern extended to 3 attributes.*)
exists ( att1 ) xor ( exists  ( att2) xor ( ( att3 )  xor  exists ( att4) ) ) ) ; (*Pattern extended to 4 attributes.*)
ose Optional Relationship Set Optional select. One and only one relationship may be populated. not ( exists ( att1 ) and exists ( att2 ) ); (*This pattern cannot be extended to handle more that 2 attributes.*)
( exists ( att1 ) xor exists ( att2 ) )  xor not ( exists ( att1 ) or exists ( att2) ) ;  (*This pattern can be extended to handle any number of attributes.*)
( exists ( att1 ) xor exists ( att2 ) xor exists ( att3 ) )  xor not ( exists ( att1) or exists ( att2 ) or exists ( att3 ) ) ; (*Pattern extended to 3 attributes.*)
val Instance Value Constraint A constraint on an instance value. These can include non-relationships. exists ( A ) or exists ( B ) ;  (*one or both must be given*)
exists ( A ) xor exists ( B ) ;  (*only one  must be given*)
( exists ( A ) and exists ( B ) ) or not exists ( A ) ;  (*If A is given, then B must also be given.*)
exists ( A ) xor ( exists( B ) or exists( C ) ) ; (*Either A or  (B and/or C) must be given.*)
not exists ( A ) or exists ( B ) ; (*If B is not given then A cannot be given.*)
( A + B ) <> 0 ;  (*The sum of the numeric values of A and B must not be zero.*)

If no tag_name is provided, 'desc' is assumed, meaning that text is a description of the item implied by the context in which the tag was placed.

The various allowed contexts are:

Although just before the semicolon seems to be most consistent, this is not required. For example in an attribute definition it can just as well be between the aggregate and the type or in the middle of the attribute list of a uniqueness rule etc.

Any comment that occurs outside of any context will be ignored if it is tagged as '(*<remark>...*), otherwise an error message will be generated. Also comments with the <remark> tag will not be preserved if they are placed in a context.