The SIRA_PRISE command language

The actual commands of the language

In this section, the SIRA_PRISE commands are presented along with a detailed explanation of their operation. Any pertinent remarks regarding some syntactical category of the grammar will also be presented here.

<SiraPriseCommand> := <Add>|<Delete>|<Update>|<Inquire>|<MultipleAssignment>+

When communicating with a SIRA_PRISE server at the lowest possible level (i.e. without using packages such as the provided java client package), then commands must be sent to the server as a text string, formatted according to the rules of the grammar. Any command must be one of the categories mentioned in the formal grammar.  These will be explained in detail in the pertinent sections below.

All applicable constraints (= all database constraints that might possibly get violated as a consequence of carrying out the given command) are checked after all the insert/delete operations have been done. If a constraint violation is detected, the statement is completely undone. No automatic rollback is done for the entire transaction in which the statement was carried out. If (the detection of) a constraint violation must result in a rollback for the entire transaction, then it is up to the user to issue a rollback message to the server.

<Add> := ASSERT|ADD|CREATE <RelvarName>,<RelationalExpression>

<RelvarName> identifies the relvar to which tuples will be added/asserted (see below), <RelationalExpression> defines which tuples will be added/asserted.

ADD will raise an exception if a tuple is added that is already present in the (body of the relation value currently held in the) updated relvar. The counterpart of ADD that does not raise an exception in that circumstance is ASSERT. In all other respects, ASSERT is identical to ADD. CREATE is a purely syntactical alias for ADD.

For relvars with at least one interval-typed attribute, this rule with respect to ADD is to be interpreted for each point of the interval individually. E.g. adding TUPLE(I(BEGIN(2)END(5))) (where attribute I is of type INTINTERVAL) to a relvar R that currently has value RELATION(TUPLE(I(BEGIN(4)END(8)))) is invalid, but "asserting" that same tuple to R is valid.

If a relvar has interval-typed attributes, ADD will attempt to combine multiple tuples into one, whilst maintaining the same "informational value" within the database. In the above example, the result of the assertion would not be a relation of cardinality 2 in R, but instead the singleton TUPLE(I(BEGIN(2)END(8))).

This rule is applied irrespective of the number of interval-typed attributes in the updated relvar. Note however, that in the case where a relvar has >1 interval-typed attributes, this implies that there is no "single predictable canonical form" that the relvar value will have after the update. The only guarantee is that if the ADD/ASSERT is accepted, then each "asserted interval point" will be present in (some tuple in the body of the relation value of) the updated relvar, and that SIRA_PRISE will have done the best it could to make the updated relation value "as dense as possible".

<Delete> := UNASSERT|DELETE|REMOVE <RelvarName>,<RelationalExpression>

<RelvarName> identifies the relvar from which tuples will be deleted/unasserted (see below), <RelationalExpression> defines which tuples will be deleted/unasserted.

DELETE will raise an exception if an attempt is made to remove a tuple from the (body of the relation value currently held in the) updated relvar, and that tuple is not there. The counterpart of DELETE that does not raise an exception in that situation is UNASSERT. In all other respects, UNASSERT is identical to DELETE. REMOVE is a purely syntactical alias for DELETE.

For relvars with at least one interval-typed attribute, this rule with respect to DELETE is to be interpreted for each point of the interval individually. E.g. deleting TUPLE(I(BEGIN(2)END(5))) (where attribute I is of type INTINTERVAL) from a relvar that currently has value RELATION(TUPLE(I(BEGIN(4)END(8)))) is invalid, but "unasserting" that same tuple from that same relvar is valid.

Like with ADD, DELETE/UNASSERT will do the best it can to make the relation value after the update "as dense as possible". This is relevant in the case where the relvar has >1 interval-typed attribute. Using a somewhat more concise notation for the interval values, consider a relvar R with value TUPLE(X(1-5)Y(1-3))TUPLE(X(1-3)Y(3-8)) . You can visualise this as two rectangles that together form an L-shaped area. Deleting TUPLE(X(3-5)Y(1-3)) reduces this L-shaped area to a single rectangle. SIRA_PRISE will observe this and merge the two tuples left after the delete into the single TUPLE(X(1-3)Y(1-8)).

<Update> := UPDATE|MODIFY <RelvarName>,<RelationalExpression>,(<ScalarUpdateExpression>+)

 <ScalarUpdateExpression> := <AttributeName>(<ScalarExpression>)

MODIFY is a purely syntactical alias for UPDATE. The meaning of the three components is as follows :

<RelvarName> is the name of the relvar whose tuples will get updated.

<RelationalExpression> denotes the tuples within that relvar that will get updated. Note in particular that this can be just any relational expression, including a relation value selector, or an expression of the relational algebra involving any number of other relvars. <RelationalExpression> in this context is thus definitely not constrained to being a restrict on the relvar being updated. The only requirement is that any tuple present in (the value denoted by) <RelationalExpression>, must also be present in (the current value of the variable whose name is) <RelvarName>. One peculiar benefit that deserves being spelled out explicitly is the possibility of using a relation value selector here in order to obtain the effect of ‘optimistic locking’ :

  1. Read the database, i.e. issue some restrict on R
  2. Display the tuple(s) on the user’s screen and let him do whatever manipulations he sees fit.
  3. After the user has finished editing, issue an update to R specifying the displayed tuple(s) using a relation value selector. If any other user has impacted that(those) tuple(s) meanwhile in any way, the update will fail, and the user issuing the update can be notified.

<UpdateExpressionList> is a list of scalar expressions that define what updates must be performed on the tuples in <RelvarName> that are denoted by <RelationalExpression>. <UpdateExpressionList> thus identifies :

  1. Which attributes of the ‘updated’ tuples will get a new value.
  2. The formula to be applied to compute the new value of this attribute. This must be a scalar expression whose only allowable variables are references to attributes in the same tuple.

Observe that the update expression list is allowed to hold scalar expressions exclusively. This reflects the restriction that SIRA_PRISE does not support the concept of relation-typed attributes in base relvars.

There is no "assert/unassert"-style counterpart to UPDATE. This is theoretically possible, given that UPDATE is nothing more than a shorthand for the combination of some DELETE (or UNASSERT) and some ADD (or ASSERT). This would give rise to four distinct types of UPDATE : DELETE/ADD, DELETE/ASSERT, UNASSERT/ADD, UNASSERT/ASSERT. Of these four, UPDATE behaves like a combination of DELETE/ADD, meaning in particular that the "deleted" tuple is required to exist in the database, and that the "new" tuple is required not to exist in the database at the time of the update.

Note that an update will not be attempted if the new tuple turns out to be equal to the existing tuple. The update command essentially proceeds as follows :

  1. Compute the set of tuples affected
  2. For that set of tuples, compute the set of replacing tuples using the update expressions specified
  3. If a replacing tuple is not equal to its corresponding existing tuple, then delete the existing and add the replacing tuple.

<MultipleAssignment> := CMD(<Add>|<Delete>|<Update>)

SIRA_PRISE supports the concept of multiple assignment, allbeit with a restriction in comparison to the specifications defined by TTM. SIRA_PRISE does evaluate all variables in source positions prior to doing the assignments, but it does not fully apply the "assignment compaction rule" specified in TTM's prescription 21b. This obviously causes SIRA_PRISE to behave differently in comparison to TTM in some of the cases where a variable is assigned to more than once within the same MA.

Consider the MA CMD(ADD R1,...)CMD(DELETE R1,R1). The tuples added to R1 in the first step of this MA will NOT be "seen" by the second step of this MA, and will thus not be deleted from R1, leaving R1 with a non-empty value (it will hold all the tuples added in the first step), whereas the TTM specification requires R1 to be left empty after this assignment. So as was the case with SIRA_PRISE V1.1, support for multiple assignment still does not cover the full 100% of the specifications. However, the situation has improved for a number of cases. According to the TTM specifications, the ‘sequence’ A:=B,B:=A results in the variables A and B ‘swapping’ values. SIRA_PRISE now deals correctly with this case, whereas the previous version would have first assigned the value of B to A and then the new value of A to B, thus leaving B unaltered.

To summarize : the only deviation from the TTM specifications now left in SIRA_PRISE is the case where the same assignment target is used more than once within some MA, AND that same assignment target is also referenced in any but the first expression denoting the relation to be inserted into/deleted from that target.  Note that SIRA_PRISE's compaction does behave correctly with assignments such as, e.g. CMD(ADD R1,v)CMD(DELETE R1,v), where v is twice the same relation value selector : this assignment will correctly leave R1 unaltered.

It is not allowed to mingle assignments to catalog relvars and assignments to "regular" user relvars within the same MA. This means it is not possible to define a new relvar (which involves adding tuples to some catalog relvar) and assign it a non-empty value (which involves adding tuples to that new user relvar) within one single MA. Note that before being able to have SIRA_PRISE record a value for a relvar, some physical design details too must be provided too for that relvar.

Apart from those differences, SIRA_PRISE's version of multiple assignment conforms to TTM, thus offering the possibility to do all of the following combinations of operations, and have them behave as if they were one single statement (meaning its effects will be undone entirely if the assignment causes a constraint violation) 

<Inquire> := INQUIRE|SELECT <RelationalExpression>[,(<AttributeName>+,)]

INQUIRE evaluates its relational expression and sends the result back to the client.

The optional AttributeName list of INQUIRE defines the attributes that will determine the order in which the tuples will be presented in the result. If no AttributeName list is specified, then the ordering of the tuples is undetermined. If an AttributeName list is specified, then determining the position in the result of a tuple t1, relative to that of another tuple t2, is done by first comparing the values that appear in those tuples for the first attribute of the list, if those are equal then of the second attribute of the list, etc. etc. The comparison is done using the comparison operator (GT) of the attribute's type. If no such comparison operator exists (the type is unordered), then the comparison is based on the bit pattern of the encoding of the attribute's value. Note that for this reason, relation-valued attributes cannot be specified in the ordering attribute list because they simply do not have a bit pattern encoding (not in the same sense as a scalar value having such a bit pattern). If all compared attribute values yield "equal", then the relative ordering of the tuples is determined by their internal tuple ID. This makes the ordering deterministic in all cases (which is needed for quota query processing, quota query processing being a planned future feature).

<Expression> := <RelationalExpression>|<ScalarExpression>

Expressions in general fall apart in two categories : relational expressions (i.e. expressions whose value is a relation) and scalar expressions (all the others). This distinction doesn't actually bear great importance, except to make the paragraphs commenting on them manageable ...

<RelationalExpression> := <RelvarName> | <AttributeName> | <NamedRelationValueSelector> | <UnNamedRelationValueSelector | <RelationalOperatorExpression>

Relational expressions can take on five different forms :

  1. References to database relvars
  2. References to relation-typed attributes within visible scope
  3. Relation value selectors
  4. Relation value selectors using a relvar name as a shorthand for specifying the relation type
  5. Invocations of relational operators

<NamedRelationValueSelector> := <RelvarName>(<TupleValueSelector>*)

If the name of a known relvar is used to specify a relation constant, then the specified relation body must match the heading of that known relvar. The relation type of the relation value selector expression is the same as that of the named relvar.

TABLE_DEE() is a valid relation value selector, because TABLE_DEE is a known relvar name. Note however that TABLE_DEE() is not equal to TABLE_DEE, but instead it ís equal to TABLE_DUM ...

<UnNamedRelationValueSelector> := RELATION(HEADING(<HeadingAttribute>*)BODY(<TupleValueSelector>*))

In order to specify a relation value of just any relation type, the keyword RELATION must be used. In that case, the full relation heading must be specified using the HEADING(...) construct. The benefit of using known relvar names is, obviously, that the value selector gets much shorter to write because the HEADING(...) part can be omitted. As an example, here are two ways to denote the same relation value (observe that TYPE is a catalog relvar with a single attribute TYPENAME of type NAME) :

RELATION(HEADING(TYPENAME(NAME))BODY(TUPLE(TYPENAME(SOMENAME))))
TYPE(TUPLE(TYPENAME(SOMENAME)))

The empty relation value (i.e. the tupleless relation) of any relation type can then be specified as

RELATION(HEADING(...)BODY())

<HeadingAttribute> := <SHeadingAttribute>|<RHeadingAttribute>
<SHeadingAttribute> := <AttributeName>(<TypeName>)
<RHeadingAttribute> := <AttributeName>(RELATION(HEADING(<HeadingAttribute>*)))

When specifying the heading of a relation, that heading can hold scalar attributes as well as relation-typed attributes. For scalar attributes, the attributename and the (scalar) typename must be specified. But if a relation type has a relation-typed attribute, then selecting a relation value of that type using a relation value selector involves selecting relation values for the relation-typed attribute. So it might appear as if a full RELATION(HEADING(...)BODY(...)) is required within each TUPLE(RELATIONTYPEDATTRIBUTE(...)) construct, as in :

RELATION(HEADING(RVA(RELATION(HEADING(ATTR(INT)))))BODY(TUPLE(RVA(RELATION(HEADING(ATTR(INT))BODY(T(ATTR(3))))))TUPLE(RVA(RELATION(HEADING(ATTR(INT))BODY(T(ATTR(7))))))))

It's easy to see why this is both inefficient and undesirable, so SIRA_PRISE therefore proceeds somewhat differently. In such value selectors, the heading of the RVA relation type is always specified inside the HEADING(...) of the containing relation, and never repeated in the tuple value selectors in the BODY(...) part :

RELATION(HEADING(RVA(RELATION(HEADING(ATTR(INT)))))BODY(TUPLE(RVA(BODY(TUPLE(ATTR(3)))))TUPLE(RVA(BODY(TUPLE(ATTR(7)))))))

<TupleValueSelector> := TUPLE(<AttributeValueSelector>*)
<AttributeValueSelector> := <AttributeName>(<Expression>)

Tuple value selectors essentially consist of a series of attribute value selectors, which in turn name the concerned attribute and state the expression that will select the value that will appear for that attribute in the selected tuple value. Note in particular that the <Expression>s selecting the attribute values need not be literals exclusively. Take a close look at the following example and a (possible) value it produces :

RELATION(HEADING(TYPE(RELATION(HEADING(TYPENAME(NAME))))DBMSFILE(RELATION(HEADING(FILENAME(FILENAME)PAGESIZE(INT)))))BODY(TUPLE(DBMSFILE(DBMSFILE)TYPE(TYPE))))

TYPE DBMSFILE
TYPENAME
FLOATINTERVAL
INTINTERVAL
DECIMAL
INT
BOOLEAN
BITS
LONGINTERVAL
STRING
DATEINTERVAL
RELATION
LONG
NAME
FLOAT
AVERAGE
FILENAME
DECIMALINTERVAL
DATE
PAGESIZE FILENAME
8192 VIT1.ERDB
8192 TRCU.ERDB
32768 VIT2SHIX.ERDB
32768 VIT2MHIX.ERDB
8192 PC.ERDB
32768 DATABASECATALOG.ERDB
32768 VIT2TRIX.ERDB

This value selector selects a singleton relation value of degree two, with the two attributes named 'TYPE' and 'DBMSFILE'. Both attributes are relation-typed. The TYPE attribute has relation values of degree 1, with a single attribute named 'TYPENAME', the DBMSFILE attribute has relation values of degree two, with attributes 'FILENAME' and 'PAGESIZE'. The two attribute values in the singleton relation (the overall expression) are equal to the current value of the TYPE and DBMSFILE relvars of the database, this being achieved with relvar references as the expressions inside the tuple/attribute value selectors.

BTW : this example illustrates how a database can theoretically be seen, in its entirety, as a single tuple with as many relation-typed attributes as there are relvars in the database.

<RelationalOperatorExpression> := <ROjoin> | <ROunion> | <ROintersect> | <ROminus> | <ROsemiminus> | <ROsemijoin> | <ROrename> | <ROproject> | <ROrestrict> | <ROtclose> | <ROdivideby> | <ROextend> | <ROgroup> | <ROungroup> | <ROaggregate> | <ROSummarizeBy> | <ROTransform> | <ROleftjoin> | <ROxminus>

Relational operator invocations are invocations of one of the 19 operators supported by SIRA_PRISE.

<ROjoin> := JOIN(<RelationalExpression>,<RelationalExpression>+,)

JOIN is the 'natural join' operator on relations. The operator is associative, meaning that it is possible to give more than two relations as an argument. Theoretically, the syntax could also provide for monadic and niladic invocations of JOIN (i.e. joins on only one relation and joins on no relation at all), but SIRA_PRISE doesn't support that. The observation that for all R, JOIN(R) = R, and that JOIN() = TABLE_DEE should suffice as an explanation why. We prefer to spend our time trying to solve problems that are a bit more 'real', such as the following :

Allthough SIRA_PRISE is able to find the best evaluation strategy for joins between two arguments, it is not able to do so for joins that have >2 arguments. So the order in which the arguments are listed in an associative join invocation, can significantly impact performance. For good performance, the user is required to write the arguments that "join well" close to one another, and this, perhaps counterintuitively, AT THE END of the list of join arguments. Fortunately, this is about the only bad news there is to mention about (SIRA_PRISE's implementation of) JOIN.

An example of a join of a relation value selector with four database relvars :

JOIN(RELATION(HEADING(RELVARNAME(NAME))BODY(TUPLE(RELVARNAME(RELVAR))TUPLE(RELVARNAME(RELVARATTRIBUTE)))),RELVAR,RELVARATTRIBUTE,ATTRIBUTE,TYPE)

RELVARPREDICATE TYPENAME ATTRIBUTENAME RELVARNAME
§RELVARNAME§ is a Relation variable whose predicate is "§RELVARPREDICATE§". STRING RELVARPREDICATE RELVAR
§RELVARNAME§ is a Relation variable whose predicate is "§RELVARPREDICATE§". NAME RELVARNAME RELVAR
Attribute §ATTRIBUTENAME§ is an attribute of Relation variable §RELVARNAME§. NAME ATTRIBUTENAME RELVARATTRIBUTE
Attribute §ATTRIBUTENAME§ is an attribute of Relation variable §RELVARNAME§. NAME RELVARNAME RELVARATTRIBUTE

Joins can also be performed with relation-typed attributes among the join attributes. The following example tells us which relvars have an identical key (for an explanation of the GROUP operator used here to produce the relation-valued attribute, see the section on that operator) :

RESTRICT(JOIN(GROUP(KEYATTRIBUTE,(KEYATTRIBUTES(ATTRIBUTENAME))),RENAME(GROUP(KEYATTRIBUTE,(KEYATTRIBUTES(ATTRIBUTENAME))),RELVARNAME,RELVARNAME2,KEYID,KEYID2)),NOT(EQ(RELVARNAME,RELVARNAME2)))

RELVARNAME KEYID RELVARNAME2 KEYID2 KEYATTRIBUTES
ATTRIBUTE 2 ATTRIBUTEPHYSICALLENGTH 96
ATTRIBUTENAME
ATTRIBUTENAME
ATTRIBUTE 2 ATTRIBUTELOGICALLENGTH 86
ATTRIBUTENAME
ATTRIBUTENAME

(For the pernickety : the relation printed here is not actually the real result - the real result is way too long to include here, and there is actually still a flaw in the query too, which I leave as an exercise for the reader to find.)

<ROunion> := UNION(<RelationalExpression>,<RelationalExpression>+,)

Like JOIN, UNION too is an associative operator. Once again, allthough it would be theoretically possible to support monadic and niladic invocations of this operator, SIRA_PRISE doesn't do that. For all R, UNION(R) = R and UNION() = RELATION(HEADING(...)BODY()) (i.e. the empty relation of the appropriate type). There is little more to be said about UNION, except to give some examples for completeness' sake :

UNION(MULTIHASHINGINDEXSPACE,DATASPACE)

FILENAME STORAGESPACEID MAXIMUMGAPS GAPCOMPRESSIONTHRESHOLD
DATABASECATALOG.ERDB 65 7 4
DATABASECATALOG.ERDB 51 7 4
DATABASECATALOG.ERDB 21 7 4
DATABASECATALOG.ERDB 15 7 4
DATABASECATALOG.ERDB 10 7 4
DATABASECATALOG.ERDB 3 7 4
DATABASECATALOG.ERDB 4 7 4
DATABASECATALOG.ERDB 117 7 4

The following example shows that UNION always eliminates duplicates :

EXTEND(UNION(TABLE_DEE,TABLE_DEE),(TXT(STRING(WHERE HAS THAT "SECOND" TUPLE GONE?))))

TXT
WHERE HAS THAT "SECOND" TUPLE GONE ?

<ROintersect> := INTERSECT(<RelationalExpression>,<RelationalExpression>+,)

For this associative operator, the same remark applies as for JOIN and UNION : monadic and niladic invocations of the operator are not supported. INTERSECT(R) = R for all R, and INTERSECT() = RELATION(HEADING(...)BODY(TUPLE(...)...)), with the relation body growing beyond any computer's memory capacity for all but the simplest relation types.

No examples are given, as we think this operator "speaks for itself".

<ROminus> := MINUS(<RelationalExpression>,<RelationalExpression>)

The following example employs the relational difference to find all the types that do not have a corresponding interval type :

MINUS(TYPE,PROJECT(INTERVALTYPE,(TYPENAME)))

TYPENAME
NAME
BITS
RELATION
FLOATINTERVAL
AVERAGE
INTINTERVAL
STRING
DECIMALINTERVAL
FILENAME
BOOLEAN
DATEINTERVAL
LONGINTERVAL

<ROxminus> := XMINUS(<RelationalExpression>,<RelationalExpression>+,)

XMINUS is the operator that implements the operation known as 'symmetrical difference'.  In its binary form, the result includes all the tuples that appear in the first argument, but not in the second, plus all tuples that appear in the second argument, but not in the first. Thus, XMINUS(R1,R2) is semantically equivalent to UNION(MINUS(R1,R2),MINUS(R2,R1)).

It also happens to be the case that this operator is associative. Hence the same remark applies as for JOIN ,UNION and INTERSECT : monadic and niladic invocations of the operator are not supported. XMINUS(R) = R for all R, and XMINUS() = RELATION(HEADING(...)BODY()).  However, unlike the other associative relational operators, it is also currently not possible to use XMINUS as the aggregation operator in an AGGREGATE invocation that aggregates over relation-valued attributes.  This may be fixed in a future release, but we don't regard this issue as very pressing. (Consider the "meaning" of some tuple appearing in the result of such an "associative" (i.e. >2 arguments) XMINUS invocation : it means that the tuple appeared in any odd number of the XMINUS arguments, but you don't know whether it appeared in 1 or 3 or ... of the arguments, and of course you don't know in which of them.  It doesn't seem like a very useful thing with a compelling need to support it.)

<ROleftjoin> := LEFTJOIN(<RelationalExpression>,<RelationalExpression>,(<AttributeValueSelector>*))

Natural join has the property that it only includes tuples in the result if in the "second" relation, at least one tuple is found whose attribute values match the attribute values of the tuple (of the "first" relation) that is being joined to. This is often useful, but it is also often quite annoying. Often, we want to retain all tuples from one of the join arguments, regardless of whether any matching tuples exist in the other argument. This is where LEFTJOIN is useful. The prescriptions of the relational model require, however, that the result contains a value for each attribute of that other argument. In order to be able to comply to that rule, a third argument must be provided which lists all the attribute values that are to be included in the result for tuples of the first argument, for which no matching tuples could be found in the second.  LEFTJOIN(R1,R2,attrvalues)  is thus semantically equivalent to UNION(JOIN(R1,R2,),EXTEND(SEMIMINUS(R1,R2),attrvalues)).

<ROsemijoin> := SEMIJOIN(<RelationalExpression>,<RelationalExpression>)

In TTM, the name for the SEMIJOIN operator has recently changed to MATCHING. We prefer to stick with the old names, primarily because the new name for SEMIJOIN's counterpart, SEMIMINUS, includes the word 'NOT' in its new name, which we feel is a word that doesn't belong in names. No piece of furniture is called 'NOT TABLE', no photographer's monopod is called 'NOT TRIPOD', no president of the US is called 'NOT BUSH', no birdwatcher's telescope is called 'NOT BINOCULARS', the boolean value that means "not true" is not called 'NOT TRUE', etc. etc. Other than this psychological naming issue, there is no difference between SIRA_PRISE's SEMIJOIN and TTM's MATCHING.

The following query uses the SEMIJOIN operator to find the types that have a corresponding interval type :

SEMIJOIN(TYPE,INTERVALTYPE)

TYPENAME
FLOAT
DATE
LONG
INT
DECIMAL

<ROsemiminus> := SEMIMINUS(<RelationalExpression>,<RelationalExpression>)

SEMIMINUS corresponds to TTM's NOT MATCHING operator. As already indicated, the word 'NOT' in that name is the reason why SIRA_PRISE doesn't follow TTM's recent rename. The following query uses the SEMIMINUS operator to find which relvars don't have any associated recordtype :

SEMIMINUS(RELVAR,RECORDTYPE)

RELVARNAME RELVARPREDICATE
CURRENTDATE The current system date is §CURRENTDATE§.
CURRENTUSER The current user is §USERID§.
CURRENTUPDATE The current update operation is §OPERATIONTYPE§ on relvar §UPDATEDRELVARNAME§.
CURRENTCONTEXT The current date is §CURRENTDATE§, the current user is §USERID§, the current operation type is §OPERATIONTYPE§, and the updated relvar is §UPDATEDRELVARNAME§.

<ROrename> := RENAME(<RelationalExpression>,(<AttributeNamePair>+,))
<AttributeNamePair> := <AttributeName>,<AttributeName>

There is a slight difference between SIRA_PRISE's RENAME operator and TTM's one. TTM defines a multiple rename to be equivalent to an invocation of nested renames. RENAME R ADD A AS B, B AS A should be equivalent to RENAME (RENAME R ADD A AS B) ADD B AS A. But the inner rename is invalid ! Contrary to TTM, SIRA_PRISE allows such renames and deals with them as presumably expected by the user :

RENAME(DBMSFILE,(FILENAME,PAGESIZE,PAGESIZE,FILENAME))

PAGESIZE FILENAME
DATABASECATALOG.ERDB 32768

<ROproject> := PROJECT(<RelationalExpression>,(<AttributeName>*))

PROJECT(STORAGESPACE,())

This example illustrates that it is hard to visualise a relation with one tuple and no attributes as a table with one row and no columns (some browsers may allow you to observe that the line around this table is a bit thicker than the lines around the other tables), so the following examples which use the same query might be a bit clearer :

EXTEND(TABLE_DEE,EQUAL(EQ(TABLE_DEE,PROJECT(STORAGESPACE,()))))

EQUAL
True

EXTEND(TABLE_DEE,EQUAL(EQ(TABLE_DUM,PROJECT(STORAGESPACE,()))))

EQUAL
False

PROJECT(STORAGESPACE,(FILENAME))

FILENAME
DATABASECATALOG.ERDB

<ROrestrict> := RESTRICT(<RelationalExpression>,<ScalarExpression>)

The following example lists all the attributes whose defined logical length exceeds 256 :

RESTRICT(ATTRIBUTELOGICALLENGTH,GT(LOGICALLENGTH,INT(256)))

ATTRIBUTENAME LOGICALLENGTH
RELVARPREDICATE 382
CONSTRAINTEXPRESSION 768
CONSTRAINTMESSAGETEXT 510
CERTIFICATE 262144
VALUE 382

<ROtclose> := TCLOSE(<RelationalExpression>,(<AttributeNamePair>+,))

TCLOSE(PARENTCHILD,(PA1,CA1,PA2,CA2))

TCLOSE is a bit different compared to how it is usually defined (including by TTM). The attributelist in TCLOSE serves the purpose of determining which attributes are compared to which, in the case where the relation to be tclosed is not binary (TTM requires the argument to be of degree 2). This could be the case if that relation establishes a bill-of-material structure (or any kind of parent-child relationship structure) between things that are identified by a composite key (e.g. in the example given : {a1,a2}). The example above thus computes a closure based on equality between (pa1,pa2) pairs and (ca1,ca2) pairs (that is : pa1=ca1 and pa2=ca2). The TTM version of TCLOSE, should in such cases be invoked on some transformation of the relation where the composite keys have first been WRAPped into a tuple value (making the relation binary, as required). SIRA_PRISE does not support user-visible tuple types and the associated WRAP/UNWRAP operators, however.

<ROdivideby> := DIVIDEBYPER(<RelationalExpression>,<RelationalExpression>,<RelationalExpression>)

For an explanation and examples of this operator, we refer to TTM.

<ROextend> := EXTEND(<RelationalExpression>,(<ExtendDef>+))
<ExtendDef> := <AttributeName>(<Expression>)

The following example uses the extend operator to compute an interval-typed value that defines the entire range of valid sizes for all scalar types :

EXTEND(RESTRICT(TYPEPROPERTIES,ISSCALAR),(SIZERANGE(INTINTERVAL(BEGIN(MINIMUMSIZE)END(PLUS(MAXIMUMSIZE,INT(1)))))))

TYPENAME MINIMUMSIZE MAXIMUMSIZE ISORDERED ISSCALAR SIZERANGE
FLOAT 1 1 True True BEGIN(1)END(2)
BITS 0 786431 False True BEGIN(0)END(786432)
DATE 1 1 True True BEGIN(1)END(2)
INT 1 1 True True BEGIN(1)END(2)
FLOATINTERVAL 2 2 False True BEGIN(2)END(3)
AVERAGE 1 1 False True BEGIN(1)END(2)
INTINTERVAL 2 2 False True BEGIN(2)END(3)
DECIMALINTERVAL 36 36 False True BEGIN(36)END(37)
STRING 0 1048576 False True BEGIN(0)END(1048577)
LONG 18 18 True True BEGIN(18)END(19)
DECIMAL 18 18 True True BEGIN(18)END(19)
FILENAME 1 250 False True BEGIN(1)END(251)
LONGINTERVAL 36 36 False True BEGIN(36)END(37)
DATEINTERVAL 2 2 False True BEGIN(2)END(3)
BOOLEAN 1 1 False True BEGIN(1)END(2)
NAME 1 250 False True BEGIN(1)END(251)

<ROgroup> := GROUP(<RelationalExpression>,(<GroupDef>+))
<GroupDef> := <AttributeName>(<AttributeName>+,)

The GROUP operator supports the creation of more than one relation-valued attribute at the same time. The rules for computing the values of the resulting RVA's (in the case there is more than one RVA to be computed) are those of projection.

The following example uses the GROUP operator to "bundle" the minimumsize and maximumsize attributes in a single relation-typed attribute for all the nonscalar types (every single one of them) :

GROUP(RESTRICT(TYPEPROPERTIES,NOT(ISSCALAR)),(SIZES(MAXIMUMSIZE,MINIMUMSIZE)))

SIZES TYPENAME ISORDERED ISSCALAR
MAXIMUMSIZE MINIMUMSIZE
2147483647 0
RELATION False False

The following example illustrates a multiple grouping on the same input :

GROUP(RESTRICT(TYPEPROPERTIES,NOT(ISSCALAR)),(SIZES(MAXIMUMSIZE,MINIMUMSIZE)BOOLEANS(ISSCALAR,ISORDERED)))

SIZES BOOLEANS TYPENAME
MAXIMUMSIZE MINIMUMSIZE
2147483647 0
ISSCALAR ISORDERED
False False
RELATION

The following example uses the group operator to group together all the types that have the same size range :

GROUP(TYPEPROPERTIES,DETAILS(TYPENAME,ISSCALAR,ISORDERED))

DETAILS MINIMUMSIZE MAXIMUMSIZE
TYPENAME ISSCALAR ISORDERED
STRING True False
0 1048576
TYPENAME ISSCALAR ISORDERED
DECIMAL True True
LONG True True
18 18
TYPENAME ISSCALAR ISORDERED
AVERAGE True False
INT True True
BOOLEAN True False
FLOAT True True
DATE True True
1 1
TYPENAME ISSCALAR ISORDERED
LONGINTERVAL True False
DECIMALINTERVAL True False
36 36
TYPENAME ISSCALAR ISORDERED
RELATION False False
0 2147483647
TYPENAME ISSCALAR ISORDERED
BITS True False
0 786431
TYPENAME ISSCALAR ISORDERED
DATEINTERVAL True False
FLOATINTERVAL True False
INTINTERVAL True False
2 2
TYPENAME ISSCALAR ISORDERED
FILENAME True False
NAME True False
1 250

This query could be written to use the output of the extend example too, where the "grouping key" would be the interval-typed attribute. However, SIRA_PRISE currently does not support grouping keys that include interval-typed attributes. Another unsupported feature is a multiple grouping where some attribute appears in more than one <GroupDef>, as in :

GROUP(TYPEPROPERTIES,(DETAILS(TYPENAME,ISSCALAR,ISORDERED)BOOLEANS(ISSCALAR,ISORDERED)))

<ROungroup> := UNGROUP(<RelationalExpression>,(<AttributeName>))

The ungroup operator is, at present, restricted to the ungrouping of at most one relation-typed attribute. TTM is not entirely clear as to how ungroup should proceed in the event of multiple ungroup attributes under certain peculiar conditions (particularly in the event of name-clashes between the attributes of the relation-typed attributes to be ungrouped).

Observe the result of ungroup if the relation-typed attribute to be ungrouped has the empty relation as its value :

EXTEND(RESTRICT(TYPEPROPERTIES,ISORDERED),X(TABLE_DUM))

TYPENAME MINIMUMSIZE MAXIMUMSIZE ISORDERED ISSCALAR X
FLOAT 1 1 True True
LONG 18 18 True True
DATE 1 1 True True
INT 1 1 True True
DECIMAL 18 18 True True

And the ungrouping of this relation over attribute X (which is the empty relation in all tuples) :

UNGROUP(EXTEND(RESTRICT(TYPEPROPERTIES,ISORDERED),X(TABLE_DUM)),(X))

TYPENAME MINIMUMSIZE MAXIMUMSIZE ISORDERED ISSCALAR

One possibly sensible way to overcome the single-ungroup-attribute restriction, is to transform the input relation by first joining together all the relation-typed attributes to be ungrouped. Details are not included here and left for the reader as an exercise.

<ROaggregate> := AGGREGATE(<RelationalExpression>,(<AggregationDef>+))
<AggregationDef> := <AttributeName>(<OperatorName>(<Expression>))

The AGGREGATE operator is an additional operator in comparison to TTM. It is much more basic in its operation in comparison to TTM's summarize (thus also less functional), and is in fact a "building block" on which the implementation of SIRA_PRISE's SUMMARIZEBY operator relies. The rules for aggregate are as follows :

Aggregate thus provides a, perhaps somewhat clumsy, substitute for operators such as TTM's COUNT(<Relation>) :

AGGREGATE(RELVAR,(COUNT(PLUS(INT(1)))))

COUNT
63

Observe that AGGREGATE requires its operator to be both associative and commutative on the corresponding expression type. But then how about computing averages, which is perhaps the most common form of aggregation ? To do averaging, SIRA_PRISE requires the use of the PLUS operator, which operates, not on any number type, but on the special-purpose type AVERAGE :

AGGREGATE(DBMSFILE,(AVGPAGESIZE(PLUS(AVERAGE(COUNT(1)VALUE(FLOAT(THE_STRING(PAGESIZE))))))))

AVGPAGESIZE
COUNT(7)VALUE(22235.428571428572)

Observe the following points :

  1. The operator name is PLUS
  2. The expression aggregated is a value selector of type AVERAGE
  3. That value selector selects values with two components, an observation count and an observation value
  4. The observation count component is of type INT
  5. The observation value is of type FLOAT
  6. Getting the 'FLOAT' value of the PAGESIZE attribute, involves getting the STRING representation of that attribute (which is of type INT, not FLOAT)

Cumbersome, but it works, and at least it is explicit about the observation count, also in the following case :

AGGREGATE(TABLE_DUM,(AVG(PLUS(AVERAGE(COUNT(1)VALUE(FLOAT(1)))))))

AVG
COUNT(0)VALUE(0.0)

Another example which finds the length of the longest scalar type :

AGGREGATE(RESTRICT(TYPEPROPERTIES,ISSCALAR),(LONGESTLENGTH(MAX(MAXIMUMSIZE))))

LONGESTLENGTH
1048576

Aggregation on empty inputs yields the aggregation operator's identity element, if that exists :

AGGREGATE(TABLE_DUM,(MAX(MAX(INT(1)))MIN(MIN(INT(1)))SUM(PLUS(INT(1)))))

MAX MIN SUM
-2147483648 2147483647 0

<ROSummarizeBy> := SUMMARIZEBY(<RelationalExpression>,(<AttributeName>*,),(<AggregationDef>+))

SUMMARIZEBY corresponds to the TTM operator of the same name. There is currently no equivalent in SIRA_PRISE for TTM's SUMMARIZE operator (which has the additional PER construct). <RelationalExpression> is the relational expression to be summarized, the <AttributeName>s constitute the "grouping key" in the summary, and the <AggregationDef>s define which summaries are requested.

The following example produces a summary of the catalog relvar named KEY, counting the number of keys per relvar and the sum of the KEYID values, appropriately named 'SILLY' in the output (complete result not listed) :

SUMMARIZEBY(KEY,(RELVARNAME),SILLY(PLUS(KEYID))KEYCOUNT(PLUS(INT(1))))

SILLY KEYCOUNT RELVARNAME
30 1 KEY
72 1 TABLE_DEE
70 1 TABLE_DUM
50 1 RELVAR
110 2 RELVARID
52 1 RELVARATTRIBUTE

<ROTransform> := TRANSFORM(<RelationalExpression>,(<TransformDef>*,))
<TransformDef> := <AttributeName>[(<Expression>)]

TRANSFORM is an operator that combines the functionality of EXTEND and PROJECT. The result of the operator is a relation whose heading consists of all and exactly those attributes that are named in the <TransformDefList>. All attributes of the input relation that are not mentioned in the <TransformDefList> are projected away from the result. Each <TransformDef> for an attribute in the result can optionally contain an expression. If it does, then <AttributeName> must not be a name of any attribute in the input relation. The value for the named attribute in the result relation will be the value that is obtained by evaluating the given expression (which is of course done processing the input relation on a tuple-per-tuple basis). If the <TranformDef> does not contain an expression, then the attribute name must be the name of an attribute of the input relation, and the values for those attributes will be copied into the resulting relation. Note that allthough TRANSFORM can thus achieve the same result as RENAME - (TRANSFORM(R,(B(A))) is the same as RENAME(R,(A,B))) -, it is recommended not to use TRANSFORM for this purpose.

To illustrate, we use the same expressions as previously when illustrating EXTEND, but now using TRANSFORM :

TRANSFORM(RESTRICT(TYPEPROPERTIES,ISSCALAR),(SIZERANGE(INTINTERVAL(BEGIN(MINIMUMSIZE)END(PLUS(MAXIMUMSIZE,INT(1)))))))

SIZERANGE
BEGIN(0)END(1048577)

Exercise : explain this result.

TRANSFORM(RESTRICT(TYPEPROPERTIES,ISSCALAR),(TYPENAME,SIZERANGE(INTINTERVAL(BEGIN(MINIMUMSIZE)END(PLUS(MAXIMUMSIZE,INT(1)))))))

TYPENAME SIZERANGE
NAME BEGIN(1)END(251)
FLOAT BEGIN(1)END(2)
BITS BEGIN(0)END(786432)
DATE BEGIN(1)END(2)
INT BEGIN(1)END(2)
FLOATINTERVAL BEGIN(2)END(3)
AVERAGE BEGIN(1)END(2)
INTINTERVAL BEGIN(2)END(3)
DECIMALINTERVAL BEGIN(36)END(37)
STRING BEGIN(0)END(1048577)
LONG BEGIN(18)END(19)
DECIMAL BEGIN(18)END(19)
FILENAME BEGIN(1)END(251)
LONGINTERVAL BEGIN(36)END(37)
DATEINTERVAL BEGIN(2)END(3)
BOOLEAN BEGIN(1)END(2)

<ScalarExpression> := <ScalarValueSelector>|<AttributeName>|<ScalarOperatorExpression>

Scalar expressions fall apart in the three categories indicated : scalar value selectors, invocations of scalar operators, and references to attribute names.

References to attribute names are obviously only valid if there is a scope in which those references can be resolved. Such scope is created by invocations of the relational operators RESTRICT, EXTEND, and AGGREGATE (which in turn implies that SUMMARIZEBY also creates scope, as SUMMARIZEBY builds on AGGREGATE).

<ScalarValueSelector> := <TypeName>(<LiteralValue>|<PossRepComponent>+)
<PossRepComponent> := <PossRepComponentName>(<LiteralValue>|<ScalarExpression>)

Scalar value selectors are always introduced by the type name. The specification of the value is either as a literal (e.g. INT(1) ), or else as a list of possrep component value specifications (e.g. DATE(D(...)M(...)Y(...)) ). A Possrep component value specification is always introduced by the possrep component name, followed by either a literal value (e.g. D(1) ) or else any scalar expression that evaluates to a value of the type of the named possrep component (e.g. D(INT(1)) or D(MAX(INT(1),SUB(X,5))) ).

 <ScalarOperatorExpression> := <OperatorName>(<Expression>*,)

Scalar operator expressions are introduced by the operator name, followed by the expressions denoting the operator invocation arguments.

Constructs not explicitly defined in this grammar

<RelvarName>
<AttributeName>
<TypeName>
<PossRepComponentName>
<OperatorName>
<LiteralValue>

The first five of these are names, and should obey the rules for valid SIRA_PRISE names :

The <LiteralValue> element is, obviously, subjected only to the rule that it must represent a valid value of the type of the value selector that it appears in.