SWI-Prolog Jasmine (ODB) Interface


Jan Wielemaker
SWI,
University of Amsterdam
The Netherlands
E-mail: jan@swi.psy.uva.nl

Abstract

The Jasmine interface provides a client interface to the Object Oriented Jasmine database. The interface consists of two layers. The first is a simple wrapper around the Jasmine C-interface. The second provides utility predicates providing more high-level access to Jasmine.

Table of Contents

1 Introduction

Jasmine is an object-oriented database (ODB), implementing ODQL (Object Database Query Language). It provides a C-interface based on the following components:

2 Basic ODB predicates

Below is the definition of the basic ODB access predicates defined in jasmine.c.

2.1 Session management

Sessions are accessed using a session-handle. This opaque handle is normally preserved in the Prolog database.

odb_ses_start(-SH, +Database, +User, +Passwd, +EnvFile)
Connect to the indicated database and return a handle for the created session in SH. SH is an opaque Prolog term providing context for subsequent ODB calls. Database specifies the database to connect to. It is an atom formatted as below, where nvode is the name of the machine to connect to. User and Passwd and EnvFile are either atoms or unbound variables. The latter makes the interface choose default values. EnvFile is the name of a file providing parameters for the interface. See the C-API documentation for details.
[vnode::]/jasmine/jasmine
odb_ses_end(+SH)
Terminate the session. Note that at_halt/1 can be used to ensure termination of the session when Prolog halts.

2.2 ODQL Statements

ODQL statement are passed in textual form and specified either as atoms or SWI-Prolog strings. The latter makes it possible to construct statements using sformat/3. See also odb_exec_odql/3.

odb_exec_odql(+SH, +Statement)
Execute the given ODQL Statement on the session SH. This predicate either succeeds or raises an exception. See section ?? for details.

2.3 Variables

Variables play a vital role in the interface. Interface variables are defined using ODQL statements. They are scoped to the session, but otherwise global. There are two approaches to deal with this. One is to define a suitable set of variables for the application at startup and the other is to create them as they are needed. In the latter case one should be sure the variable name is currently not in use. In some of the examples we therefore see:


undefVar pcount;
Integer pcount;

From this example we learn that variables are typed. The type is accessible through the C-interface and used by the access predicate to perform suitable conversion to Prolog.

odb_get_var(+SH, +Name, -Value)
Fetches the value of the named interface variable. Succeeds if the value can be unified successfully, fails if the value is retrieved correctly but unification fails and raises an exception otherwise.

The representation of Value depends on the type of Name in the database interface.

odb_set_var(+SH, +Name, +Value)
Set a variable. In accordance with the guidelines in the interface this first fetches the value to examine the type of the variable. The latter is problematic, as not-yet-filled variables yield the Nil type. In this case the type is determined from Value.

This translation currently does not deal with the type-ambiguities. It is currently not possible to set nil-variables to a boolean, byte-sequence or date. This problem can be fixed by using an ODQL query to fill the empty variable with an object of the requested type.

2.4 Collections

Database queries normally yield collections as results. The interface simply converts collections into Prolog lists. The current interface does not yet provide mechanisms for fetching part of a collection. Note that, using ODQL statements it is possible to get the length of a collection before conversion:


collection_length(SH, Collection, Length) :-
        odb_exec_odql(SH, 'Integer len;'),
        odb_exec_odql(SH, 'len = ~w.count();', [Collection]),
        odb_get_var(SH, len, Length).

odb_collection_to_list(+SH, +Collection, -List)
Where Collection is the name of a variable containing a collection or the object-identifier of a collection. The elements of the collection are converted using the same rules as odb_get_var/3.

3 Utility Predicates

The predicates of the previous section provide all important aspects of the C-API to the Prolog user. The provided access however is very low-level. A very first start has been made to provide a number of utility predicates.

odb_exec_odql(+SH, +Format, +Args)
First constructs a command using sformat/3 from Format and Args and then execute it.
odql(:SH, +Declarations, +Statements)
Utility to deal with a sequence of ODQL statements, requiring some variables to execute them. Declarations is a list of VarName:Type. These variables are first unset and then declared using the given type. Please note that this principle is not re-entrant. Statements is a list containing a mix of ODQL statements, set/get variables, access collections and ordinary Prolog code:
get(VarName, Value)
Fetch the interface variable VarName using odb_get_var/3.
set(VarName, Value)
Store the interface variable VarName using odb_set_var/3.
get_list(Collection, List)
Get a variable or object-id into a list of values using odb_collection_to_list/2.
{}(Goal)
Call normal Prolog goal in the module from which odql/3 was called. Note that {Goal} is the same as {}(Goal).
-(Format, Args)
Execute an ODQL query using odb_exec_odql/3.
Command
Execute ODQL command.

Here is an example, extracting the available class-families from the Jasmine database:


families(SH, List) :-
        odql(SH,
             [ ss:'Bag<String>'
             ],
             [ 'ss = FamilyManager.getAllFamilies();',
               get_list(ss, List)
             ]).

4 Error handling

All errors are reported using Prolog exceptions. This package raises two types of exceptions. If Prolog arguments cannot be converted into the desired data, normal Prolog type_error and instantiation_error exceptions are raised. Jasmine calls returning an error are translated into an error term of the format

error(package(jasmine, ErrorId), Context)

Where Context is

context(Message, _)

In this term, ErrorId is the (numerical) error identifier raised by Jasmine and Message is Jasmine's textual representation of the error.

5 Issues

The interface defined here provides the foreign-language basis for a more advanced Prolog ODQL interface. Specifying all ODQL as strings and dealing with the interface variables is not a desirable way to deal with ODQL. A more fundamental approach is to define a Prolog API for ODQL and an interface for translating these Prolog queries into textual ODQL calls. For example, the families/2 example above could be written as:


families(SH, Families) :-
        odql(Families:bag(string) = 'FamilyManager'.getAllFamilies).

6 Installation

The jasmine package has currently been build only on Windows. As Jasmine is also available on Unix, the standard SWI-Prolog package infra-structure for Unix foreign packages is provided.

6.1 Unix systems

Installation on Unix system uses the commonly found configure, make and make install sequence. SWI-Prolog should be installed before building this package. If SWI-Prolog is not installed as pl, the environment variable PL must be set to the name of the SWI-Prolog executable. Installation is now accomplished using:


% ./configure
% make
% make install

This installs the Prolog library files in $PLBASE/library, where $PLBASE refers to the SWI-Prolog `home-directory'.

6.2 Windows

Run the file setup.pl by double clicking it. This will install the required files into the SWI-Prolog directory and update the library directory.

Footnotes

note-1
This could be considered a bug. What would be a better choice?