Developed at the

Polish-Japanese Institute of Information Technology

Chair of Software Engineering

SBA and SBQL home pages

© Copyright by ODRA team, © Copyright by PJIIT

 

 

 

 

ODRA – Object Database for Rapid Application development

Description and Programmer Manual

 

 

 

by Radosław Adamus and the ODRA team

 

8. SBQL Procedures

ODRA procedures are determined by a source code (as usual) and by the runtime environment. During runtime, the procedures are special complex objects stored inside modules. Such procedures are globally available. Procedures stored inside classes are named “methods” and called in a class instance context. Procedures can also be components of updatable views, where they play a special role. Procedures encapsulate arbitrary complex computation and can be called many times in many places of the code. Their behaviour can be parameterized by the use of parameters and/or by the state of the execution environment. For all kinds of procedures we assume that local procedure objects and their actual parameters are invisible from outside and are invisible for called procedures; thus each procedure creates its own local name space. This is a typical assumption for practically all programming languages, greatly supporting reuse and independent work of programmers.

 

 

8.1 Procedures and Functional Procedures

In ODRA there is little distinction between procedures and functional procedures. Semantically, the only difference is that a functional procedure returns an output on its invocation; thus, an invocation can be treated as a query and can be nested as a part of other queries. A non-functional procedure cannot be a part of a query because it returns no value. Typing of procedures and functional procedures is a bit different: the type of a proper (non-functional) procedure does not contain a return type.

Each procedure is uniquely identified by its name. Overloading of the names is not supported. The result of a functional procedure is typed, similarly to other programming languages. Each functional procedure can also be used as a proper procedure; in this case its return value is neglected.

Procedure declaration syntax

A procedure declaration consists of a procedure name, a typed parameter list, a type of its output and a list of imperative SBQL statements.

    name([parameter_list])[:returntype] {statement_list}

    parameter_list ::= par_declaration [;parameter_list]

Procedure parameter declaration syntax determines its name, type and cardinality:

par_declaration ::= name:type [ cardinality ]

If the cardinality is not specified, the default cardinality [1..1] is assumed. In the procedure signature parameter declarations are separated by semicolons.

 

Procedure invocation syntax

    name([actual_par_list])

    actual_par_list ::= parameter[;actual_par_list]

    parameter ::= query

 

8.2 Parameters of Procedures

Procedures can possess parameters. A parameter specification determines its formal name, a parameter type and a cardinality. The parameter passing technique implemented in the ODRA system is known (e.g. from C) as strict-call-by-value. It means that each actual parameter determined by a query is evaluated before the procedure execution and the result is stored at the procedure activation record as a binder which name is the name of the corresponding formal parameter. Thus, (like Pascal and unlike Java or C/C++) procedure parameters do not have the “local variable” semantics. If the result of an argument query is a value, it is passed directly as a (named) value (passing by value). If the result of an argument query is an object reference it will be passed directly as a (named) reference (passing by reference). This parameter transmission method combines call-by-value with call-by-reference in a very general fashion, which allows the programmer to pass as a parameter the result of any complex query that combines atomic values, references, auxiliary names, structures, bags, sequences, etc.

 

8.3 Local Variables

Local variables (objects) can be declared within procedures and within statement blocks. According to the scope rules the visibility of each declaration is constrained to its declaration block and sub-blocks (unless it is overwritten by a new declaration). See the variable declaration section for the syntax of a local variable declaration.

Inside a procedure body a local variable can be created by a create statement (see the object creation section). A locally created object can be declared inside the current code block or in any visible environment (e.g. an outer block and a module).

 

8.4 Return from a Functional Procedure

To return a value from a functional procedure the return statement must be used. The return value is determined by a query. The syntax is as follows:

return query;

The statement causes immediate terminating the procedure. The query can be any SBQL query returning a result that conforms to the return type in the procedure specification signature. Returning references to local variables is forbidden (rejected by the dynamic type checking).

The return value can be also built up from a query that returns a named result (see: auxiliary names). For example, the statement:

return (Emp where worksIn.Dept.name = “PR”) as PR_Staff;

returns references to employees working in the PR department. The references are named PR_Saff. The name is available outside the procedure body.

 

8.5 Examples of Procedures

A functional procedure add returns the sum of two integer values passed as parameters.

add(a:integer;b:integer):integer {

 return a + b;

}

Usage:

add(12; 5);

add(5 + count(Emp); 64) – 10;

A proper procedure giveRise rises salaries of employees (passed as parameter emp, which is a bag of references) with a value (passed as parameter value). The procedure returns no value.

giveRise(emp:ref Emp[0..*]; value:integer) {

     foreach emp as e do {

         e.sal := e.sal + value;

     }

}

Usage:

giveRise( Emp where worksIn.Dept.dName = “PR”; 200);

 

8.6 Functional Procedures as Database Views

A procedure lowEarning for each employee earning less that the average and having the position within the pos parameter (bag of strings) returns a structure containing three named fields: employee name N, salary  S and department name D).

lowEarning(pos:string[1..*]): record{N:string; S:integer; D:string;}{

   a : real;

   a := avg(Emp.sal);

   return (Emp where sal < a and pos contains position).

       (lName as N, sal as S, worksIn.Dept.dName as D);

}

Usage:

lowEarning(“bricklayer”).(N, S, D);

lowEarning(bag(“programmer”, “designer”)).(N, S);

If a procedure returns a collection of records with named fields, it is similar to the concept that in databases is known as view. For instance, an invocation of the procedure lowEarning can be as follows:

(lowEarning(“programmer”)

         where N = “Brown” and D = “Toys”).S

The query returns the salary of a low earning programmer named Brown from the Toys department. Because such a procedure can return references, it can also be used for updating, for instance:

foreach(lowEarning(“programmer”)

         where N = “Brown” and D = “Toys”) do S := S + 200;

Although ODRA does not forbid such updates, they should be used with care due to the view updating anomalies, which may warp the programmer intention. In case of doubts the programmer is recommended to use SBQL updatable views rather than functional procedures.

8.7 Recursive Procedures and Methods

In ODRA recursive procedures do not imply any special syntax. All procedures can be recursive. Such a feature is common in popular programming languages implementations. ODRA SBQL extends the property to query languages. This allows for smart specification of some inherently recursive tasks. The use of recursive procedures needs some attention from the programmer concerning the following aspects:

·         Depth of the recursion: SBQL does not limit it physically, but too large depth may compromise performance.

·         The depth cannot be infinite, hence the programmer is responsible for ensuring that the recursion eventually will be terminated.

·         Performance: in many cases iterations or transitive closures are faster than recursive procedures.    

Example

factorial(n:integer):integer {

   if n = 0 or n = 1 then return 1;

   return n * factorial(n - 1);

}

 

 

Last modified: June 28, 2008