Advanced Usage

This document covers some advanced XQuery usage. This functionality is partly tied to using Tamino query pragmas that affect the processing of the query. With Tamino query pragmas you can import modules and add declarations for namespaces and collations as well as functions and external variables. They have the syntactic form {?piname?}, which is why they were previously called XQuery processing instructions. This document discusses some of these concepts:

Another important area of advanced XQuery usage is related to performance questions and optimizing your Tamino XQuery programs. You can find more information about this topic in the Performance Guide.


Namespaces

A namespace declaration defines a namespace prefix and associates it with a valid namespace URI. This namespace declaration is valid throughout the query expression. For example, you can define your own namespace:

declare namespace dotcom="http://company.dot.com/namespaces/corporate"
for $a in input()/bib/book
return 
<dotcom:bookshelf>
 { $a } 
</dotcom:bookshelf>

It is an error (INOXQE 6356) to redefine a namespace:

declare namespace dotcom="http://company.dot.com/namespaces/corporate"
declare namespace dotcom="http://company.dot.com/new-namespaces/corporate"
for $a in input()/bib/book
return 
<dotcom:bookshelf>
 { $a } 
</dotcom:bookshelf>

But you can use two different namespaces at the same time:

declare namespace dotcom="http://company.dot.com/namespaces/corporate"
declare namespace tf="http://namespaces.softwareag.com/tamino/TaminoFunction"
for    $a in input()/bib/book
where  tf:containsText($a/title, "UNIX")
return
<dotcom:bookshelf>
 { $a } 
</dotcom:bookshelf>

You can also declare default namespaces that can be applied to elements or attributes alike.

declare default element namespace "http://company.dot.com/namespaces/corporate"
for $a in input()/bib/book
return 
<bookshelf>
 { $a } 
</bookshelf>

The default element namespace applies to all unqualified element names, in both expressions and constructors, unless a different default namespace is defined in an inner scope, as shown in the following example:

declare default element namespace "X"
declare namespace y="Y"
let $x := <x1>{element x2{<x3 xmlns="Y">{element x4{}}</x3>}}</x1>
return $x/x2/y:x3/y:x4

In this example, the default element namespace applies to all occurrences of x1 and x2; however, it does not apply to x3 and x4, because their default namespace is defined by the inner specification xmlns="Y".

The default element namespace does not apply to attribute names.

There is no default attribute namespace.

There is a default function namespace. It applies to function names in function definitions and function calls.

Tip:
You need not declare a namespace in the prolog if you use one of the predeclared namespaces. You can find the list of predeclared namespaces in the description of NamespaceDecl in the XQuery Reference Guide

User-Defined Functions

In addition to the large set of XQuery functions as defined by the W3C and those that are specific to Tamino, you can write your own functions. User-defined functions consist of a function header representing its signature and the function body containing an XQuery expression. The header consists of an identifier, a list of parameters and corresponding type information, and the type of the return value. Type information is optional: if you omit it, item* will be assumed. Note that the set of allowed types is restricted to all simple types and some common XQuery types (the reference description of FunctionDecl contains a complete list).

The function identifier must be a QName together with a non-empty namespace prefix. To avoid introducing a new namespace for locally used functions, that is functions used in the current module, XQuery provides an implicit namespace to which the prefix local is bound. This is an example for a function that uses this local namespace:

declare function local:siblings($a as element()) as element()*
{
	for    $x in $a/../*
	where  not($x is $a)
	return $x	
}

let    $a := <a> <a1>foo</a1> <a2>bar</a2> </a>
return local:siblings($a/a1)

It takes an element node as argument and returns all its sibling element nodes. Following the function declaration it is directly used in a query body resulting in the node <a2>bar</a2>.

User-defined functions may be recursive, that is a function can contain a call to itself:

declare function local:depth($e as node()) as xs:integer
{
	if (not($e/*))	
	then
		1
	else		
		max(for $c in $e/* return local:depth($c)) + 1
}

This function computes the depth of a node by calling itself for its children nodes.

To avoid running out of memory during the execution of a user-defined function, the XQuery processor restricts the amount of memory that can be used by a single query processing thread during query execution. The amount of memory can be modified by an XQuery pragma. Similarly, you can avoid a stack overflow by restricting the call stack of the function. The following pragma sets the available memory to 100 MB and the maximum call stack depth to 1000 (default value is 256):

{?execution memory="100" call-stack-depth="1000"?}

Note:
See also the Performance Guide for information about optimizing user-defined functions using inlining techniques.

Defining and Using Modules

In XQuery, code can be organized into modules. There are two principal kinds of modules: a main module that contains the query body, and library modules that you can import into other library modules or into the main module. For example, you can put the functions defined in the last section into a module of its own:

module namespace tree="http://www.examples.com/tree"

declare function tree:depth($e as node()) as xs:integer
{
	if (not($e/*))	
	then
		1
	else		
		max(for $c in $e/* return tree:depth($c)) + 1
}

declare function tree:siblings($a as element()) as element()*
{
	for    $x in $a/../*
	where  not($x is $a)
	return $x	
}

Before you can use the functions defined in a library module, you must store the module at a special place to make it directly accessible to the XQuery processor. In Tamino, modules are stored in the system collection ino:source. From a module definition, some meta data is derived to provide access to module properties that are relevant for efficient lookup of modules and user-defined functions. The meta data also simplifies querying module properties in order to generate interface descriptions such as WSDL definitions.

Modules are stored as non-XML data in the ino:module doctype of the ino:source collection. This way you can define modules using the _process command and remove them with _delete. Alternatively, you can use the Tamino Interactive Interface.

Note that a library module must not contain variable declarations. Also, Tamino query pragmas which are directives to the query processor are not allowed.

To use a module, you must import it with an import statement. Consider using the library of tree functions:

import module namespace tree="http://www.examples.com/tree"

let    $a := <a> <a1>foo</a1> <a2>bar</a2> </a>
return tree:siblings($a/a1)

This import statement imports the module with the target namespace http://www.examples.com/tree. Note that you cannot import the same module more than once.

To check for existing modules, you can query the ino:source collection in the following way to retrieve the non-XML data of all modules of a given namespace:

declare namespace ino="http://namespaces.softwareag.com/tamino/response2" 
root(
 for $a in collection("ino:source")/ino:module
 where $a/@ino:targetNamespace="http://www.examples.com/tree"
 return $a
)

The query functions defined in an XQuery module may also be used for defining a computed index. For details on computed indexes please refer to Performance Guide > Advanced Indexes.

The XQuery Tool delivered with Tamino provides convenient support for maintaining XQuery modules.

Serializing Query Results

When Tamino executes a query, it handles the query in terms of its data model: an XML document is treated as a tree with a document root and different types of nodes. The query result is then serialized and wrapped into a Tamino response document. You can determine the serialization by using a Tamino query pragma, or XQuery processing instruction, which always has the form {?piname?}. Like a regular XML processing instruction, it can have additional information. This section discusses the available serializations:

Suppressing the Response Wrapper

Normally, when Tamino executes a query, it returns the result using a response wrapper. This is the result of the very first query in this manual:

Tamino response to an XQuery

You can, however, suppress the response wrapper so that you get the bare result by using the serialization pragma and choosing the method "xml":

{?serialization method="xml"?}
<fact> This section contains { 3 + 6 } examples.</fact>

The output is then much shorter and is restricted to the query result as such:

<fact>This section contains 9 examples.</fact>

Setting the MIME Media Type

Using {?serialization method="xml"?} always implies that "text/xml" is used as media type for the response document. You can change this by adding media-type. The following query returns a literal SVG document which has the media type "image/svg+xml":

{?serialization method="xml" media-type="image/svg+xml"?}
<svg width="200" height="200">
  <circle cx="100" cy="50" r="40" stroke="black" stroke-width="3" fill="red"/>
</svg>

This delivers the SVG document with the appropriate MIME media type which is then set in the HTTP response header contentType. It also means that the normal response wrapper is not used.

Defining a Custom Output Handler

You can define your own method of delivering the response document by using a server extension (SXS) as output handler. This output handler will then act in place of the internal Tamino XQuery processor. In general you use:

{?serialization method="<outputHandler>"
  parameter="<outputHandlerParameter>"?}

For example, this allows using the XSLT server extension to transform query results on-the-fly:

{?serialization method="XSLT.transform"
  parameter="stylesheets" parameter="stylesheets/patientRecords.xsl"?}
<report>
  { for    $a in collection('Hospital')/patient
    return <patient>{ $a/name, $a/sex, $a/born, $a/address }</patient>
  }
</report>

The method attribute identifies the output handler. The two parameters specify the XSL stylesheet to be used and the query. Tamino then passes the query result, the list of patients, to this output handler which transforms it using the stylesheet patientRecords.xsl.

Please note that you can also use media-type in the serialization pragma for an output handler.

Collations

For certain operations on strings such as comparison or sort operations collations are used. They inherently determine the result of a comparison to accommodate for special regional purposes or linguistic needs. If a collation is not defined, Tamino uses Unicode codepoints for performing these operations. There are two ways to use collations: In the schema you can add collation information per element by using tsd:collation. In addition, you can assign a default collation in the query prolog such as:

declare default collation "collation?language=fr"

The string literal is the collation URI which is interpreted as relative URL with the base URI http://www.softwareag.com/tamino. In this example, the collation defined for the French language is used.

You can use the Tamino-specific XQuery function tf:getCollation to deploy collations as in the following query:

for $i in input()/patient/name
where compare($i/surname, 'Müller', tf:getCollation($i/surname)) = 0
return $i

This query returns those name elements whose surname child element has a contents that is compared equal to the string "Müller" using the collation information defined in the Tamino schema for surname elements.

Please see also the section Collations in the Tamino XML Schema User Guide for details about collation definitions in schemata.