Tamino: Getting there

Abstract: A database has little value unless you can store information into it and extract information from it. This paper will give an overview of different methods for interacting with Tamino. It includes a brief introduction to XQuery, the W3C standard XML query language used by Tamino, a description of the X-Machine commands used to communicate with Tamino, and a survey of the APIs available in various programming languages.

Preliminaries

The X-Machine commands and Tamino APIs will be easier to understand if we have some basic knowledge about how Tamino works, so first let’s review a few things about Tamino, HTTP and XQuery.

HTTP

The HyperText Transport Protocol (HTTP) is the core protocol for communication on the web. It defines a simple exchange of messages between a client and a server. The client begins the exchange by sending a request message consisting of a request line, a set of header lines, and a message body (which may be empty.) The server responds with a message consisting of a status line, header lines, and a message body (which again may be empty.) The request, status, and header lines all consist of ASCII text terminated by a carriage return and line feed; an empty line consisting of just a carriage return and line feed marks the end of the headers.

The header lines for both the request and the response messages contain metadata used to describe the client and server, the communication itself, and the body of the message. Each line begins with a keyword followed by a colon character (':') and a value. As an example of a common header, the Content-type header specifies the MIME type of the message body and should always be present if the message has a body.

The request line from the client contains a verb describing the method, the path part of the URL for the request, and the HTTP version. The important methods for our discussion are GET, HEAD, POST, PUT, and DELETE. The GET method indicates a request that the server return a representation of the resource identified by the URL in the body of the response. (The GET request itself normally does not include a body.) GET requests should never cause a significant change to the state of the resource or the server; it should be possible to issue a GET request as many times as desired without regard to the consequences. In a browser, clicking a link on a web page or entering a URL in the location field normally causes the browser to issue a GET request. The HEAD request is the same as a GET except that the server will only return the headers without any body in the response. The PUT method requests that the server create a new resource or replace an existing resource using the body of the message, while the DELETE message asks the server to destroy the resource. Finally, the POST method requests that the server perform some sort of processing on the resource using the message body. POST processing may include significant state changes, so clients cannot assume that POST requests will not have important consequences. In a browser, submitting an HTML form usually means the browser will issue a POST request.

The status line in the server’s response consists of the HTTP version, a three digit code describing the status of the request, and a brief textual description of the meaning of the status code. A code in the 100 range indicates that the server is processing the request and will send another response message later, a code in the 200 range indicates successful completion of the request, a code in the 300 range indicates that the client must take further action (for example, redirect the request to a different URL), a code in the 400 range indicates a problem with the request, and a code in the 500 range indicates a server error.

Here is an example of a very simple HTTP transaction. First, the request:

GET /hello.html HTTP/1.1
Host: www.example.com
User-Agent: Spiffy Browser/1.0

and here is the response:

HTTP/1.1 200 OK
Date: Wed, 18 Oct 2006 13:10:12 GMT
Server: Spiffy Web Server/1.0
Content-length: 136
Content-type: text/html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" 
     "http://www.w3.org/TR/html4/strict.dtd">
<title>Hello</title><p>Hello, world!

In some of the examples below, I will use the Unix command-line utility curl, which takes a URL as a parameter, issues a GET request (or POST if certain flags have been set) and prints the body of the response to the terminal. (If given the -i flag, it will print the status line and headers as well.)

Tamino organization

The fundamental unit of storage in Tamino is the document. (A document in Tamino plays roughly the same role as a record in Adabas or a row in a relational database.) Tamino can store both XML and non-XML documents, but most if its functionality exists to support XML documents — a non-XML document is little more than a stream of bits for Tamino. Documents are grouped into collections, roughly corresponding to a file in Adabas or a table in a relational database. Typically, a collection will contain documents associated with a particular application.

Tamino assigns each document to a doctype, which for an XML document corresponds to the name of the root element of the document. (Collections and doctypes are usually defined by a schema, although newer versions of Tamino allow administrators to define schema-free or schema-optional collections.) When a document is stored in Tamino, the creating application may assign it a document name, and Tamino will always assign it a numeric identifier known as the ino:id. This ino:id will be unique within the collection and doctype; it plays a role in Tamino analogous to the ISN in Adabas. Document names, if assigned, must also be unique within the collection and doctype.

A brief introduction to XQuery

XQuery plays the same role for Tamino and other XML databases that SQL plays for relational databases like Adabas D, Oracle, or DB2. In fact, XQuery can be considered an extension of XPath 1.0 (XPath 2.0 is officially a subset of XQuery 1.0) with features for joins and other database functionality inspired by SQL. Since many Tamino accesses will require involve constructiong an XQuery expression, let’s take a quick look at XQuery first.

You construct a query in XQuery by composing an expression, a string of characters that the XQuery processor (in our case, Tamino) evaluates to return a result. Expressions consist of keywords, symbols, and operands; operands are usually other expressions. The result of an expression is a sequence of zero or more items. (The sequence may be empty, meaning it contains zero items, or a singleton, containing exactly one item. A singleton sequence is equivalent to the item it contains; for example, the value of the expression 2 + 2 can be considered the number 4 or a sequence containing just the number 4.) Items may be XML documents, nodes from an XML document, or atomic types. The nodes of an XML document are the elements, attributes, comments, and so forth that make up the document. XQuery uses the definition of atomic types from the W3C XML Schema Recommendation; this recommendation defines a set of such types and a mechanism for defining new types using an XML schema.

The most distinctive types of expressions in XQuery are XPath path expressions and FLWOR expressions. XQuery also has expressions that can change the state of the database.

Path expressions

XPath path expressions select nodes from XML documents. They consist of one or more path steps separated by a slash character ('/'). Each step takes a sequence of nodes and generates a new sequence of nodes; if a path expression has more than one step, the output sequence from each step is used as the input sequence for the next step. A path step has three parts: an axis, a node test, and optional predicates.

The axis determines the direction of movement from the input nodes to the output nodes and consists of a keyword followed by two colon characters ('::'). The most common axis is child::, which means that the output nodes will be selected from the children of the input nodes. This axis is also the default, so if the axis is omitted then Tamino uses the child axis. The second most common axis is attribute::, which may be abbreviated by using an “at sign” ('@'). The other axes are rarely used.

The node testselects nodes from the set defined by the axis. The most common node test is the element or attribute name, but it may also be test for a node type like node() (which selects for any node type) or comment(). An asterisk ('*') may be used as a wildcard to match any element or attribute name.

The set of result nodes in a step may be filtered further by any number of predicates, or boolean expressions. Each predicate is enclosed in square brackets ('[' and ']'). A node will remain in the result set only if all the predicate expressions evaluate to true.

A few path steps are used so commonly that special abbreviations have been provided. The step “.” abbreviates “self::node()” and “..” abbreviates “parent::node()”, selecting the current node or its parent. Two slashes together in a path without anything intervening (“//”) represent “/descendent-or-self::node()/”, which allows for selection of any descendent at any level. (In many cases this path step should be avoided for performance reasons.)

Examples

Consider the following XML document:

<prefs>
	<favs name="Samuel">
		<song>Old McDonald</song>
		<quote>To be or not to be.</quote>
	</favs>
	<favs name="Jonathan">
		<song>The Eensy-weensy spider</song>
		<song>Popcorn popping</song>
	</favs>
</prefs>

The result of the expression “prefs/favs/song” would be a sequence containing “<song>Old McDonald</song>”, “<song>The Eensy-weensy spider</song>”, and “<song>Popcorn popping</song>”. The result of the expression “prefs/favs[@name = "Samuel"]/song” would be a sequence containing “<song>Old McDonald</song>”.

FLOWR expressions

The FLWOR expression (pronounced “flower”) takes its name from the five clauses it may contain: for clauses, let clauses, a where clause, an order by clause, and a return clause. This expression type gives XQuery much of its power; among other uses, it provides the equivalent of SQL’s JOIN clause.

A FLWOR expression begins with any number of for and let clauses, which bind values to variables used in the rest of the expression. XQuery variable names begin with a dollar sign ('$'). A simple let clause takes the form:

let $variable-name := expression

and binds $variable-name to the sequence returned by expression. A single let clause can bind more than one variable by separating the assignments with a comma; for example:

let $s-date := xs:date('2006-10-17'), $e-date := xs:date('2006-10-20')

The for clauses create implicit loops. A simple for clause takes the form:

for $variable-name in expression

and successively binds $variable-name to each item in the sequence returned by expression. As with the let clause, multiple bindings can be defined in a single clause:

for $book in input()/book, $author in input()/author

(The input() function returns all the documents in the current collection.)

When a FLWOR expression contains a for clause with more than one binding, or if it contains multiple for clauses, the rest of the expression is evaluated for the Cartesian product of all the for clause bindings. For example, in the above clause if the collection has three book documents, which we’ll label A, B, and C, and two author documents, which we’ll label X and Y, then the expression will be evaluated six times, with ($book=A, $author=X), ($book=A, $author=Y), ($book=B, $author=X), ($book=B, $author=Y), ($book=C, $author=X), and ($book=C, $author=Y). With two bindings we could call each matching of instances a pair, but we will usually use the more general term tuple which works for any number of bindings.

To make the distinction between for and let clauses clearer, consider the following two expressions:

for $b in input()/book return count($b)
let $b := input()/book return count($b)

In this case, the first expression would return a sequence containing the number 1 three times, while the second expression would return the number 3.

The optional where clause contains a boolean expression used to filter the iterations: the rest of the clause will only be evaluated if the expression in the where clause evaluates true. For example, consider:

for $book in input()/book, $author in input()/author
where $book/author = $author/name

Now instead of processing all possible combinations of book and author, the query will only process those where the book’s author matches the author’s name.

The optional order by clause can be used to sort the tuples before processing the return clause. Finally, the required return clause contains an expression which will be evaluated for each tuple (and with all bindings from any let clauses in scope.) The result of the FLWOR expression is the sequence formed by concatenating the results of each evaluation of the return clause.

Here is an example showing all the possible clauses of the FLWOR expression:

for $book in input()/book, $author in input()/author
let $title := $book/title, $name := $author/name
where $book/author = $name
order by $name
return <author-title>
           {$name, $title}
       </author-title>

Updating queries

Tamino’s implementation of XQuery also has expressions that can perform updates on the database. (This functionality has not yet been standardized by the W3C.) An updating expression starts with the keyword update followed by an insert clause, a replace clause, a rename clause, a delete clause, or a FLWU expression.

delete

The delete clause is probably the simplest of these clauses. It consists of the keyword delete followed by an expression that selects a set of nodes, and it deletes the selected nodes. By using the root() function you can use this expression to remove complete documents from the database. For example:

update delete root(input()/fragment[tf:getInoId(.) = 12])

would delete the fragment document in the current collection with ino:id of 12.

rename

The rename clause allows you to change the name of an element or attribute while leaving its attributes, contents, or value the same. It consists of the keyword rename followed by an expression to select the nodes to rename followed by the keyword as and the new name. (Other than some kind of schema migration I can’t imagine what this would be good for.)

replace

The replace clause allows you to remove an existing node and provide one or more new nodes it its place. It has the keyword replace and an expression selecting a node to replace followed by the keyword with and an expression defined the new node(s). For example:

update replace input()/project[@projid = 2358]/@status
	with attribute status {"completed"}
insert

The insert clause allows you to add new nodes to existing documents. (It does not, however, support adding complete new documents.) It begins with the keyword insert followed by an expression defining the new node(s). When adding attributes, next comes the keyword into followed by an expression selecting the element that should have the new attribute(s). When adding elements, you can also use the into keyword and a selecting expression, in which case the new elements are added as children to the selected element after any existing children, or you can use the keywords preceding or following with a selecting expression, in which case the new elements become siblings of the selected element, either before it or after it. Here is an example:

update insert <payment date="2006-10-01">500.00</payment>
	into input()/account[@acct-id = "loan88345"]/history
FLWU

The FLWU expression looks just like a FLWOR expression, except it has no order by clause, and instead of a return clause it has the keyword do followed by a sequence of one or more updating expressions. For example, the previous could be recoded as:

update for $a in input()/account
where $a/@acct-id = "loan88345"
do insert <payment date="2006-10-01">500.00</payment> into $a/history

Since the do clause takes a sequence of updates, we could extend this query as follows:

update
let $p := 500.00
for $a in input()/account
let $b := $a/@balance
where $a/@acct-id = "loan88345"
do (insert  <payment date="2006-10-01">{$p}</payment> into $a/history,
	replace $b with attribute balance {$b - $p})

Example query

Since this paper isn’t really about XQuery, we’ll use the following simple query as an example whenever we need one:

input()/fragment[author = "Lewis Carroll"]

In our sample collection, this query selects two documents:

<fragment>
<index>1</index>
<author>Lewis Carroll</author>
<text>“Just the place for a snark!” the Bellman cried
As he landed his crew with care,
Supporting each man on the top of the tide
By a finger entwined in his hair.</text>
</fragment>

and

<fragment>
<index>9</index>
<author>Lewis Carroll</author>
<text>’Twas brillig, and the slithy toves
Did gyre and gimble in the wabe:
All mimsy were the borogoves,
And the mome raths outgrabe.</text>
</fragment>

Some XQuery resources

A note on X-Query

For backward compatibility, Tamino supports an older query language called X-Query. (Yes, the only difference in the name is the hyphen.) Like XQuery, X-Query is a superset of XPath and includes path expressions, but it does not have FLWOR expressions or updating expressions and has other limitations. I recommend that new Tamino users avoid using X-Query and that existing applications convert to XQuery.

The foundation: X-Machine

The Tamino server has four main components: the Tamino manager, which provides administrative functions, X-Node, which allows Tamino to access external databases (for example, Adabas C or SQL databases), X-Tension, which allows an installation to add functionality to Tamino with code written in C++, Visual Basic, or Java, and the X-Machine, our focus in this paper, which performs storage and retrieval of documents, query processing, document composition, and other services. Communication with the X-Machine takes place via a web server using HTTP. It supports two distinct flavors of access: plain URL addressing and X-Machine commands. Documentation for X-Machine programming is available on ServLine24 and distributed with Tamino.

Plain URL addressing

Every document in a Tamino database has one or two URLs that uniquely refer to it, and the X-Machine allows you to use standard HTTP methods to directly manipulate the document using those URLs: a GET request retrieves the document, a PUT request creates or updates it, and a DELETE request deletes it. The formats for these URLs are:

http://hostname/tamino/dbname/collection/doctype/docname
http://hostname/tamino/dbname/collection/doctype/@ino:id

where hostname is the domain name or the IP address of the web server, dbname is the name of the Tamino database, collection is the name of the collection containing the document, doctype is the document type of the document, docname is the document name, and ino:id is the numeric identifier that Tamino assigned the document. A document already stored in Tamino may always be referenced with the URL that ends with the ino:id; Tamino will accept the GET method to retrieve with this URL to retrieve the document, HEAD to get its related metadata, DELETE to remove it, or PUT to replace it with a new version. If the document was assigned a name when it was stored in Tamino, you can use these same methods with the URL containing the document name. You can also use the PUT method with the document name form of the URL to store a new document.

Here is an example of retrieving a document using the ino:id URL:

$ curl http://flute.its.utexas.edu/tamino/cgp/test2006/fragment/@3
<?xml version="1.0" encoding="ISO-8859-1" ?><fragment><index>3</index>
<author>Publius Vergilius Maro</author>
<text xml:lang="lt">Pone merum et talos; pereat qui crastina curat!
Mors aurem vellit: &#x201C;Vivite,&#x201D; ait, &#x201C;venio.&#x201D;</text>
</fragment>$ 

and here is an example using the document name URL and showing the headers returned by Tamino and the web server:

$ curl -i http://flute.its.utexas.edu/tamino/cgp/test2006/fragment/Hamlet
HTTP/1.1 200 OK
Date: Sat, 23 Sep 2006 20:38:57 GMT
Server: Apache/2.0.54 (Unix) DAV/2
X-INO-returnvalue: 0
X-INO-Docname: Hamlet
Last-Modified: Tue, 13 Dec 2005 20:45:46 GMT
Cache-Control: no-cache
X-INO-Version: 4.4.1.4
Content-Length: 223
Content-Type: text/xml; charset=ISO-8859-1

<?xml version="1.0" encoding="ISO-8859-1" ?><fragment><index>2</index>
<author>William Shakespeare</author>
<text>To be, or not to be,
That is the question.</text>
</fragment>$ 

X-Machine commands

Plain URL addressing is simple and efficient, but it has some serious limitations:

A simple file system provides this level of functionality; we expect more from a database. Tamino provides additional functionality through X-Machine commands. You send X-Machine commands to Tamino using HTTP GET or POST. Although Tamino does not enforce this, you should always use POST for commands that will modify the database. The normal URL for X-Machine commands is:

http://hostname/tamino/dbname

where hostname, etc. have the same meaning as above. For some X-Machine commands you must also add the collection name to this URL, and in some cases you can add the doctype and document name or ino:id as in plain URL addressing.

X-Machine commands are sent in the same manner as a browser sends form data. Each command and parameter has a keyword, and the request consists of keyword/value pairs. The keywords all begin with and underscore and are not case sensitive, and you can send more than one command in a single request. For a GET request, the command with its parameters is formatted as a URL-encoded query string and appended to the URL after a question mark ('?'). For a POST request, this information is passed in the message body as a document of type multipart/form-data.

As an example, let’s send Tamino the example XQuery expression we listed above. The keyword for the X-Machine command to send an XQuery expression to Tamino is _xquery. We’ll add the _encoding parameter to show how multiple commands and parameters are formatted. For the GET method, we must URL-encode the expression by replacing space characters with a plus sign ('+') and replacing other special characters with a percent sign ('%') followed by the hexadecimal representation of their ASCII code points. Here is what it looks like when we use curl to make this request:

$ curl http://flute.its.utexas.edu/tamino/cgp/test2006?_xquery=input%28%29%2ffra
gment%5bauthor+%3d+%22Lewis+Carroll%22%5d\&_encoding=utf-8
<?xml version="1.0" encoding="ISO-8859-1" ?><ino:response xmlns:ino="http://name
spaces.softwareag.com/tamino/response2" xmlns:xql="http://metalab.unc.edu/xql/">
<xq:query xmlns:xq="http://namespaces.softwareag.com/tamino/XQuery/result"><![CD
ATA[input()/fragment[author = "Lewis Carroll"]]]></xq:query><ino:message ino:ret
urnvalue="0"><ino:messageline>XQuery Request processing</ino:messageline></ino:m
essage><xq:result xmlns:xq="http://namespaces.softwareag.com/tamino/XQuery/resul
t"><fragment><index>1</index>
<author>Lewis Carroll</author>
<text>&#x201C;Just the place for a snark!&#x201D; the Bellman cried
As he landed his crew with care,
Supporting each man on the top of the tide
By a finger entwined in his hair.</text>
</fragment><fragment><index>9</index>
<author>Lewis Carroll</author>
<text>&#x2019;Twas brillig, and the slithy toves
Did gyre and gimble in the wabe:
All mimsy were the borogoves,
And the mome raths outgrabe.</text>
</fragment></xq:result><ino:message ino:returnvalue="0"><ino:messageline>XQuery 
Request processed</ino:messageline></ino:message></ino:response>$

In this example, the actual curl command (including the query URL) takes up the first two lines, and the response begins on line 3 with the XML declaration ‘<?xml ...’. Note that by default Tamino wraps the result of evaluating the query in an xq:result element, which it wraps in turn along with metadata about the request in an ino:response element. Here’s the response again, with line breaks and indenting added to show the structure:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<ino:response xmlns:ino="http://namespaces.softwareag.com/tamino/response2"
              xmlns:xql="http://metalab.unc.edu/xql/">
  <xq:query xmlns:xq="http://namespaces.softwareag.com/tamino/XQuery/result">
    <![CDATA[input()/fragment[author = "Lewis Carroll"]]]>
  </xq:query>
  <ino:message ino:returnvalue="0">
    <ino:messageline>XQuery Request processing</ino:messageline>
  </ino:message>
  <xq:result xmlns:xq="http://namespaces.softwareag.com/tamino/XQuery/result">
    <fragment>
      <index>1</index>
      <author>Lewis Carroll</author>
      <text>&#x201C;Just the place for a snark!&#x201D; the Bellman cried
            As he landed his crew with care,
            Supporting each man on the top of the tide
            By a finger entwined in his hair.</text>
    </fragment>
    <fragment>
      <index>9</index>
      <author>Lewis Carroll</author>
      <text>&#x2019;Twas brillig, and the slithy toves
            Did gyre and gimble in the wabe:
            All mimsy were the borogoves,
            And the mome raths outgrabe.</text>
    </fragment>
  </xq:result>
  <ino:message ino:returnvalue="0">
    <ino:messageline>XQuery Request processed</ino:messageline>
  </ino:message>
</ino:response>

Since this query does not alter the contents of the database, GET is the prefered HTTP method to use, but we could use POST instead. The complete message sent to Tamino (headers and everything) would look something like this:

POST /tamino/cgp/test2006 HTTP/1.1
Host: flute.its.utexas.edu
User-Agent: Sample/1.0
Content-Type: multipart/form-data; boundary=grueUFuu48
Content-Length: 289

--grueUFuu48
Content-Disposition: form-data; name="_encoding"
Content-Type: text/plain
Content-Length: 5

utf-8
--grueUFuu48
Content-Disposition: form-data; name="_xquery"
Content-Type: text/plain
Content-Length: 42

input()/fragment[author = "Lewis Carroll"]
--grueUFuu48--

The order in which the items appear does not matter to Tamino. For more information on the format of POST requests, see the Forms submission section of the HTML recommendation (which also describes the URL encoding used in GET requests) and section 5.1 of RFC 2046.

Document commands

The most commonly used X-Machine commands manipulate documents: they allow you to store, retrieve, modify, and delete individual documents or parts of documents. All the document commands require the URL to include the collection name.

We’ve already seen one of the document-related X-Machine commands, _xquery, in the example above. The value of this command is a query in the XQuery language for Tamino to evaluate. Tamino also still supports the _xql command, which takes a query in the X-Query language, but this should not be used by new applications.

Another important command is the _process command, used to store or replace documents in the database. You should always use the POST method with this command. The value of this command can take one of two formats:

The _delete X-Machine command removes complete documents from the database. Its value is an X-Query expression selecting the documents to be removed. Since it uses the obsolete X-Query language, I recommend using XQuery’s update delete ... expression instead of the _delete command.

Schema commands

Tamino collections must always be defined before they can be used, and usually you will want to define the doctypes within the collection as well. You do this by providing Tamino with a schema document via the _define command. The URL for this command does not include the collection name. This command can also update an existing schema or define a schema-free collection. Normally you will develop your schemas in the Tamino Schema Editor, which provides ways to issue this X-Machine command directly from within the editor.

There are three possible formats for the value of the _define command:

The _undefine command lets you remove a previously defined collection, schema, or doctype. To remove an entire collection, you provide the collection name. To remove a schema, you provide the collection name and the schema name separated by a slash character ('/'). If a schema defines multiple doctypes, you can remove just one of them by providing the collection name, schema name, and doctype separated by slashes. You can undefine multiple items in a single request by providing a comma separated list of the items to remove.

Sessioning

HTTP is a stateless protocol, in which context is not normally preserved between requests. For some kinds of database processing, though, we need to preserve state over a series of requests; examples include transaction processing and cursoring. Tamino provides a sessioning mechanism for these cases, with several X-Machine commands for setting up and ending sessions, as well as for transactions and cursors within a session.

To start a session, you use the _connect command, and to end it you use the _disconnect command. The form data value for these commands is an asterisk ('*'). Tamino will return two values in response to the _connect command, a session id and a session key. The session id is a unique number used by Tamino to identify the session; it will remain the same for the duration of the session. The session key is a second number identifying a particular request within the session. Whenever you issue a request as part of a session, you must pass Tamino the session id and the session key returned from the previous request. Tamino always returns the session id and session key in two non-standard HTTP headers, X-INO-Sessionid and X-INO-Sessionkey. If the response body consists of an XML document of type ino:response (the default for most X-Machine commands) then they will also be returned in the ino:sessionid and ino:sessionkey attributes of the ino:response element. To pass these values to Tamino, you usually include the form-data items _sessionid and _sessionkey as part of your request, although you may also use the HTTP headers. (For example, if you use plain URL addressing for a request within a session, you would have to use the headers since those requests don’t use form data.)

Transactions

A transaction is a set of commands that modify the database as a single unit. Tamino’s transactions meet the standard ACID criteria. When issued in the context of a session, the first command that modifies the database (_process, _define, updating _xquery, etc.) begins a transaction. The transaction ends in one of four ways:

After committing (or rolling back) a transaction you can start another one by issuing another database-modifying command. In this way a single session may include multiple consecutive transactions.

Transaction processing is affected by session parameters that you may set when issuing the _connect command. Besides _maximumTransactionDuration, _isolationLevel, _lockMode, and _lockWait determine how your transaction’s requests interact will requests from other users. See the documentation on session parameters for a complete explanation.

Cursors

Another use for sessions is cursoring. Sometimes the result of a query includes more data than a client can handle at once. Cursoring allows you to specify where in the result sequence to start and how many items to return. You create a cursor by including a _cursor command with the value open with your _xquery request. Tamino returns a value called a handle to represent the cursor. To actually retrieve results, you send a _cursor=fetch command with a _handle parameter to indicate the handle, a _position parameter indicating which item to start with, and an optional _quantity parameter to specify how many items to return. (The default quantity is 10.) When finished with a cursor you should send Tamino a _cursor=close command to release it. You can learn more from the documentation on the _cursor command.

Prepared queries

Tamino version 4.4 has added a new feature, prepared queries, that only works in a session context. The feature was added as a performance enhancement, but it can also help protect against “injection” attacks.

You create a prepared query by issuing a _prepare command. The value for this command is a query in the XQuery language. When Tamino receives this command it compiles the query and returns a handle to use to refer to it. Then you issue a _execute command to actually evaluate the query; the value for this command is prepared-xquery and it must be accompanied by a _handle parameter providing the handle returned by the _prepare command. The prepared query is released when the session ends, or you can explicitly release it with the _destroy command.

Prepared queries are designed to be used with a newly supported feature of XQuery, external variables. Let’s rewrite our example query to use this feature:

declare variable $auth as xs:string external

input()/fragment[author = $auth]

After compiling this query with a _prepare command, when you issue the _execute command you would need to include a form-data item named $auth to provide a value for this variable. In our example, we could issue multiple _execute commands, each type passing a different author’s name. The Tamino documentation includes more information on prepared queries.

Other X-Machine commands

The X-Machine also supports some miscellaneous commands for administration and monitoring.

The _diagnose command provides functions for monitoring and testing connectivity. It takes four possible values: ping, which just returns a message saying the database is alive if it’s up, version, which returns a message giving the current version of Tamino, time, which returns the amount of CPU time used by the server in user and kernel mode, and echo, which returns the HTTP headers seen by Tamino.

The _admin command provides support for various administrative functions. These functions include support for cancelling long-running requests, displaying, optimizing, and fixing indices, and changing passwords. A more complete description is available in the documentation for the _admin command.

Programming language APIs

After all that, you’re probably wondering if there isn’t an easier way to work with Tamino. For most people, the answer is “yes,” because higher-level Application Programming Interfaces (APIs) for accessing Tamino have been written for many environments and programming languages. In some cases, the APIs were written and are supported by Software AG, some were written by third parties but have been distributed (without support) by Software AG, and some are entirely third party. All these APIs, with one exception, provide a layer of abstraction on top of the X-Machine commands described above. The rest of this paper is a brief survey of most of the programming APIs for Tamino.

Tamino API for C

We’ll start out with the sole API that doesn’t use X-Machine commands, the Tamino API for C. This API (I’ll abbreviate it T4C) was introduced in Tamino version 4.1 and will not work with earlier versions. Programs that use it communicate directly with the database without going through the web server. The system where such a program runs must have Software AG’s XTS product installed, and no means for encrypting the communication is provided, so this API is best suited for applications that require high performance and do not require wide distribution. The documentation for the Tamino API for C is available on ServLine24 and distributed with Tamino.

T4C provides C functions that correspond to most of the X-Machine commands. All T4C functions are defined in a single header file, TaminoAPI4C.h, and have names beginning with ‘tac_’.Before using any of the other functions, you must call tac_init(), which takes the name of the database and returns a handle that must be passed to all other functions. When done, you must call tac_end() to release any resources acquired by the API.

Here’s a simple program using T4C to issue our example query. In the interest of brevity error checking code has been left out.

#include <stdio.h>
#include "TaminoAPI4C.h"

int main() {
  TAC_HANDLE handle;
  const char dbname[] = "cgp";
  const char collection[] = "test2006";
  const char query[] = "input()/fragment[author = \"Lewis Carroll\"]";
  const char* response;

  handle = tac_init(dbname);
  tac_xquery(handle, collection, query);
  tac_last_xml_response(handle, &response);
  printf("%s\n", response);
  tac_end(handle);

  return 0;
}

If you set up a session (tac_connect(), etc.) the T4C functions will automatically keep track of the session id and session key. T4C also includes some “second layer” functions for which source code is provided. These functions pipe Tamino input directly to or from files.

Tamino API for Java

The Tamino API for Java (documented at ServLine24) provides a high-level interface for working with Tamino in the Java programming language. It normally accesses Tamino through the web server, but can be configured to use the Tamino API for C via JNI. Most of our Tamino applications at the University of Texas use this API. I will abbreviate it T4J.

T4J primarily provides the programmer with interfaces (in the Java sense, as opposed to concrete classes) and factory methods for creating instances. For example, XML documents are represented by instances of the abstract interface TXMLObject, which has concrete classes that allow you to manipulate the document using various Java APIs for XML processing. Supported Java APIs include DOM, JDOM, and the raw string representation of the document. Here is a simple example of issuing our sample query:


final static String QUERY = "input()/fragment[author = \"Lewis Carroll\"]";
final static String DATABASE_URI = "http://flute.its.utexas.edu/tamino/cgp";

static public void main(String[] args) throws Exception {
// Create an object to represent the result document. We will use JDOM
  TXMLObject xmlObj = TXMLObject.newInstance(TJDOMObjectModel.getInstance());
// create an object representing the database connection
  TConnection dbconnect =
        TConnectionFactory.getInstance().newConnection(DATABASE_URI);
// an object accessor represents a collection within the database
//    and the Java XML API we'll use to manipulate the results.
  TXMLObjectAccessor objAcc = dbconnect.newXMLObjectAccessor(
        TAccessLocation.newInstance("test2006"),
        TJDOMObjectModel.getInstance());
// create an object representing the query
  TXQuery query = TXQuery.newInstance(QUERY);
// now we can actually query the database
  TResponse tresp = objAcc.xquery(query);
// extract the first result from the response
  if (tresp.hasFirstXMLObject()) {
    xmlObj = tresp.getFirstXMLObject();
// use JDOM methods to process document
    Element el = (Element)xmlObj.getElement();
    String content = el.getText();
    System.out.println(content);
  }
// close the connection
  dbconnect.close();
}

Tamino and J2EE

For applications that run in the J2EE environment, Software AG provides the Tamino Resource Adapter that implements the Java Connector Architecture (JCA) to make T4J available in this environment.

XML:DB

Besides Tamino, several other native XML databases are available, both commercially and through open source projects. Of course, the Tamino APIs only work with Tamino, but just like you can use ODBC or JDBC to access different relational databases, there is a generic API called XML:DB that is intended to work with any native XML database. Software AG provides an implementation of the XML:DB API that sits on top of T4J. It is not distributed with Tamino, but you can download it from the Tamino Developer Community web site. Also available at that site is a programmers cookbook for using this API. (As a note, it’s not clear to me whether much work is being done currently on XML:DB, either by Software AG or the XML:DB community; most of the documentation I’ve found is several years old.)

Tamino API for .Net

For Windows developers, Microsoft has developed the .Net application framework. The core of this framework is a Common Language Runtime (CLR) that allows programmers to mix code from any the languages that have been implemented for this runtime. The two most commonly used .Net languages are C# and Visual Basic.Net. Software AG provides a Tamino API written in C# for this runtime, which can be invoked in a .Net application written in any .Net language. (Although Microsoft created this for Windows, they submitted several specifications related to .Net to ECMA for standardization. There is an open source implementation of these specifications, Mono, available for Linux, Mac OS, and Unix platforms. I don’t know if the Tamino API for .Net will work in Mono.) Documentation for this API is available on ServLine24.

Here is some C# code for issuing our example query. (Note: I do not do Windows programming, so my examples are based on the Tamino documentation.)

TaminoConnection connection
        = new TaminoConnection("http://flute.its.utexas.edu/tamino/cgp");
connection.Open(TaminoConnectionMode.AutoCommit);
TaminoCommand cmd = connection.CreateCommand("test2006");
TaminoQuery xquery
        = new TaminoQuery("input()/fragment[author = \"Lewis Carroll\"]");
TaminoQueryResponse tresp = cmd.Query(xquery);
foreach (XmlNode node in tresp)
{
    Console.WriteLine(node.OuterXml);
}
tresp.Close();
connection.Close();

HTTP Client API for ActiveX

For Windows shops that haven’t yet moved to .Net, Software AG provides an ActiveX client that can be used from Visual Basic applications. See the documentation on ServLine24 for more information.

HTTP Client API for JScript

Software AG also provides a library of JavaScript functions that can be used to access Tamino, documented on ServLine24. These functions are all defined in a single file, TaminoLib.js. The documentation indicates that this library is Windows only, and in fact my experiments suggest that it only works in Internet Explorer. (If the documentation is correct, it can also work in server-side Javascript in IIS.) It is only included in the development tools when Tamino is installed on Windows, although the TaminoLib.js file is also included in the “Welcome to Tamino” part of the site when installed in Unix. I believe an ambitious Javascript coder could use it as a starting point to develop functions that would work in other browsers.

Here is some sample code using our example query:

var db = new TaminoClient("http://flute.its.utexas.edu/tamino/cgp/test2006");
var qresponse = db.xquery("input()/fragment[author = \"Lewis Carroll\"]");

var xqueryResultList = qresponse.getResult().childNodes;
// xqueryResultList contains DOM objects representing the result nodes.

You should think about several issues before using this API in a browser. First, depending on the target audience for your application, many of the people you are writing for may not use Internet Explorer, and therefore would not be able to use your application. Second, the pages containing the Javascript will normally have to be served from the same web server as Tamino. Third, if you have any security on your database, the userids and passwords will have to appear somewhere in the text of the Javascript files, where any user who does a little looking can find them.

Tamino API for PHP

After installing Tamino version 4.4, if you look in the directory where it was installed you will find a subdirectory named “Unsupported”. There you will find two additional APIs for Tamino, one for Perl and one for PHP. First, let’s look at the PHP API. (Although this API is officially unsupported, there is a forum for it on Software AG’s developer site.) There are two versions of this API, one for PHP 4 and on for PHP 5. It consists of two files, TaminoAPI.php and TaminoHelperFunctions.php; the version 5 API also has a third file, TaminoAPIException.php. Here is how we might issue our example query:

<?php
require_once("TaminoAPI.php");
require_once("TaminoHelperFunctions.php");

$tamino = &new TaminoAPI("http://flute.its.utexas.edu",80,"cgp","","");
$tamino->setCollection("test2006");

if($tamino->xquery("input()/fragment[author = \"Lewis Carroll\"]")){
    $tamino->printResultBody();
} else {
    thfPrintError($tamino);
}
?>

There is some limited documentation for the Tamino API for PHP included with the implementation files. We have applications at the University of Texas that use this API to access Tamino.

Tamino API for Perl

The second distributed but unsupported API for Tamino is the one for Perl. Although the distribution directory contains subdirectories name “Documentation” and “examples”, in my case at least these directories were empty. However, running the installation procedure does create a Unix man page. From what I understand from this document (and I’m not a Perl programmer) using our example query would look something like this:

use Tamino;
$db = Tamino->new("http://flute.its.utexas.edu/tamino/cgp");
$xml = $db->xquery("test2006", "input()/fragment[author = \"Lewis Carroll\"]");

The documentation indicates that this API has no current support for authentication or transactions.

Natural

Natural does not have an API for Tamino, but the REQUEST DOCUMENT and PARSE statements allow you to access Tamino using X-Machine commands and process the results. These statements have been available in Open Systems Natural for the past few releases and are available as of version 4.2 in mainframe Natural if the ICU library has been installed. Here is some Natural code to process our example query:

REQUEST DOCUMENT FROM "http://flute.its.utexas.edu/tamino/cgp/test2006"
    WITH DATA
        NAME  '_xquery'
        VALUE 'input%28%29%2ffragment%5bauthor+%3d+%22Lewis+Carroll%22%5d'
        NAME  '_encoding' VALUE 'utf-8'
    RETURN RESPONSE #TAMINO-DOCUMENT
PARSE XML #TAMINO-DOCUMENT INTO NAME #ELEM-NAME VALUE #XML-TEXT
    IF #ELEM-NAME = 'index' OR = 'author' OR = 'text'
        WRITE #ELEM-NAME ':' #XML-TEXT
    END-IF
END-PARSE

Note that the query must be URL encoded. You should use dynamic variables for target fields (in this example, #TAMINO-DOCUMENT, #ELEM-NAME, and #XML-TEXT) when using these statements, as the size of the documents and elements is unpredictable.

Ruby

I’d like to end the list of APIs with a project of my own. I have been developing a Tamino API for Ruby that I hope to be able to release soon. Here is what the code for our example query looks like:

require 'tamino/database'
require 'tamino/xquery'

query_string = 'input()/fragment[author = "Lewis Carroll"]'
xquery = Tamino::XQuery.new('test2006', query_string)

Tamino::Database.connect('http://flute.its.utexas.edu/tamino/cgp') { |db|
    xquery.each_result(db) {|doc|
        puts doc
    }
}

Conclusion

By building access to Tamino on top of the widely-supported HyperText Transport Protocol, Software AG has made it comparatively easy to use Tamino from a variety of languages and environments. For many of the more popular programming languages, higher-level APIs are also available to make using Tamino even easier.