Managing the database is done through "DDL" commands. In SIRA_PRISE, these commands are nothing else than regular assignment commands to catalog relvars. Two particular consequences from this approach deserve to be spelled out explicitly :
Also note that, in contrast with assignments to "user" relvars, assignments to catalog relvars are always immediately committed. That is, all assignments to catalog relvars are treated as if they were made from a connection/transaction that has its 'autocommit' flag set to true.
The DDL commands will now be discussed one by one, and any relevant remarks given as appropriate. For all updatable catalog relvars, one section will be presented with the concerned relvar's name as the section title. In the sections, the heading of the relvar will be specified as well as that relvar's keys, unless the relvar is all-key (every attribute participates in the only key).
HEADING(TYPENAME(NAME))
The first step in defining a new database is, of course, to define its logical design. This involves deciding on which data types will be used in the database to represent the information it is supposed to contain. A number of types are already provided by SIRA_PRISE, but it is unlikely that these will suffice to satisfy all your requirements. At times, you may want or need to define your own custom type. In that case, you'd need to add a new type to the SIRA_PRISE catalog. In order to do this, an implementation for the type must be available to the SIRA_PRISE server. This means that a java class must be present on the server's classpath whose name is the word 'Dbms', followed by the capitalized name of the type, followed by the word "Implementation", and whose package declaration must be the package name be.SIRAPRISE.typeimplementations. More user-/programmer- friendly procedures, such as e.g. a "packagename search path" facility, are not (yet) supported.
E.g. for adding the type ANGLE to the type system, a java class be.SIRAPRISE.typeimplementations.DbmsAngleImplementation must be available. The requirements for type implementation classes are documented in the javadoc. It is advisable not to let your type names end with the word 'interval', because SIRA_PRISE automatically generates types with such names for all types that have a comparison operator GT. Also consider that the name of the corresponding intervaltype, that is automatically generated by SIRA_PRISE if the type has an ordering operator, must itself also obey the rules for typenames, in particular the maximum length for typenames. This means that the maximum length for names of types that have an ordering operator, is only 20, not 28.
When a type is added to SIRA_PRISE's type system, SIRA_PRISE records certain properties of the type in some of its non-manipulable relvars.
It is impossible to remove a type as long as there are any references to the type, directly or indirectly. Direct references may be : an attribute that is declared to be of that type, a constrained type that is declared to be a specialization of this type, or a possrep component in some type that is declared to be of this type. Any existing indirect references to the type will obviously also prohibit the deletion of that type. The system-defined types can never be deleted, either.
Deleting a type will also delete the corresponding intervaltype based on the deleted type, if such an intervaltype exists. It is not possible to delete only the intervaltype based on some ordered base type, only the base type itself can be deleted, which will have the effect of removing both from SIRA_PRISE's type system.
Updating types is not (yet) supported. In particular, it is very strongly advised NEVER to replace an implementation class for some type with a new version. SIRA_PRISE cannot stop you from doing this, but it may disrupt the operation of the server and render your databases corrupt even if you think your new implementation is perfectly backward compatible.
HEADING(TYPENAME(NAME)POSSREPNAME(NAME)SP_EXPRESSION(STRING))
Key
{TYPENAME}
SIRA_PRISE also offers the possibility of defining custom types without providing a java implementation for the type. Naming such a type is done by adding a tuple to the UserDefinedType relvar.
The typename attribute obviously gives the type a name, which cannot be allowed to coincide with the name of any existing java-implemented or constrained type.
The possrepname attribute names the possrep that will act as the physical possrep, i.e. the possrep whose components will determine the bit encoding of any values of this type. The "structure" (i.e. the components, their types, and their physical ordering) will have to be specified in the UDTPhysicalPossrepComponent relvar.
The sp_expression attribute
defines the possrep
constraint expression for the type. The expression can be the
zero-length string, which is interpreted as if the possrep
constraint were "BOOLEAN(TRUE)", meaning that no possrep constraint
applies to the type. Any other value for the sp_expression
attribute must denote a valid SIRA_PRISE expression, in which the only
allowable variables are the component names of the physical possrep.
Note therefore that setting this expression when adding the
type, requires that all the physical possrep components are also
declared within the same MA. An example of a valid possrep
constraint expression would be 'GT(X,Y)'
where X and Y are the possrep components denoting the X and Y
coordinates of a value of type POINT. Another example of a
valid possrep constraint expression would be 'BOOLEAN(FALSE)',
allthough such expressions render the type pretty useless.
HEADING(TYPENAME(NAME)COMPONENTNAME(NAME)COMPONENTTYPENAME(NAME)LOGICALLENGTH(INT)ORDINAL(INT))
Key
{TYPENAME COMPONENTNAME}
{TYPENAME ORDINAL}
Tuples in this relvar define the components that make up the physical possrep for a user-defined type. Thus, the bit pattern of the encoding for any value of the user-defined type is determined. That bit pattern consists of the bit patterns of the encodings of the respective physical possrep component values, all concatenated together in the order specified by the ORDINAL attribute.
The componenttypename attribute names the type of the component, which can be any type, including intervaltypes or constrained types.
The logicallength attribute defines the maximum logical length for the values of the component, in the case when the component type is a variable-length one. In that case, the space reserved for the bit encoding of the value of this component will correspond to and be determined by this maximal length.
Observe that it is possible for a user-defined type to have no physical possrep components at all ! In that case, the type is a singleton type (and the byte length of its physical encoding is zero).
Adding, deleting or modifying any physical possrep component of a user-defined type is considered as a change to this type and is subject to the condition there cannot be any attribute of this type yet in a recorded relvar. A relvar is recorded if at least one RecordType tuple exists in the catalog for that relvar.
HEADING(TYPENAME(NAME)SP_EXPRESSION(STRING))
Key
{TYPENAME}
Tuples in this relvar define the expression that determines the ordering of the values of a user-defined type. If no tuple appears for a type in this relvar, then that user-defined type will be unordered, and no corresponding intervaltype will be automatically created by SIRA_PRISE. If a tuple does appear, then the user-defined type in question is an ordered one, and an intervaltype will automatically be available.
The sp_expression attribute
must contain a valid expression
whose only allowed variables are the two arguments of the implied GT()
operator. In the expression, these must be denoted as PARAMETER(V1(...))
and PARAMETER(V2(...)) ,
respectively (with the
ellipsis replaced by the typename of this type). References
to
the values of possrep components of any of these two arguments can be
made via an invocation of the appropriate THE_ operator (e.g. THE_X(PARAMETER(V1(...)))).
Changing the ordering
operator expression is considered as a change to the type and is
subject to the condition that no attribute of this type can already
exist in a recorded relvar (because that opens up the possibility that
a treeindex was already built using the former definition of the
ordering
- which might be a different one than the ordering defined by the
modified version of the ordering operator expression).
HEADING(TYPENAME(NAME)POSSREPNAME(NAME))
Tuples in this relvar define the non-physical possreps that exist for a user-defined type. Non-physical possreps give rise to the existence of additional value selector operators for the user-defined type (one for each non-physical possrep), and to additional THE_ operators for the user-defined type (one per component of the non-physical possrep).
HEADING(TYPENAME(NAME)POSSREPNAME(NAME)COMPONENTNAME(NAME)SP_EXPRESSION(STRING))
Key
{TYPENAME COMPONENTNAME}
Tuples in this relvar
define the components that together constitute a non-physical
possrep for a user-defined type. As is the case with
java-implementation-backed types, component names must be unique for a
type across all different possreps (including the physical possrep of
this user-defined type).
The sp_expression attribute defines the formula for computing the value that should be the result of an invocation of the THE_ operator corresponding to this possrep component. The only allowable variables in this expression are the component names defined for the physical possrep of this type.
HEADING(TYPENAME(NAME)POSSREPNAME(NAME)COMPONENTNAME(NAME)SP_EXPRESSION(STRING))
Key
{TYPENAME COMPONENTNAME POSSREPNAME}
Tuples in this relvar
define the formula for computing the value of a component of the
physical possrep of a user-defined type, when a value selector of that
type is invoked that corresponds to a non-physical possrep.
The
typename names the user-defined type. The possrepname
attribute
names the non-physical possrep to which' value selector invocations
this physical possrep component value selector corresponds.
The
componentname attribute names the component of the physical possrep
component which' value will be computed using the given expression.
The sp_expression attribute
defines the formula for computing the value that should be used as the
physical possrep component value when determining the bit pattern
encoding for the value that should be returned as a result of a value
selector invocation corresponding to the non-physical possrep named in
the tuple. It must be a valid expression whose only allowable
variables are the names of the possrep components of the non-physical
possrep for which this physical possrep component value selector is
defined.
It is at all times required that for each non-physical possrep and for each physical possrep component of a type there exists a UDTPPCValueSelector tuple. Or in other words, the projection of this relvar on its key should at all times be equal to the join of the UDTPossrep and the UDTPhysicalPossrepComponent relvars.
HEADING(TYPENAME(NAME)BASETYPE(NAME)EXPRESSION(STRING))
Key
{TYPENAME}
Types can also be defined to the SIRA_PRISE server as "constrained types". The idea is that a constrained type consists of a set of values which is a subset (and presumably, a proper subset) of some other known type, where that subset can be defined using some boolean expression in which the only allowable variable is of the "other known type".
Constrained types can be used as a type for attribute declarations, but they cannot be used (yet) as a type in possrep component declarations. If a constrained type's base type is ordered (and some interval type will thus automatically have been created for that base type), then adding this constrained type will not automatically cause the creation of an intervaltype corresponding to the new (constrained) type. Nor are any operators that exist for the base type, automatically made available for the constrained type. All this makes constrained types, currently, pretty damn useless. Providing more complete and more useful support for constrained types is future work.
The expression is the
boolean expression that will test, for any
value of the base type, whether that value is also a value of the
constrained type. References to the value being tested must be
specified using the syntax 'PARAMETER(V(...))',
with the
name of the base type replacing the ellipses. E.g. to specify the set
of integers [1000-9999] as a constrained type, the following expression
would be needed :
AND(GT(PARAMETER(V(INT)),INT(999)),GT(INT(10000),PARAMETER(V(INT))))
Deleting constrained types is subject to the same restrictions as deleting "ordinary" types.
Updating constrained types is subject to the condition that there are no attributes of this type that appear in recorded relvars (i.e. in a relvar that has a recordtype associated with it). Nor can there be attributes (in recorded relvars) of a type that has a possrep that has a component that is of the updated constrained type.
HEADING(TYPENAME(NAME)ATTRIBUTENAME(NAME))
Key
{ATTRIBUTENAME}
The next step in defining a logical design, is to define the attributes that will be used in it. In SIRA_PRISE, this must be done as a separate step, prior to defining/declaring relvars and their headings. This encourages uniform and consistent use of the same attribute names for the same "business information" : it helps avoid multiple distinct names popping up for the same kind of info, such as "customerid", "clientid", "custid", ... It is impossible to define an attribute of a type that is not known by the dbms as a type. If the declared type of the attribute is a variable-length one, then it is also required to register a logical length for this attribute.
It is not possible to delete an attribute as long as that attribute participates in any relvar.
Updates to attributes are subject to the condition that there is no recorded relvar in which this attribute participates.
HEADING(LOGICALLENGTH(NAME)
ATTRIBUTENAME(NAME))
Key
{ATTRIBUTENAME}
For all attributes of a type that is variable-length, it is required that the "logical length" of that attribute is declared. The "logical length" refers to, e.g., the number of allowable tokens in an attribute of type String. The exact meaning of the notion of "logical length" depends on the particular type of the attribute. E.g. the "logical length" of the RELATION type refers to the number of tuples in the relation body.
Please note that the notion of "logical length" has no direct connection with the resulting length in bytes of the physical encoding of a value of the type. One might e.g. conceive some variable-length numeric (integer) data type, where the "logical length" defines the maximum number of digits of which a valid value of the type can consist. The type implementation might nevertheless still choose to use the same encoding length (say, e.g., 8 bytes), regardless of the logical length of the attribute. SIRA_PRISE does assume, however, that there is a positive correlation between "logical length" and physical encoding length : it assumes that the physical number of bytes needed to encode a value v1 shall never be less than the number of bytes needed to encode a value v2, if the "logical length" of v1 exceeds the "logical length" of v2. The actual physical encoding lengths for all attributes are maintained automatically by SIRA_PRISE in one of its non-manipulable relvars.
The logical length of an attribute must be within the bounds defined for the corresponding variable-length type.
Deleting a logical length is only possible if the attribute itself is also deleted within the same MA, or it the attribute is updated to be of a fixed-length type within the same MA. Updating the logical length of an attribute is subject to the same conditions as updates to attributes : if there is any recorded relvar in which this attribute participates, then the update is rejected.
HEADING(RELVARNAME(NAME)RELVARPREDICATE(STRING))
Key
{RELVARNAME}
The actual definition of the database relvars starts with the addition of RELVAR tuples to the catalog. RELVAR tuples hold the name that a relvar will be known under to SIRA_PRISE (and the user of course), and the predicate that documents the relvar's intended meaning.
There is a little text substitution feature in the relvarpredicate attribute. Some functions within SIRA_PRISE are capable of replacing every occurrence within the predicate of an attributename surrounded by paragraph marks (§, Unicode 00A7), by an actual value for that attribute coming from some tuple of the involved relvar. E.g. the predicate of the catalog relvar named 'RELVAR' is "§Relvarname§ is a Relation variable whose predicate is "§Relvarpredicate§".", and some functions within SIRA_PRISE would use that information to generate, e.g., the sentence "RELVAR is a Relation variable whose predicate is "§Relvarname§ is a Relation variable whose predicate is "§Relvarpredicate§".".".
Please note that this is merely a textual facility. No checks whatsoever are made to verify that any name between paragraph marks is indeed an attribute of the relvar, or that all attributes of the relvar must be mentioned in the predicate this way.
Since all relvars must have a declared key, it is required to also define a key for this new relvar in the same multiple assignment.
It is not possible to delete a relvar as long as there are still physical recording data (i.e. RecordType tuples) for this relvar, or as long as it is still involved in any databaseconstraint, assignment constraint, triggered data action expression or virtual relvar expression.
Updating a relvar name is only possible if the relvar is not referenced in any database constraint, assignment constraint, triggered data action expression or virtual relvar expression.
HEADING(RELVARNAME(NAME)KEYID(INT))
Keys on the same relvar are distinguished from each other by their keyid. Please note that adding a KEY tuple to the catalog only has the effect of defining an empty (i.e. attributeless) key, meaning the relvar will be allowed to contain at most one single tuple. Defining a key's attributes is done by adding KeyAttribute tuples.
It is not possible to delete the last remaining key for any relvar.
Updates to keys are not supported.
HEADING(RELVARNAME(NAME)ATTRIBUTENAME(NAME))
Defining a relvar's heading is done by adding relvarattribute tuples to the catalog. One relvarattribute tuple represents one attribute in a relvar's heading. The declaration of the type corresponding to the attribute name is done at the Attribute level, not at the relvarattribute level. The type of a relvar attribute must be scalar.
It is not possible to delete a relvarattribute as long as that attribute still participates in any key on the relvar, or is referenced in any tuple constraint / transaction constraint on the relvar.
Adding attributes to a relvar for which there are already recorded tuples, obviously requires that the new attribute be assigned a value in each tuple that already appears. So this situation requires that a NewAttributeDefaultValue tuple also be added to the catalog within the same MA (if such a tuple was not already present). It may also be desirable to include this new relvar attribute immediately in one of the relvar's existing record types. This can be achieved by adding a RecordAttribute tuple within the same MA.
Updates to relvarattributes are not supported. You can do the corresponding deletions/additions instead through the appropriate delete/add commands.
HEADING(RELVARNAME(NAME)KEYID(INT)ATTRIBUTENAME(NAME))
It is not possible to delete a key attribute if the current value of the relvar has (a combination of) tuples that violate the new key specification.
Updates to keyattributes are not supported.
HEADING(RELVARNAME(NAME)CONSTRAINTEXPRESSION(STRING)ERRORCODE(LONG))
Key
{ERRORCODE}
Constraints on the values of the tuples that can be added to a given relvar can be defined in this way. Errorcode is the unique number identifying the constraint within the entire SIRA_PRISE system. Furthermore, an errorcode used to identify a tupleconstraint cannot be used for a databaseconstraint, nor for a transactionconstraint , and vice versa. In other words, errorcodes must be unique across all types of defined constraint.
Adding a new tuple constraint is impossible if any of the tuples currently present in the relvar, violate this new constraint.
The constraintexpression
attribute defines a boolean expression
that must be true for a tuple to be allowable to the constrained
relvar. The evaluation scope for this expression is strictly limited to
the single tuple being added to the relvar, i.e. the only allowable
variables in this expression are references to attributes of the tuple.
E.g. GT(ERRORCODE,LONG(4999))
defines that the errorcode
attribute in any tuple must have a value of 5000 or more. Note that a
tuple constraint TCEXP on relvar R is therefore always equivalent to
the databaseconstraint RESTRICT(R,NOT(TCEXP)).
Observe that tuple constraints are applied to the tuples as they will actually appear in the database after the assignment. Due to SIRA_PRISE's "autopacking" feature for relvars/tuples with interval-typed attributes, the tuple checked is thus not necessarily equal to the tuple presented for insertion by the user. Consecutive insertion of, say, interval-values BEGIN(1)END(7) and BEGIN(7)END(14) will cause the database to contain a single tuple BEGIN(1)END(14), and such a tuple would be found in violation of, say, a constraint specifying that "no tuple can be "longer than 7"", even if the individual tuples presented for insertion are themselves not in violation of such a constraint.
It is not possible to define, in one single MA, a new TupleConstraint along with the Relvar to which it applies. The constraint expression is checked for the validity of the expression, and that requires the Relvar and its attributes to be already known (i.e. committed to the catalog). For the same reason, it is also impossible to add a new relvar attribute to an already existing relvar, and define a new or update an existing tuple constraint such that it refers to that new attribute, within the same MA.
When deleting a tupleconstraint for which a constraintmessagetext appears in the database, that constraintmessagetext must be deleted within the same MA, or the delete will fail.
The only updates allowed to tuple constraints are those that update the expression. 'Updating' the errorcode or the relvar of a tuple constraint is impossible. If any of the tuples currently present in the relvar, violate the new version of the constraint, then the update is obviously rejected.
HEADING(CONSTRAINTLABEL(STRING)CONSTRAINTEXPRESSION(STRING)ERRORCODE(LONG))
Key
{ERRORCODE}
All database constraints must be defined in this way (except for relvar keys and tuple constraints of course). Errorcode is the unique number identifying the constraint within the entire SIRA_PRISE system. Furthermore, an errorcode used to identify a databaseconstraint cannot be used for a tuple constraint, nor for a transaction constraint, and vice versa. In other words, errorcodes must be unique across all types of defined constraint.
Constraintlabel is a textual description of the condition that the constraint enforces, e.g. "Each relvar must have at least one key.".
The constraintexpression
attribute defines a relational
expression that must be empty for the database value to be valid. E.g. SEMIMINUS(KEY,PROJECT(RELVAR,(RELVARNAME)))
defines that there cannot be tuples in the key relvar with a relvarname
attribute value that does not appear in any relvar tuple.
Adding a new database constraint is impossible if the current database value is such that the new constraint is violated.
Since adding a new database constraint obviously involves compilation of the constraint expression, this constraint expression is not allowed to hold references to anything that is newly created within the same MA as the databaseconstraint itself. E.g. adding a new relvar and adding a databaseconstraint within the same MA to the effect that that new relvar must be empty, will fail.
When deleting a databaseconstraint for which a constraintmessagetext has previously been added, that constraintmessagetext must be deleted within the same MA, or the delete will fail.
Updating a database constraint is impossible if the current database value makes the new version of the constraint expression nonempty.
HEADING(CONSTRAINTLABEL(STRING)
CONSTRAINTEXPRESSION(STRING) ERRORCODE(LONG) RELVARNAME(NAME)
OPERATIONTYPE(INT))
Key
{ERRORCODE}
Transaction constraints are a special type of constraint that constrain, not a database, relation or tuple value, but which update operations are allowable, given a certain transaction context, instead. For more information on how to use transaction constraints, please see "securing your databases with SIRA_PRISE".
The constraintlabel, constraintexpression and errorcode have the same meaning as with database constraints. The relvarname and operationtype attributes define which type of update operation (on what relvar) is constrained by this transaction constraint : operationtype value 1 defines a constraint on delete operations on the named relvar, 2 defines a constraint on insert operations on the named relvar.
As is also the case with database constraints, since adding a transaction constraint involves compilation of the constraint expression, it is impossible to add a new transaction constraint whose expression holds any reference to anything that is newly created in the same MA.
HEADING(CONSTRAINTLABEL(STRING)
SP_EXPRESSION(STRING) ERRORCODE(LONG))
Key
{ERRORCODE}
Assignment constraints are the successor to transaction constraints, which are now considered deprecated. For more information on how to use assignment constraints, please see "securing your databases with SIRA_PRISE".
The constraintlabel, sp_expression and errorcode have the same meaning as with database constraints. Meaning in particular that sp_expression defines a relational expression that must evaluate to the empty relation for the constraint to be satisfied.
As is also the case with database constraints, since adding an assignment constraint involves compilation of the constraint expression, it is impossible to add a new assignment constraint whose expression holds any reference to anything that is newly created in the same MA.
HEADING(ERRORCODE(LONG)
RELVARNAME(NAME) OPERATIONTYPE(INT))
AssignmentConstraintCheck tuples determine for which update operation types the Assignment Constraint identified by the errorcode attribute is to be checked.
The relvarname attribute names a relvar for which the Assignment Constraint identified by the errorcode attribute is to be checked (when some update operation is carried out to that relvar), the operationtype attribute identifies whether the constraint will be checked if the assignment carried out is an insertion (operationtype = 2) or a deletion (operationtype = 1). Since an 'update-type-assignment' (i.e. an UPDATE statement was issued rather than ADD, ASSERT, DELETE or UNASSERT) comprises both a deletion and an insertion, this implies that Assignment Constraints will always be checked for UPDATE statements, regardless of the operationtype listed, thus : operationtype 2 means the assignment constraint will be checked for ADD, ASSERT and UPDATE statements issued to the relvar, and operationtype 1 means the assignment constraint will be checked for DELETE, UNASSERT and UPDATE statements issued to the relvar.
HEADING(CONSTRAINTMESSAGETEXT(STRING)ERRORCODE(LONG))
Key
{ERRORCODE}
The text to be returned to the SIRA_PRISE client when the constraint with the specified errorcode is violated, can optionally be added to the SIRA_PRISE system this way. If such a text is returned to a client, it is first inspected for the presence of paragraph marks (§) surrounding an attribute name. All such constructs in the message text are replaced with the actual value of that particular attribute in the tuple that causes the constraint violation. Please note that this is merely a textual facility. No checks whatsoever are made to verify that any name between paragraph marks is indeed an attribute of the corresponding constraint expression (database and/or assignment constraints) or relvar (tuple and/or transaction constraints).
The only updates allowed for constraintmessagetexts are those that update the constraintmessagetext attribute. 'Updates' to the errorcode attribute are not allowed.
HEADING(DATAACTIONNAME(NAME)
RELVARNAME(NAME) OPERATIONTYPE(INT) TARGETRELVARNAME(NAME)
TRIGGEREDOPERATIONTYPE(INT) ISDISTINCT(BOOLEAN)
BYPASSASSGCONSTRAINTS(BOOLEAN) SP_EXPRESSION(STRING))
Key
{DATAACTIONNAME}
{RELVARNAME
OPERATIONTYPE
TARGETRELVARNAME
TRIGGEREDOPERATIONTYPE
ISDISTINCT
BYPASSASSGCONSTRAINTS}
TriggeredDataAction tuples
define which triggered data actions SIRA_PRISE will undertake as a
consequence of some assignment carried out to some relvar.
The
'dataactionname' attribute assigns a name to the data trigger.
The 'relvarname' and 'operationtype' attributes identify the
relvar and the assignment type that will 'fire' the trigger.
The values for the operationtype attribute are as for
AssignmentConstraintChecks : 1 for DELETE/UNASSERT/UPDATE, and 2 for
ADD/ASSERT/UPDATE.
The 'targetrelvarname',
'triggeredoperationtype' and 'isdistinct' attributes
identify the target relvar for the trigger and the assignment type that
will be carried out against the named target relvar. The
values for the operationtype attribute are as for
AssignmentConstraintChecks : 1 for DELETE/UNASSERT, and 2 for
ADD/ASSERT. The value of the 'isdistinct' attribute
determines whether the assignment carried out against the named target
relvar is the 'distinct' (ADD/DELETE) version or the 'nondistinct'
(ASSERT/UNASSERT) version of the assignment. It is not
possible to trigger an UPDATE statement directly. This must
be achieved through triggering both the appropriate insertion and
deletion for the target relvar.
By setting the 'bypassassgconstraints' to true, it becomes possible to issue updates from within a triggered data action to a relvar that would otherwise be prohibited as a consequence of some assignment constraint on that target relvar. This feature may make SIRA_PRISE's triggered data actions an appropriate means for storing derived, "redundant" data inside the dbms, and this under control of the dbms, so that the application doesn't need to worry about synchronizing the derived data with each update (and making it even impossible for the application to corrupt the derived data).
The 'sp_expression' attribute defines the expression to compute the relation value that will be inserted into/deleted from the target relvar. It must therefore denote a relational expression of the same relation type as the target relvar. Typically, this expression will contain invocations of ITRANSACTION(relvarname) or DTRANSACTION(relvarname), or even both. Note however that this is by no means required.
Triggered data actions can be recursive, i.e. a trigger fired by an update to some relvar can have that same relvar as its target, and such recursion can also be caused "indirectly". There is a builtin limit on the number of rounds that SIRA_PRISE will run recursively when determining the triggered data actions to carry out. This limit should be high enough for all practical purposes, meaning that if you run into a "probable infinite recursion" error message, it is indeed very likely that you have set up the triggered data actions in such a way that without the limited number of rounds, they would indeed run forever. Please observe that while the recursion feature is supported and available, we strongly recommend that you try and avoid such designs in which they are necessary.
When dealing with recursive triggered data actions, one thing must be noted about the result returned for invocations of ITRANSACTION() and DTRANSACTION(). In the first recursive round, these operators return the insertions and deletions as they are implied by the assignment statement as issued by the user. In the second recursive round, invocations of these operators refer to the set of assignments that was the RESULT of the first recursive round. In the third recursive round, invocations of these operators refer to the set of assignments that was the RESULT of the second recursive round, etc. etc.
HEADING(FILENAME(NAME)PAGESIZE(INT))
Key
{FILENAME}
Once your logical design, or a part of it, is complete, you will want to start defining a physical design. Before you can do this, you must ensure that all the resources needed by such a physical design are created. To this end, you will probably need to define files and storage spaces. (The space calculations functions in the web client package may be a significant help in accomplishing this task adequately.)
One thing you will almost surely need to create/format are files for SIRA_PRISE to record its records in. Doing this is done by adding DBMSFILE tuples to the catalog. Adding such a tuple will also create the file in the appropriate directory in the server's file system. The pagesize attribute must have a value of at least 4096 and not exceeding 65536.
Note that the command as given above does nothing but declare the existence of the physical file, and create that file in the file system with only a file header. In order to actually record anything in this file, storagespaces must be appended to it. Please note that, allthough the side-effect of physical file creation is recoverable (SIRA_PRISE removes the newly created file as part of its rollback procedure - file deletion is also recoverable in a similar way), it is nevertheless still a good idea to reduce the possibility of rollbacks to an absolute minimum by issuing these commands only as a single-assignment, i.e. not as part of an MA.
Deleting a dbms file will also remove the file from server's file system. Nevertheless, it may occasionally occur that the delete fails because some internal components of the system still have this file in use. In that case, do the delete immediately after SIRA_PRISE has been restarted.
Updating the name of a file is supported, and the update will also be reflected in the file system. Updating the page size is also possible, but note that this operation involves a complete reformatting of the file, during which period NONE of the file's information will be accessible to the SIRA_PRISE users. Use only with care.
HEADING(FILENAME(NAME)
STORAGESPACEID(INT) PAGECOUNT(INT))
Key
{FILENAME STORAGESPACEID}
Adding a storagespace tuple to the catalog will append a storagespace to the specified file, and initialize/format it with the specified number of pages (the size of which is defined at the dbms file level). Computing an adequate number of pages, given an expected data volume for a relvar, can be done using the space calculations functions in the supplied web client package.
Since the pages are immediately initialized/formatted, and the format of the system information on those pages will depend on the particular purpose that the storagespace serves, it must also be known what 'kind' of storagespace this will be. It is therefore necessary to also add either a dataspace or some indexspace tuple within the same MA. See the subsequent sections for details on dataspaces and indexspaces.
Deleting storage spaces is supported, but note that this operation involves a complete reorganization of the entire dbms file the storage space appears in, during which period NONE of the information contained in the ENTIRE dbms file will be accessible for the users. Use only with care.
Updating storagespace tuples (e.g. for extending the number of pages) is not supported. If you expect a need to be able to extend the number of pages in some storage space after it has initially been defined with some "smallish" number, your best option is to define StorageSpaceExtents.
HEADING(FILENAME(NAME)
STORAGESPACEID(INT)
GAPCOMPRESSIONTHRESHOLD(INT) MAXIMUMGAPS(INT))
Key
{FILENAME STORAGESPACEID}
Adding a dataspace tuple to the catalog will mark a newly appended storagespace as being intended for recording data records.
The gap compression threshold and the maximum gapcount define the way in which SIRA_PRISE will cope with 'gaps' that may appear on a data page if and when tuples, and thus the corresponding records, are removed from some relvar.
Changing an existing dataspace definition to improve the gap handling is not -yet- supported.
HEADING(FILENAME(NAME)
STORAGESPACEID(INT))
This will mark a newly appended storagespace as being intended for recording tree indexes. Treeindexes will maintain some inorder sequence of the tuples (of the relvar whose tuple records are) indexed, according to the precise definition of the index (its list of index components) and the relevant comparison operators.
Deleting treeindexspaces is supported, with the same caveat as for all storagespace deletions. Updating treeindexspaces is not supported.
HEADING(FILENAME(NAME)
STORAGESPACEID(INT))
Adding a hashingindexspace tuple to the catalog will mark a newly appended storagespace as being intended for recording hashing indexes. Hashing indexes will maintain a list of computed checksums for the tuples (of the relvar whose tuple records are) indexed, according to the precise definition of the index (its list of index components) and the relevant type implementations (which are responsible for creating the appropriate bit pattern of each value to be recorded).
Deleting hashingindexspaces is supported, with the same caveat as for all storagespace deletions. Updating hashingindexspaces is not supported.
HEADING(FILENAME(NAME)
STORAGESPACEID(INT)
GAPCOMPRESSIONTHRESHOLD(INT) MAXIMUMGAPS(INT))
Key
{FILENAME STORAGESPACEID}
As with HashingIndexSpaces, this will mark a newly appended storagespace as being intended for recording hashing indexes. The difference with a HashingIndexSpace is that a HashingIndexSpace can contain only one single index, whereas MultiHashingIndexSpaces can contain more than one. As with DataSpaces, the management of gaps that appear on the page in the event of removal of some index entry, is based on the values provided for the gap compression threshold and the maximum number of allowable gaps.
Deleting multihashingindexspaces is supported, with the same caveat as for all storagespace deletions. Updating multihashingindexspace tuples to finetune the gap handling is not -yet- supported.
HEADING(FILENAME(NAME)
STORAGESPACEID(INT)
EXTENTSCOUNT(INT))
Key
{FILENAME STORAGESPACEID}
Starting with version 1.1, all storage spaces are extensible. A dbmsfile has a defined pagesize, and all storagespaces within it have a defined pagecount, putting an absolute upper limit on the storage capacity of the storage space. StorageSpaceExtents serve the purpose of overcoming this limitation. Any StorageSpace can be defined to be "extensible", by adding a StorageSpaceExtents tuple for that storage space to the catalog. The EXTENTSCOUNT attribute defines the number of times that SIRA_PRISE will attempt to double the storage capacity of the storage space. So an EXTENTSCOUNT value of 4 will allow a storage space to grow to 16 (2^4) times the original size.
If no StorageSpaceExtents tuple exists for some storage space, then SIRA_PRISE assumes the EXTENTSCOUNT value to be zero. Also note that SIRA_PRISE will always "top off" the requested extents count such that the total number of pages in an "extended" storage space will never exceed the value of 2^31 (2147483648). This means that for a storage space of, say, 1000 pages, the actual number of extent attempts will never exceed 21, even if the EXTENTSCOUNT attribute value for this storage space shows a higher value (1000 * 2^21 = 2097152000, which cannot be doubled anymore without exceeding 2^31).
StorageSpaceExtents tuples can only be added/deleted/updated for storage spaces that are completely unused, i.e. only for dataspaces that have been defined, but are not yet used for any record type, and only for index spaces that have been defined, but are not yet used for recording any index.
Please observe, therefore, that this feature is only intended as an "escape route" for sudden unexpected changes in volumes, and not as a replacement for careful capacity planning. Effectively allowing a storage space to grow to thousands of times its original size can reasonably be expected to be "rewarded" with significant performance degradation.
HEADING(FILENAME(NAME)
STORAGESPACEID(INT)
RELVARNAME(NAME) RECORDTYPENAME(NAME) INDEXNAME(NAME))
Keys
{RELVARNAME RECORDTYPENAME} and {RELVARNAME INDEXNAME}
Adding a RecordType tuple will cause a new recordtype to be associated with the named relvar. Records of this type will be recorded in the storagespace identified by filename/storagespaceid, which must be a dataspace. The physical location of the records within that dataspace will be determined by the index whose name is given in the indexname attribute.
Since the physical location of this new record type's records cannot be managed unless a hashing index exists for this recordtype, it is required to add an index tuple defining this hashing index within the same MA as well (with the same indexname value as the one in the new recordtdype tuple), or the add will fail.
Note that adding the RecordType has no other effect than just declaring its existence, i.e. no actual attributes of the relvar are made part of this recordtype by this declaration, and the RecordType itself remains "empty". It is likely that RecordAttribute tuples need also be added within the same MA to specify which attributes of the relvar are to appear in this recordtype. This can be done in two distinct steps, but this will cause two distinct "conversion" operations to be performed for the record type : one when the recordtype is defined to write an "empty" record for each tuple of the relvar, and a second one when the record attributes are added to convert the "empty" records to records containing some attribute values.
More than one recordtype can be associated with a relvar, in which case more than one record will exist (possibly at different physical locations in the same dataspace, or in different dataspaces of the same file, or in different files) for each single tuple of that relvar. In any case, one single record type does not necessarily hold all of the relvar's attributes. If an attribute participates in a relvar, but not in any record of that relvar, then the values for this attribute in that relvar's tuples are recorded directly in the server's file system, rather than in records within dataspaces within dbms files.
In the case where more than one recordtype is associated with a relvar, it is required that a TupleIndex be defined on each record type of that relvar.
As long as a relvar has a nonempty value, removing its last RecordType is prohibited. Nor can recordtype data be removed as long as there are still indexes on this recordtype. Index definitions on this record type must therefore be removed either before this delete, or at the very latest within the same MA. The locator index (this is the index named in the recordtype tuple) can (and must) only be deleted in the same MA that deletes the record type.
Obviously, removing a recordtype associated with some relvar, will also require removing the RecordAttribute tuples for this recordtype.
Changing existing recordtype data is not (yet) supported. Nor is "simulating" such an update by issuing a delete and a "compensating" add within the same MA. Any change that is needed in recordtype definitions can always be performed by first adding a new recordtype that reflects the desired properties, and then afterwards delting the old one.
HEADING(FILENAME(NAME)
STORAGESPACEID(INT)
RELVARNAME(NAME) RECORDTYPENAME(NAME) INDEXNAME(NAME))
Key
{RELVARNAME INDEXNAME}
This will cause an index of the specified name to be recorded on the (records of the) specified recordtype. This index will be recorded in the storagespace identified by filename and storagespaceid, which must be an indexspace (either tree- or (multi)hashing-). The type of index space will determine the type of index.
Note that the command as presented above only declares the existence of the index. No attributes will actually be indexed by this declaration. The "value" that is indexed for each (record for a) relvar tuple if no index components are added, will resemble the empty tuple. Such an index will thus effectively constitute just a simple sequential list of tuple-id's (and cause the predictable performance characteristics on large relvars). It is thus certainly advisable to define the components of this index within the same MA, if the relvar already has recorded tuples.
Deleting an index will also automatically delete all the indexcomponents for this index. If the index is recorded in a single-index storage space (a TreeIndexSpace or a HashingIndexSpace), then the indexspace in which the index is recorded will become available for recording some other index.
Changing existing index declarations is not (yet) supported. Modifying index definitions can always be achieved by first defining a new index (according to the new characteristics), and then deleting the old one.
HEADING(INDEXNAME(NAME)
RELVARNAME(NAME)
RECORDTYPENAME(NAME))
Key
{RELVARNAME, RECORDTYPENAME}
This will designate the named index to become a "tuple" index for all tuples of the named relvar. This index is required to be a hashing index (meaning the index must be stored in either a hashingindexspace or a multihashingindexspace). Since a tupleindex is, like the name says, an index much like any other, the corresponding index tuple must also be added within the same MA.
Tuple indexes are special in the sense that the component that is indexed is the internal tuple ID of any tuple. No other components are allowed to be part of a tuple index, and the index thus has no user-visible components.
Deleting TupleIndex tuples will end the designation of the named index as a tupleindex, but will not delete the index itself. The index itself continues to exist, but will be rebuilt to reflect its new structure having "no components at all" (a tupleindex does have a component, the internal tupleid, even if that component is never made visible to the user).
Updates to tupleindexes are not supported.
HEADING(RELVARNAME(NAME)
RECORDTYPENAME(NAME)
ATTRIBUTENAME(NAME) ORDINAL(INT))
Keys
{RELVARNAME RECORDTYPENAME ATTRIBUTENAME} and
{RELVARNAME RECORDTYPENAME ORDINAL}
This will determine the ordinal position of the (encoding of the) value of the named attribute in the records for some tuple of the named relvar. Ordinal positions are not required to form a set of strictly consecutive numbers. They just determine the physical position of an encoding of a value in a record, in the sense that the ordering of the attribute's value encodings is the same as the ordering of the corresponding ordinal numbers. So it is possible to define recordattributes with ordinal numbers that are a multiple of (e.g) 10, so that later on, attributes can easily be added "in the middle of a record" (if there is a good reason to do this).
When deleting recordattribute tuples, existing records of this record type will be reformatted so that they no longer contain values for the attribute being removed. If there are no other recordtypes left for this relvar in which the concerned attribute appears, then the recordings of the attribute values are moved to resource files directly in the server's file system.
Updates to recordattributes (e.g. to change an ordinal position and reformat the records) are not (yet) supported.
HEADING(RELVARNAME(NAME)
ORDINAL(INT) INDEXNAME(NAME)
EXPRESSION(STRING))
Key
{RELVARNAME INDEXNAME ORDINAL}
Indexcomponent tuples
define the structure of an index. The
'relvarname' and 'indexname' attributes identify the index, the
'ordinal' attribute identifies the position of the index component
relative to the others, and the 'expression' attribute defines the
expression that defines the value that will be indexed for some tuple
of the indexed relvar. This expression is not limited to attribute
references exclusively. E.g. if a relvar has an interval-typed
attribute V, then it is possible to have an index on THE_BEGIN(V).
However, there is currently little point in doing this, since
SIRA_PRISE is not yet smart enough to actually use such indexes for
purposes of query optimisation (RESTRICT(R,EQ(THE_BEGIN(V),...))).
Any addition, deletion or update of an indexcomponent to an existing index will entirely rebuild the index.
HEADING(RELVARNAME(NAME)
VALUE(STRING) ATTRIBUTENAME(NAME))
Key
{RELVARNAME ATTRIBUTENAME}
NewAttributeDefaultValues are used by SIRA_PRISE when an attribute is added to a relvar that already has recorded tuples. The record(s) that is(are) extended with the additional attribute, will record the given value for the new attribute in every tuple record. The given value must be a valid textual representation of a value of the type of the new attribute. It is not yet possible to enter an expression that refers to other attributes in the tuple or to other relvars in the database.
Deleting NewAttributeDefaultValues can be done immediately after the addition of the relvar attribute has been carried out successfully (but not within the same MA, obviously). This is not necessary, but it will be impossible to remove the attribute from the relvar as long as the NewAttributeDefaultValue is still present in the catalog.
Updates to NewAttributeDefaultValues are not supported (it would be pretty pointless to do so since the information it contains is not used any more, anywhere).
HEADING(RELVARNAME(NAME)
ATTRIBUTENAME(NAME)
VALUECOUNT(LONG) FREQUENCY(FLOAT) STDDEV(FLOAT))
Key
{RELVARNAME ATTRIBUTENAME}
AttributeValueDistributions are used by SIRA_PRISE when making estimates about the cost of various possible evaluation strategies for a relational expression. In particular, these statistics are useful for determining whether and to what extent a restriction involving an equality test on some attribute implies a reduction factor in the cardinality of the result.
In principle, users should never update this relvar directly, because the contents of this relvar are also updated as a result of executing a STATISTICS command, but even so the possibility to update this relvar directly remains open.
The frequency attribute is the average of the appearance counts of each appearing value, the stddev attribute is the standard deviation on that average. A comparatively low standard deviation thus implies a fairly "even" distribution of appearing values, whereas a comparatively high standard deviation means that some values appear significantly more than the average recorded here.
For more detailed information about statistics, please see the discussion of the STATISTICS command elsewhere.
HEADING(RELVARNAME(NAME)
CARDINALITY(LONG))
Key
{RELVARNAME}
This relvar records the tuple count in a relvar after successfull execution of a STATISTICS command. The information is used to make cardinality estimates when SIRA_PRISE needs to decide on an evaluation strategy for some relational expression.
As with the attribute value distribution statistics, users shouldn't need to update this relvar directly, but they can do so if they see a good reason for it.
HEADING(CLIENT(NAME)
TRUSTAUTHENTICATION(BOOLEAN))
Key
{CLIENT}
This relvar records the identities of the clients that can successfully connect to the SIRA_PRISE server. The TRUSTAUTHENTICATION field indicates whether a user authentication by the client described by the tuple, will be trusted by SIRA_PRISE.
For more detailed information on how SIRA_PRISE uses the information in this relvar, please see "securing your databases with SIRA_PRISE".
HEADING(CLIENT(NAME)
ALGORITHM(NAME) CERTIFICATE(BITS))
Key
{CLIENT ALGORITHM}
This relvar records the certificates that can be used for authenticating the clients who wish to make a connection to the SIRA_PRISE server. The ALGORITHM attribute identifies a signing algorithm that can be used by the client the tuple describes, and the CERTIFICATE attribute records the X.509 certificate that must be used by SIRA_PRISE to authenticate the named client using the named algorithm.
HEADING(USERID(NAME))
This relvar records the identifications of the users of the database managed by the engine.
HEADING(USERID(NAME)
ALGORITHM(NAME) CERTIFICATE(BITS))
Key
{USERID ALGORITHM}
This relvar records the certificates that can be used for authenticating the identity of a user who wishes to start a transaction. The ALGORITHM attribute names a signing algorithm this user might use to authenticate himself, and the CERTIFICATE attribute records the X.509 certificate, pertaining to this user, that is to be used when this user authenticates himself using the named algorithm.
HEADING(RELVARNAME(NAME)
SP_EXPRESSION(STRING))
Key
{RELVARNAME}
This relvar records the definitions of all virtual relvars known to SIRA_PRISE.
Adding a definition of a virtual relvar to the system will involve compiling the expression defining the virtual relvar. It is therefore not possible to define a virtual relvar if its defining expression references anything that is created within the same MA. Virtual relvars can be used in exactly the same way as any "regular" relvar, except they can't be updated (meaning, virtual relvars can only be referenced in expressions, but cannot appear as the target in an assignment). References to virtual relvars evaluate to the value of their defining expression.
The expression defining a virtual relvar can reference other virtual relvars. It is, however, not possible to define or modify a virtual relvar in such a way that any virtual relvar would directly or implicitly reference itself. Whenever any change is made to the definition of any relvar (whether base or virtual), all the virtual relvars that reference this modified relvar either directly or implicitly, are marked "invalid". Virtual relvars that have been marked "invalid" will be recompiled the first moment they are referenced. This means in particular that if some change to some relvar R renders the expression defining some other virtual relvar V invalid, then this will not be taken as a reason to reject the change to R. The problem will only be noticed the first time a user requests to evaluate some reference to V.
This section provides a little more information on the catalog relvars that are not 'included' in SIRA_PRISE's DDL, and are thus non-updatable from a user's perspective. Obviously, updates do occur in these relvars, but that is only possible as a side-effect of some other DDL specified by the user. As with all catalog relvars, these 'non-updatable' relvars can be queried, however, which is why this section provides a brief explanation about their meaning. This meaning can, incidentally, also be queried by looking at the (value of the) RelvarPredicate attribute in the Relvar tuple for such a relvar.
The TupleConstraintAttribute relvar records which relvar attributes are a determinant factor in which tuple/transaction constraint on what relvar. It prohibits the renaming of an attribute after a reference to this attribute has been included in a tuple/transaction constraint expression.
These relation "variables"
hold the relation values RELATION(HEADING()BODY(TUPLE()))
and RELATION(HEADING()BODY()),
respectively.
The IndexAttribute relvar records which attributes are referenced in which indexcomponent. Its purpose is similar to that of the TupleConstraintAttribute relvar : to prevent renames of attributes that would render a valid existing expression (in this case an expression that defines the value of an index component) invalid (because of illegal attribute references).
The SystemDefinedType relvar lists which types are system-defined, preventing their deletion from the catalog.
The AttributePhysicalLength relvar records the needed encoding length in bytes for each defined attribute. Its role is to prevent the user from defining records (or indexes) that are larger than can fit within the page size of the storage space they are recorded in.
These relvars list the internal identification number for every defined relvar, every defined index, and every defined record type, respectively. Please observe that it is unadvisable to use these values anywhere. In a recovery scenario using rollforwards, these relvars are guaranteed not to hold the same ID values as the ones they had when the original updates occurred.
The PossRep relvar lists the names of the possible representations for every type added to the SIRA_PRISE catalog, and the names of the components of such a possrep, respectively. Note that these are obtained from the type's implementation class.
The TypeProperties relvar lists some properties of each type defined to SIRA_PRISE. As with possreps, this information is obtained from the type's implementation class at the time the type is added to the catalog.
The IntervalType relvar lists the names of the intervaltypes known to SIRA_PRISE, and the name of the base type to which they correspond.
This relvar lists, for each database constraint, which kinds of update to which relvars can potentially cause a violation of that database constraint (and must therefore be checked when such an update occurs to a listed relvar).
This relvar lists the system-provided clients that can successfully connect to the server.
This relvar lists system-provided users that can succesfully start a transaction.
This relvar lists the system-defined relvars. This relvar is used to prevent the user from modifying these relvars.
This relvar lists the system-defined constraints. This relvar is used to prevent the user from modifying these constraints.
This relvar lists the direct references to other relvars made by the expression defining a virtual relvar. For finding all references, including the implicit ones, apply the TCLOSE operator to this relvar.
This relvar lists the direct references to other relvars made by the expression defining a triggered data action.
|
TypeName |
Description |
|---|---|
|
INT |
The usual range of 32-bit integers from -2.147.483.648 to 2.147.483.647. |
|
LONG |
The usual range of 64-bit integers. |
|
DECIMAL |
A non-integer numeric type. The maximum number of digits in a decimal value is 18. Precision in computations is maintained up to this maximum number of digits. |
|
CHAR |
A single character of the java character set (== all UTF characters with an encoding value between 0 and 65K ???). |
|
STRING |
All strings that can be composed using the characters of the CHAR type, limited to a maximum length of (close to) 1G (1073741820) tokens. |
|
NAME |
String type that allows only alphabet characters, and treats its values as case-insensitive. Minimum logical length is 1 (so "empty" names are invalid), maximum length is 250. |
|
FILENAME |
Case-insensitive string type specifically destined to deal with filenames (thus mostly for internal use by SIRA_PRISE). |
|
FLOAT |
Intended to correspond to IEEE floating point. Implementation uses the java double type. |
|
DATE |
Gregorian calendar dates. |
|
TIMEOFDAY |
An hours-minutes-seconds-nanoseconds time value |
|
BOOLEAN |
The obvious type for truth values. |
|
BITS |
A type destined for storing all types of "binary" or "raw" content. Currently limited to a maximum length of 805306365 byte positions. |
|
ANGLE |
Type provided to demonstrate the plug-in type capabilities of SIRA_PRISE. |
|
RELATION |
No comment needed. |
In order to allow the users to implement their own operators that act on these types, the physical encoding information (i.e. the bits-and-bytes patterns of the ScalarValueBuffers that hold values of these types) is provided in the javadoc (follow the links under "known implementing classes" to get to the details for a particular type).
|
Operator name and argument types |
Comments |
|---|---|
|
EQ(...,...) |
Provided for all types |
|
GT(...,...) |
Provided for types INT, LONG, FLOAT, DECIMAL, DATE and TIMEOFDAY. |
|
XOR(boolean,boolean) |
|
|
AND(boolean,boolean) |
|
|
OR(boolean,boolean) |
|
|
NOT(boolean) |
|
|
ABS(...) |
Returns the absolute value of its argument. Provided for the types INT, LONG, FLOAT. |
|
MAX(...,...) |
Returns the greater of its two arguments. Provided for the types INT, LONG, FLOAT, DATE and TIMEOFDAY. |
|
MIN(...,...) |
Returns the lower of its two arguments. Provided for the types INT, LONG, FLOAT, DATE and TIMEOFDAY. |
|
PLUS(...,...) |
Returns the sum of its two arguments. Provided for the types INT, LONG, FLOAT, DECIMAL and TIMEOFDAY. |
|
SUB(...,...) |
Returns the difference of its two arguments. Provided for the types INT, LONG, FLOAT and TIMEOFDAY. |
|
MULT(...,...) |
Returns the product of its two arguments. Provided for the types INT, LONG, and FLOAT. |
|
DIV(...,...) |
Returns the result of the division of the first argument by the second. Provided for the types INT, LONG, and FLOAT. |
|
ROUND(float,float) |
Out of the set of whole multiples of the second argument, returns the value that is closest to the first argument. |
|
EXP(float,float) |
Returns the result of the first argument raised to the power of the second. |
|
EXP(float) |
Returns the result of e raised to the power of the argument. |
|
LN(float) |
Returns the natural logarithm of the argument. |
|
SQRT(float) |
Returns the square root of the argument. |
|
CEIL(...,...) |
Returns the smallest number that is an exact multiple of the second argument and greater than or equal to the first. Provided for types INT and LONG. |
|
FLOOR(...,...) |
Returns the largest number that is an exact multiple of the second argument and smaller than or equal to the first. Provided for types INT and LONG. |
|
MOD(...,...) |
Returns the number c such that if c = mod(a,b), then a = (a / b) + c. Provided for types INT and LONG. |
|
COS(angle) |
Returns the cosine of the argument |
|
COSEC(angle) |
Returns the cosecant of the argument |
|
COTAN(angle) |
Returns the cotangent of the argument |
|
SEC(angle) |
Returns the secant of the argument |
|
SIN(angle) |
Returns the sine of the argument |
|
TAN(angle) |
Returns the tangent of the argument |
|
ACOS(float) |
Returns the arc cosine of the argument |
|
ACOSEC(float) |
Returns the arc cosecant of the argument |
|
ACOTAN(float) |
Returns the arc cotangent of the argument |
|
ASEC(float) |
Returns the arc secant of the argument |
|
ASIN(float) |
Returns the arc sine of the argument |
|
ATAN(float) |
Returns the arc tangent of the argument |
|
LENGTH(longinterval) |
|
|
LENGTH(intinterval) |
|
|
LENGTH(string) |
Returns the number of tokens that appear in the string argument. |
|
CONCAT(string, string) |
Returns the result of concatenating the two string arguments. |
|
UPPERCASE(string) |
Returns the result of converting the string argument to uppercase. Note that this can be dependent on locale of the machine where the server is running. |
|
LOWERCASE(string) |
Returns the result of converting the string argument to lowercase. Note that this can be dependent on locale of the machine where the server is running. |
|
SUBSTR(string, start) |
Returns the string that consists of the same series of tokens that appear in the string argument starting at the position designated by the integer argument start. The first position is position 1 (i.e. positions are not offsets). If the start value is less than 1, then the entire string argument is returned. If the start value is larger than the given string's actual length, then the empty string is returned. |
|
SUBSTR(string, start, end) |
Returns the string that consists of the same series of tokens that appear in the string argument starting at the position designated by the integer argument 'start', and before the position designated by the integer argument 'end' (i.e. the token at position 'end' is not included in the result). The first position is position 1 (i.e. positions are not offsets). If the 'start' value is outside the range [1-actual length of the string], then it is adjusted to fall within that range. After that, if the 'end' argument is outside the range [start-actual length of the string], then it is also adjusted to fall within that range. If, after adjustment, 'end' is equal to 'start', then the empty string is returned. |
|
MATCHES(string, regex) |
Returns true if the first argument "matches" the second, false otherwise. The second argument must be a valid Java Regular Expression. |
|
BEGINSWITH(..., ...) |
Returns true if the first argument "begins with" the second, false otherwise. Provided for types STRING and NAME. |
|
ENDSWITH(..., ...) |
Returns true if the first argument "ends with" the second, false otherwise. Provided for types STRING and NAME. |
|
DATESHIFT(date,years,months,days) |
Years, months and days are all integer arguments. Only one is allowed to be nonzero. Returns the date that is obtained by adding that nonzero value to the given date. |
|
FIRST...() |
Returns the lowest value of the type whose name follows 'FIRST' in the operator name. Provided for types INT, LONG and DATE. |
|
LAST...() |
Returns the lowest value of the type whose name follows 'FIRST' in the operator name. Provided for types INT, LONG and DATE. |
|
NEXT(...) |
Returns the value that is next-higher to the given argument. Provided for types INT, LONG and DATE. |
|
PRIOR(...) |
Returns the value that is next-lower to the given argument. Provided for types INT, LONG and DATE. |
|
AFTER(anyintervaltype,anyintervaltype) |
See following section for more detail on intervaltypes and their operators. |
|
BEFORE(...,...) |
|
|
BEGINS(...,...) |
|
|
ENDS(...,...) |
|
|
CONTAINS(...,...) |
|
|
WITHIN(...,...) |
|
|
ISEMPTYINTERVAL(...) |
|
|
ISINFINITEINTERVAL(...) |
|
|
MEETS(...,...) |
|
|
MERGES(...,...) |
|
|
OVERLAPS(...,...) |
|
|
IINTERSECT(...,...) |
|
|
IUNION(...,...) |
|
|
IMINUS(...,...) |
|
|
CONTAINSPOINT(...,...) |
|
|
STARTPOINTOF(...) |
|
|
ENDPOINTOF(...) |
|
|
SPLITS(...) |
|
|
SPLITBY(...) |
Whenever the system must load any type, it also tries to find an implementation for the ordering operator for that type (i.e. GT() ). If it does find such an implementation, then SIRA_PRISE automatically also defines an intervaltype, based on the ordered base type concerned and the ordering operator found for it. The name of this intervaltype will be the name of the ordered base type, suffixed with the word "Interval". E.g. the interval type corresponding to ordered base type INT will be named INTINTERVAL. The intervaltype will itself be a scalar, unordered type.
For all such interval types, all the relevant operators (i.e. all the ones starting from AFTER() in the operators table) are also automatically provided. These operators are discussed below.
The interval types themselves (or rather the interval values) will be open-ended intervals specifically. Values of the interval type are represented using a concept that is highly similar, but not exactly the same, as "possrep components". There is always a BEGIN(...) part to (the representation of) an interval value, and optionally also an END(...) part. (It is precisely the fact that this end "component" is optional which makes it something different than a "true" possrep component) Interval values without an END(...) part "stretch to infinity", so to speak, whereas interval values that include and END(...) part do not.
One more or less special interval value is the empty interval (which, in the approach adopted by SIRA_PRISE, is indeed a valid interval value). It can be recognised (if and when it appears anywhere - which it really shouldn't) by the fact that the END(...) part is equal to the BEGIN(...) part. This is one other peculiarity that distinguishes these begin- and end-parts from "true" possrep components : BEGIN(1)END(1) and BEGIN(2)END(2) are all representations of the same -empty- interval value. The empty interval will never appear anywhere as a value of an attribute inside a relation.
More or less aside : intervals without a BEGIN(...) part are not supported. So intervals cannot be made to "start at negative infinity", so to speak.
This section will mention, for each provided interval operator, its return type, its definition, its treatment of the empty interval, and remarks with respect to argument limitations, if any. The following abbreviated notation will be used to denote the arguments and their "components" :
i1 Denotes the first (or only) interval input argument, i2 denotes the second. b1 Denotes the begin() part of the first (or only) input argument, b2 the begin part of the second. e1 Denotes the end() part of the first (or only) input argument, e2 the end part of the second. If there actually is no end() part, then e1/e2 are assumed to represent some conceptual "value" of "infinity", which compares to any real value of the base type as being greater (and to itself as equal). The empty interval is denoted by o.
AFTER(i1,i2) returns a boolean (this applies to all operators up to and including MERGES(), and will not be repeated everywhere). The return value is true if b1>=e2, false otherwise. If either argument is o, false is returned.
BEFORE(i1,i2) returns true if b2>=e1, false otherwise. If either argument is o, false is returned.
BEGINS(i1,i2) returns true if b1=b2 and e1<e2. False if either argument is o.
ENDS(i1,i2) returns true if e1=e2 and b1>b2. False if either argument is o.
CONTAINS(i1,i2) returns true if b1<=b2 and e1>=e2. True if i2 = o. False if i1=o and i2<>o.
WITHIN(i1,i2) returns true if b1>=b2 and e1<=e2. True if i1 = o. False if i2=o and i1<>o.
MEETS(i1,i2) returns true if b1=e2 or e1=b2. True if either argument is o.
OVERLAPS(i1,i2) returns true if b1<e2 and b2<e1. False if either argument is o.
MERGES(i1,i2) = MEETS(i1,i2) or OVERLAPS(i1,i2).
ISEMPTYINTERVAL(i1) returns true if i1 is o, false otherwise.
ISINFINITEINTERVAL(i1) returns true if i1's representation has no end() part, false otherwise.
CONTAINSPOINT(i1,p) takes one interval value and a value p of the type on which the interval type is based, and returns the boolean value true if i1 <> o, b1<=p and e1>p, false otherwise.
STARTPOINTOF(i1) returns the begin() part of i1. An exception is raised if i1 is o. This operator can also be invoked as THE_BEGIN(i1).
ENDPOINTOF(i1) returns the end() part of i1. If ISINFINITEINTERVAL(i1) returns true, or if i1 is o, an exception is raised . Synonymous to THE_END(i1).
IUNION(i1,i2), IINTERSECT(i1,i2) and IMINUS(i1,i2) are the (hopefully) obvious interval computation operators. IUNION() and IMINUS() raise exceptions if the "result" is not one single interval, IINTERSECT() possibly returns o.
SPLITBY(i1,i2) returns true if b1<b2 and e1>e2 and i2<>o.
SPLITS(i1,i2) returns true if b2<b1 and e2>e1 and i1<>o.
SIRA_PRISE supports the inclusion in a type definition of 'possible representations' (possreps for short). Each type obviously must have at least one such possrep, for otherwise the values of the type simply would not be externally representible. That possrep is the type's "default" possrep.
All possreps can have 'possrep components'. For each component of a possrep, SIRA_PRISE automatically defines a THE_ operator whose argument is a value of the possrep's type, and whose return value is the value for that particular component in that particular possrep's external representation of the argument.
E.g. a date value can be represented as a single 10-character string, or it can be represented using three integer values denoting the day, month and year respectively. The latter gives rise to a possrep with three components named DAY, MONTH and YEAR respectively, and to the automatic availability of three operators THE_DAY, THE_MONTH and THE_YEAR respectively (the argument to each of these operators is a value of type DATE, of course). Values of type date can then be selected using either one of DATE(yyyy-mm-dd), DATE(ISO(yyyy-mm-dd)), or DATE(DAY(dd)MONTH(mm)YEAR(yyyy)).
Below is a table of the system-provided types that include a possrep in their definition, and the names and types of each component (where that component type is, obviously, also the return type of the corresponding THE_ operator).
|
Type |
Possrep |
Component name |
Component Type |
|---|---|---|---|
|
BOOLEAN |
YN |
YN |
STRING |
|
BOOLEAN |
STRING |
STRING |
|
|
INT |
HEX |
HEX |
STRING |
|
INT |
STRING |
STRING |
|
|
LONG |
HEX |
HEX |
STRING |
|
LONG |
STRING |
STRING |
|
|
CHAR |
CHAR |
STRING |
STRING |
|
UNICODE |
UNICODE |
INT |
|
|
FLOAT |
FLOAT |
STRING |
STRING |
|
DECIMAL |
DECIMAL |
STRING |
STRING |
|
NAME |
NAME |
STRING |
STRING |
|
FILENAME |
FILENAME |
STRING |
STRING |
|
BITS |
BITS |
STRING |
STRING |
|
DATE |
DMY |
DAY |
INT |
|
MONTH |
INT |
||
|
YEAR |
INT |
||
|
ISO |
ISO |
STRING |
|
|
TIMEOFDAY |
HMS |
HOURS |
INT |
|
MINUTES |
INT |
||
|
SECONDS |
INT |
||
|
NANOSECONDS |
INT |
||
|
ISOWITHNANOSECONDS |
ISOWITHNANOSECONDS |
STRING |
|
|
ANGLE |
DMS |
DEGREES |
INT |
|
MINUTES |
INT |
||
|
SECONDS |
FLOAT |
||
|
RADIANS |
RADIANS |
FLOAT |
Please observe that the component names of a possrep must be unique across all the possreps of the same type (otherwise there would have to be multiple distinct THE_ operators that share the same name and have the same argument type, making their signature completely equal, and thus rendering the operators indistinguishible from the engine's perspective). So it is therefore not possible to define, say, a possrep MDY for type DATE that also has a component named DAY, MONTH or YEAR.
Also observe that type STRING does not seem to have a possrep. If it had one, it should also have a component, and that component would (in practice) have to be of type STRING as well, breaking the rule that no type can have a component that is of the very same type. The absence of a possrep for type STRING obviously does not prohibit writing value selectors for the type. The absence of a possrep for type STRING only reflects the fact that STRING is the actual root type of the system, as far as "representation of values" is concerned.
The content of this paragraph has been moved to the javadoc.
The catalog -currently- has no record of which operators are available. Nevertheless, it is possible for a user to create an implementation of an operator, and let SIRA_PRISE make use of that implementation. All that is needed, is that the implementation class of the operator is added in the classpath of the Java virtual machine that runs SIRA_PRISE. For creating such an operator implementation, we refer to the javadoc.