API to the Sedna XML database management system

The API to Sedna allows querying and managing XML data from an application written in Scheme. The API follows the spirit of SchemeQL [10], an implementation of SQL 1992 for Scheme. The results of XQuery queries to the Sedna database management system (DBMS) via the API can be represented either in XML or SXML [11].

  1 Sessions
  2 Transactions
  3 Executing Queries
  4 Bulk load from stream
  5 Higher-level function for a transaction
  6 Error handling
  7 Code example
1 Sessions

For working with the Sedna DBMS from Scheme, you should first establish a connection between the Scheme API driver and the Sedna DBMS. Here are two functions to manage connections:

> (sedna:connect-to-database host db-name user password)  
  : String String String String -> connection-object

Establishes a connection between the client application and the Sedna DBMS. Returns a ’connection’ object which incapsulate information about the connection. The arguments are strings that denote connection parameters:

To disconnect from the database, you can use the following function:

> (sedna:disconnect-from-database connection)  
  : connection-object -> void

Closes the connection represented by the connection object. If server fails to close the connection, the function closes the connection forcibly from the client side and raises the exception, as discussed in subsubsection 6.

2 Transactions

After the connection with a database is established and the session is begun, you can run zero or more transactions in this session. Transactions are to be run sequentially, with no more than a single transaction at a time, so you should commit your running transaction before starting a new one.

To begin a new transaction, the following function is provided:

> (sedna:begin-transaction connection)  
  : connection-object -> void

It accepts the connection object (earlier created by sedna:connect-to-database function) and starts a new transaction. If the transaction could not be created, the exception is raised, as discussed in subsubsection 6.

To end your running transaction, you are provided with the following function:

> (sedna:end-transaction connection action)  
  : connection-object, symbol -> void

If action is ’COMMIT the transaction in the given connection will be commited, if ’ROLLBACK is given, the transaction will be rolled back.

3 Executing Queries

Within a transaction, you can execute zero or more queries to the database.

> (sedna:execute-query connection query)  
  : connection-object, string -> result

The first argument is the connection object, earlier created by sedna:connect-to-database function. The query is represented as a string and can express one of the 3 kinds of queries:

If an error occurs at the server side during query execution (e.g. the requested document not found), the function raises an exception that contains the message about the error occurred.

In the successful case of query execution, sedna:execute-query returns #t for the last 2 kinds of queries, to denote a successful update made to the database. The XQuery query results to a sequence of items, which are evaluated lazily and are represented as a pair:

xquery-result ::= (cons current-item promise)

This way of result representation is very close to the notion of SchemeQL cursor [10] (with the only difference in that the Scheme API driver returns XQuery items instead of table rows returned by SchemeQL). The first member of the pair is the current-item represented in SXML, and the second member of the pair holds a promise (which can be forced) to evaluate and return the next item in the result sequence.

To iterate over the result sequence, you can use the function:

> (sedna:next xquery-result)  
  : xquery-result -> xquery-result or ’()

which forces the evaluation of the following items in the result sequence, until the end of the sequence is reached.

Such a design allows you to process a query result in a lazy stream-wise fashion and provides you with an ability to process large query results, which would not otherwise fit in the main memory.

However, for query results that are not very large, you may find it convenient to evaluate them all at once and represent the result sequence as a Scheme list. Scheme API provides a function that converts the xquery-result into the list that contains all items of the result sequence:

> (sedna:result->list xquery-result)  
  : xquery-result -> (listof item)

To obtain the result sequence in the form of the list, you can execute your queries as a superposition of the above considered functions:

(sedna:result->list  
  (sedna:execute-query connection query))

It should be noted that the XQuery query result in that case exactly corresponds to the term of a node-set in the XPath implementation in Scheme SXPath [14].

If you want to obtain your query results in XML instead of SXML, you can use the function

> (sedna:execute-query-xml connection query)  
  : connection-object, string -> result

It is the counterpart of earlier discussed sedna:execute-query and has the same signature, but represents query results in XML. The function returns a sequence of items, in the form of xquery-result discussed above, but the current-item is now a string containing the representation for the current item in the form of XML.

4 Bulk load from stream

The following wrapper function provides a natural way to load an input stream containing an XML document into your database:

> (sedna:bulk-load-from-xml-stream  
                          connection port document-name . collection-name)  
  : connection-object, input-port, string [, collection-name] -> boolean

As for sedna:execute-query, the first argument here is the connection object, earlier created by sedna:connect-to-database function. Argument port is a Scheme input port and is supposed to contain a well-formed XML document. Argument document-name specifies the name that will be given to the XML document within a database. If the 4-th argument collection-name is supplied, the XML document is loaded into the collection which name is specified by the collection-name argument. If the 4-th argument of the function call is not supplied, the XML document is loaded into the database as a standalone document.

By allowing you to specify the input port you would like to use, this function provides a higher-level shortcut for sedna:execute-query when bulk load from stream is concerned. For more details on bulk load, see Sect. 2.4.

5 Higher-level function for a transaction

This higher-level function provides a convenient way for executing a transaction consisting of several queries, within a single function call:

> (sedna:transaction connection . queries)  
  : connection-object, string* -> result

This function starts a new transaction on the connection objects and executes all the queries given in order. If no exception occurs, the function commits the transaction and returns the result of the last query. If any exception occurred during query execution, the function sends rollback to the Sedna DBMS and passes along the exception to the application.

6 Error handling

Error handling in the Scheme API driver is based on the exception mechanism suggested in the (currently withdrawn) SRFI-12 [12]. The SRFI-12 exception mechanism is natively supported in the Chicken Scheme compiler. In the Scheme API driver, we also provide the SRFI-12 implementation for PLT and Gambit.

7 Code example

This section presents an example that illustrates the application of the Scheme API driver.

 
(require-extension sedna)  
 
; Create a connection  
(define conn  
  (sedna:connect-to-database "localhost" "sample-db" "SYSTEM" "MANAGER"))  
 
; Begin a transaction  
(sedna:begin-transaction conn)  
 
; Bulk load  
(call/cc  ; Thanks to Felix Winkelmann for recommending us to use call/cc here  
 (lambda (k)  
   (with-exception-handler  ; Exception handler  
    (lambda (x)  
      (display "File already loaded to the database")  
      (newline)  
      (k (sedna:begin-transaction conn)))  
    (lambda ()  
      (sedna:execute-query conn "LOAD ’region.xml’ ’regions’")))))  
 
; Execute a statement and represent it as an SXML nodeset  
(pp  
 (sedna:result->list  
  (sedna:execute-query conn "document(’region’)/*/*")))  
 
; Update statement  
(pp  
 (sedna:execute-query conn "UPDATE delete document(’region’)//africa"))  
 
; Querying all regions once again  
(pp  
 (sedna:result->list  
  (sedna:execute-query conn "document(’region’)/*/*")))  
 
; Commit transaction  
(sedna:end-transaction conn ’COMMIT)  
 
; Close the connection  
(sedna:disconnect-from-database conn)

You can find the example and the Scheme API driver code in:

[win:] INSTALL_DIR\examples\applications\scheme  
[linux:] INSTALL_DIR/examples/applications/scheme

where INSTALL_DIR refers to the directory where Sedna is installed.

Before running the example make sure that the Sedna DBMS is installed and do the following steps:

  1. Start Sedna by runing the following command in a command line:
    se_gov

    If Sedna is started successfully it prints ”GOVERNOR has been started in the background mode”.

  2. Create a new database sample-db by running the following command:
    se_cdb sample-db

    If the database is created successfully it prints ”The database ’sample-db’ has been created successfully”.

  3. Start the sample-db database by running the following command:
    se_sm sample-db

    If the database is started successfully it prints ”SM has been started in the background mode”.

If the Sedna DBMS is running on the same computer as your Scheme application, you don’t need to change anything in the example code. If the Sedna DBMS is running on a remote machine, you should use the name of this machine when connecting to the database with sedna:connect-to-database function.