Updates
Information stored in the database is represented as mappings between
function arguments and results. These mappings are either defined at
object creation time or altered by one of the function update
statements: set
, add
, or remove
. The extent of a function is
the bag of tuples mapping its arguments to corresponding
results. Updating a stored function means updating its extent.
The set
statement sets the value of an updatable function given the
arguments.
For example, assume the following schema defined by types and property functions:
create type Machine;
create function name(Machine) -> Charstring as stored;
create function manufacturer(Machine) -> Charstring as stored;
create type Installation;
create function name(Installation) -> Charstring as stored;
create function machine(Installation) -> Machine as stored;
create function location(Installation) -> Charstring as stored;
Furthermore, assume we have
created one object of
type Machine
and two objects of type Installation
bound to the
session variables :m1
, :i1
, respectively, without
setting any of their properties:
create Machine instances :m1;
create Installation instances :i1
To populate the database by setting the names of machine and the installations one-by-one, do:
set name(:m1) = "L90F";
set manufacturer(:m1) = "Volvo";
set name(:i1) = "MiA";
set machine(:i1) = :m1;
set location(:i1) = "Eskilstuna";
We can also add data with a create object statement:
create Machine (name, manufacturer) instances
:m2 ("CBM 7000", "Hagglunds");
create Installation (name, machine, location) instances
:i3 ("Cooler", :m2, "Bredbyn")
Not every function is updatable. A function is updatable if it is a stored function, or if it is derived from a single updatable function with a single call that includes all arguments. In particular inverses of stored functions are updatable.
For example, the following function is updatable:
create function installations(Machine m) -> Bag of Installation
as select i
from Installation i
where machine(i) = m;
name(installations(:m1));
To populate an updatable bag valued function such as
installations()
you can use bag
construction:
create Installation(name, location) instances
:i2("MiB", "Boliden");
set installations(:m1) = bag(:i1, :i2);
name(installations(:m1));
The add
statement adds a result object to a bag valued function.
For example:
create Machine(name, manufacturer) instances
:m3 ("MoriSeiki 5000", "Mori Seiki");
create Installation instances :i4;
set name(:i4) = "Mymill";
set location(:i4) = "Uppsala";
add installations(:m3) = :i4;
name(installations(:m3))
The remove
statement removes the specified tuple(s) from the result
of an updatable bag valued function.
Example:
name(installations(:m1));
remove installations(:m1) = :i2;
name(installations(:m1));
A Boolean function can be set to either true
or false
.
Example:
create function needs_service(Installation) -> Boolean as stored;
set needs_service(:i1) = true;
select name(i)
from Installation i
where needs_service(i)
Setting the value of a boolean function to false
means that the
truth value is removed from the extent of the function.
For example:
set needs_service(:i1) = false;
select name(i)
from Installation i
where needs_service(i)
alternatively
remove needs_service(:i1) = true
Object properties can be assigned by queries in update statements.
For example,
select name(:i1), location(:i1);
set location(:i1) = location(i)
from Installation i
where name(i) = "MiB";
select name(:i1), location(:i1)
Here the update first retrieves the location of installation i
named
"MiB"
and then sets the location of:i1
to the location of i
.
You can store records in stored functions.
Example:
create function props(Installation) -> Record
as stored;
set props(:i1) = {'Status': 'OK', 'Operator': 'Carl'};
props(:i1)["Status"];
Cardinality constraints​
A cardinality constraint is a system maintained restriction on the number of allowed occurrences in the database of an argument or result of a stored function. For example, a cardinality constraint can be that there is at most one machine per installation, while a machine may have any number of installation. The cardinality constraints are normally specified by function signatures.
In our example above, the function machine(Installation)->Machine
restricts an installation to be on only one machine, while there may
be many installations on every machine. Therefore the following update
fails:
add machine(:i1) = :m2;
This one fails for the same reason:
set installations(:m2) = installations(:m1);
The system prohibits database updates that violate the cardinality
constraints. For the function machine()
an error is raised if one
tries to make an update making the same installation on two machines.
In general one can maintain four kinds of cardinality constraints for a function modeling a relationship between types, many-one, many-many,one-one, and one-many:
one-many is the default when defining a stored function returning a
single value as in machine(Installation)->Machine
. There can be one
machine for every installation and many installations for each
machine.
many-many is specified by prefixing the result type specification of
a stored function with Bag of
. In this case there is no cardinality
constraint enforced.
one-one is specified by suffixing a result variable with key
.
Example:
create function name(Machine m) -> Charstring nm key
as stored
will guarantee that a machine's name is unique.
one-many is normally represented by an inverse function with
cardinality constraint many-one as for the function
installations(Machine)->Bag of Installation
above being the inverse of
machine(Installation)->Machine
.
Since inverse functions are updatable the function installations()
is
also updatable and can be used when populating the database.
Any variable in a stored function can be specified as key, which will restrict the updates to maintain key uniqueness.
Cardinality constraints can also be specified for foreign functions, which is important for optimizing queries using the functions. It is then up to the foreign function implementer to guarantee that specified cardinality constraints hold.
Dynamic updates​
Sometimes it is necessary to create objects whose types are not known until runtime. Similarly one may wish to update functions without knowing the name of the function until runtime. This is achieved by the following procedural system functions:
createobject(Type t) -> Object
createobject(Charstring tpe) -> Object
deleteobject(Object o) -> Boolean
addfunction(Function f, Vector argl, Vector resl) -> Boolean
remfunction(Function f, Vector argl, Vector resl) -> Boolean
setfunction(Function f, Vector argl, Vector resl) -> Boolean
The function createobject()
creates an object of the type specified
by its argument.
The function deleteobject()
deletes an object.
The functions setfunction()
, addfunction()
, and remfunction()
update a function given an argument list and a result tuple as vectors. They return true if the update succeeded.
To delete all rows in a stored function fn
, use:
dropfunction(Function fn, Integer permanent)->Function
If the parameter permanent
is the number one the deletion cannot be
rolled back, which saves space if the extent of the function is large.