SQL Processor
1. Introduction
The SQL processor provides an XML interface to any SQL database accessible through
JDBC. It allows to easily query databases and produce XML outputs readily usable
by other processors. Conversely, the SQL processor allows to perform updates,
insertions and deletions from XML data generated by other processors.
2. Inputs and Outputs
Type |
Name |
Purpose |
Mandatory |
Input |
config |
Configuration in XML-SQL |
Yes |
Input |
data |
Source XML data |
No |
Input |
datasource |
Datasource configuration |
No |
Output |
data |
Result XML data |
No |
If the data input is not connected, the SQL processor configuration
must not use XPath expressions operating on it. If this condition is not satisfied,
the SQL processor generates an error at runtime. It is typically useful to omit the
data input when the SQL processor only reads data from the database and
generates an output XML document.
If the data output is not connected, the output of the SQL processor is
simply ignored. It is typically useful to omit the data output when the
SQL processor only updates or insert data into the database from an input XML
document.
3. XML-SQL
3.1 Configuration
XML-SQL is a simple set of XML tags that allow you to integrate SQL queries and
XML. XML-SQL lives in the http://orbeon.org/oxf/xml/sql namespace,
which is usually mapped to the sql prefix. An XML-SQL configuration
input has the following format:
<sql:config xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <!-- Optional user content -->
<sql:connection> <!-- Datasource name (can also be specified with optional "datasource" input) -->
<sql:datasource>my-datasource</sql:datasource>
<!-- ... -->
</sql:connection> <!-- Optional user content -->
</sql:config>
The sql:datasource element specifies a datasource name under
java:comp/env/jdbc. In the example above, the datasource named
my-datasource is used. How the datasource is configured depends on
the application server used. Please refer to the documentation of your
application server for more information.
Alternatively, the sql:datasource element can be omitted. In that
case, the datasource input of the SQL processor must be connected
to an external datasource definition, which describes database connections
without using JNDI names mapped by the container. This is an example of
datasource definition:
<datasource> <!-- Specify the driver for the database -->
<driver-class-name>org.hsqldb.jdbcDriver</driver-class-name>
<!-- This causes the use of the embedded database -->
<uri>jdbc:hsqldb:file:orbeondb</uri>
<!-- Optional username and password -->
<username>sa</username>
<password/>
</datasource>
Warning
External datasource definitions do not use connection pooling at the moment.
Because creating database connections is usually an expensive operation, they
should be used only for development or demo purposes.
The sql:config element can contain any number of user-defined
content before and after the sql:connection element.
3.2 sql:execute
The sql:execute element controls the execution of a single
SQL query or update (update, insert or delete). It must start with either
a sql:query or a sql:update element, which contains
the SQL to execute. sql:query can
be followed by sql:results, sql:no-results or
both elements. Any number of sql:execute elements can be used
in order to execute several queries or updates within a single connection
declaration.
<!-- Optional user content -->
<sql:execute xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <!-- Optional user content -->
<sql:results> </sql:results> <sql:no-results> </sql:no-results> </sql:execute> <!-- Optional user content -->
<sql:execute xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <sql:update> </sql:update> </sql:execute> <!-- Optional user content -->
3.3 sql:query and sql:update
sql:query and sql:update encapsulate SQL
statements. Like in JDBC, a distinction is made between queries (SQL
select) and updates (SQL update, insert
and delete):
<sql:query xmlns:sql="http://orbeon.org/oxf/xml/sql">select * from employee</sql:query>
<sql:update xmlns:sql="http://orbeon.org/oxf/xml/sql">insert into employees values ('John', 'Doe')</sql:update>
It is possible to pass parameters to a query using the sql:param
element. sql:param requires a type attribute that
specifies the type of the parameter to set. The type system is borrowed from the
XML Schema Part 2: Datatypes
specification. The following types are supported:
- xs:string
- xs:int
- xs:boolean
- xs:decimal
- xs:float
- xs:double
- xs:dateTime
- xs:date
- xs:base64Binary
- oxf:xmlFragment
xs:date and xs:dateTime should be
in one of the following formats:
- CCYY-MM-DDThh:mm:ss.sss
- CCYY-MM-DDThh:mm:ss
- CCYY-MM-DD
Unless a getter is nested in the sql:param element (see the section
about nested queries below), a select attribute is mandatory. Its
content is evaluated as an XPath expression against the input XML document:
<sql:query xmlns:sql="
http://orbeon.org/oxf/xml/sql"
>select first_name, last_name from employee where employee_id in (<sql:param type="xs:int" select="/query/employee-id[1]"/>,<sql:param type="xs:int" select="/query/employee-id[2]"/>)
</sql:query>
Any number of sql:param elements may be used.
sql:param supports an optional boolean replace
attribute, that can take the value true or false (the
default). When replace is set to true, the parameter
is simply replaced in the query, instead of being set on a JDBC
PreparedStatement. This however works only with the
xs:int and oxf:literalString types. This attribute is
useful to dynamically generate parts of SQL queries, or to set parameters that
do not allow being set via JDBC's setYyy methods. For example,
with SQL Server:
<sql:query xmlns:sql="
http://orbeon.org/oxf/xml/sql"
>select top <sql:param type="xs:int" select="/query/max-rows" replace="true"/> * from employee
</sql:query>
sql:param supports an optional separator attribute.
When that attribute is present, the result of the XPath expression in the
select attribute is interpreted as a node-set. One query parameter
is set for each element in the node set, separated by the characters in the
separator. For example:
<sql:query xmlns:sql="
http://orbeon.org/oxf/xml/sql"
>select * from book where book_id in (<sql:param type="xs:int" select="/query/book-id" separator=","/>)
</sql:query>
Assuming the input document contains:
<query> <book-id>5</book-id>
<book-id>7</book-id>
<book-id>11</book-id>
<book-id>13</book-id>
</query>
The following equivalent query will be executed:
<sql:query xmlns:sql="
http://orbeon.org/oxf/xml/sql"
>select * from book where book_id in (5, 7, 11, 13)
</sql:query>
3.4 Root Element
It is important to make sure that one and exactly one root element is output by
the SQL processor. A good place to put such a root element is around the
sql:connection element:
<sql:config xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <employees> <sql:connection> </sql:connection> </employees> </sql:config>
3.5 sql:results and sql:no-results
These elements must be used only in conjunction with sql:query.
When a query return at least one row, the subsequent sql:results
is evaluated. When no row is returned, sql:no-results is evaluated.
sql:results must contain a
sql:row-results element, which is evaluated once
for every row of the result set. sql:row-results
typically contains user-defined content, as well as column
getters such as sql:get-column and
sql:get-columns.
<sql:results xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <sql:row-results> <employee> <first-name>
<sql:get-column type="xs:string" column="first_name"/>
</first-name> <last-name>
<sql:get-column type="xs:string" column="last_name"/>
</last-name> </employee> </sql:row-results> </sql:results>
Assuming the result set contains two rows with "John Doe" and "Billy Smith",
the above code produces the following XML fragment:
<employee> <first-name>John</first-name>
<last-name>Doe</last-name>
</employee> <employee> <first-name>Billy</first-name>
<last-name>Smith</last-name>
</employee>
3.6 Getting Column Values
You can get column values with the
sql:get-column and
sql:get-columns elements.
sql:get-column takes a mandatory
column attribute and a mandatory
type attribute. The type system is borrowed
from the XML
Schema Part 2: Datatypes specification. The following
types are supported:
- xs:string
- xs:int
- xs:boolean
- xs:decimal
- xs:float
- xs:double
- xs:dateTime
- xs:date
- xs:base64Binary
- oxf:xmlFragment
xs:dateTime returns a date in the following format:
CCYY-MM-DDThh:mm:ss.sss.
xs:date returns a date in the following format:
CCYY-MM-DD.
oxf:xmlFragment is a special type that gets the column as a
string, parses it as an XML fragment, and embeds the resulting XML in the SQL
processor output.
Note
For compatibility with XPath 1.0, xs:float and
xs:double do not return values in the exponential notation. For
example, instead of 1.2E10, 12000000000 is returned.
When the number of columns returned is large, it is convenient to use
sql:get-columns, which automatically determines what columns are
available and generates elements accordingly. sql:get-columns takes
an optional prefix attribute specifying the output namespace prefix
to use for all the elements, and an optional format attribute
specifying how column names are converted. It also supports any number of
embedded exclude elements that specify columns to exclude from the
result.
sql:get-columns supports an all-elements attribute.
If set to true, an element is output for a column even if that
column returns a null value. If missing or set to false, no element
is output for a null column.
The namespace prefix, if specified, must have been mapped to a namespace URI.
If no format is specified, the original column names are used. Specifying the
xml format converts all column names to lower case and transforms
"_" into "-".
The example below generates the same results as above:
<sql:results xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <sql:row-results> <employee> <sql:get-columns format="xml"/>
</employee> </sql:row-results> </sql:results>
Not specifying the xml format generates the
following results:
<employee> <first_name>John</first_name>
<last_name>Doe</last_name>
</employee> <employee> <first_name>Billy</first_name>
<last_name>Smith</last_name>
</employee>
It is possible to exclude the first_name column
as follows:
<sql:results xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <sql:row-results> <employee> <sql:get-columns format="xml"> <exclude>first_name</exclude>
</sql:get-columns> </employee> </sql:row-results> </sql:results>
This generates the following results:
<employee> <last_name>Doe</last_name>
</employee> <employee> <last_name>Smith</last_name>
</employee>
When no rows are returned, the content of the sql:results element
is completely ignored. Instead, sql:no-results is evaluated.
sql:no-results may contain user-defined elements and nested queries
(see below).
3.7 Specifying a SQL Type
When setting a parameter of type xs:string or
oxf:xmlFragment, it is possible to specify an additional attribute
on sql:param: sql-type. By default, text is written
using the JDBC setString() method. In case the data must be stored in the
database as a Character Large OBject (CLOB) or other database-specific types,
it is necessary to tell PresentationServer that a different API must be used. For example, to
write a string into a CLOB:
<sql:execute xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <sql:update>insert into test_clob_table (clob_column) values (<sql:param select="/document/text" type="xs:string" sql-type="clob"/>)
</sql:update> </sql:execute>
The same string can be written as a regular varchar type as
follows:
<sql:execute xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <sql:update>insert into test_table (varchar_column) values (<sql:param select="/document/text" type="xs:string" sql-type="varchar"/>)
</sql:update> </sql:execute>
varchar is actually the default, so you can simply omit the
sql-type and write:
<sql:execute xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <sql:update>insert into test_table (varchar_column) values (<sql:param select="/document/text" type="xs:string"/>)
</sql:update> </sql:execute>
Note
The disadvantage of using database columns of type varchar is that
those are severely limited in size, for example 4000 bytes in the case of Oracle
9. The maximum size of CLOB columns is usually much larger, for example up to 4
GB with Oracle 9. In order to store large strings or large XML documents, it is
therefore necessary to use the CLOB type.
The following values are supported for sql-type:
- varchar (the default)
- clob
- xmltype (see Reading and Writing XML Documents below)
Warning
Using the clob and xmltype SQL types is currently
only supported with the following application server / database
combinations:
- Tomcat 4.1 and Oracle 9
- WebLogic 8.1 and Oracle 9
Please contact Orbeon to inquire about
supporting additional systems.
Reading from CLOB columns on the other hand is supported with all JDBC
drivers that support the CLOB API.
3.8 Reading and Writing XML Documents
As explained in the section about sql-type above, when text data,
in particular XML data, is large, it is best stored as a CLOB type or, in the
case of XML, as a native database XML data type such as the Oracle
XMLType data type.
Reading and writing XML data is supported to and from database CLOBs and, with
Oracle 9, to and from XMLType. The oxf:xmlFragment
type must be used. To write XML data to a CLOB, use the
oxf:xmlFragment type as follows:
<sql:execute xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <sql:update>insert into test_clob_table (clob_column) values (<sql:param select="/*" type="oxf:xmlFragment" sql-type="clob"/>)
</sql:update> </sql:execute>
The XPath expression must return one element node. The result of the XPath
expression specified in the select attribute is converted into a
new XML document having as root element the selected element node. The document
is then serialized to a character stream and stored as a CLOB.
To read a document from a CLOB column, use the oxf:xmlFragment
type as follows:
<sql:execute xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <sql:query>select clob_column from test_clob_table
</sql:query> <sql:results> <rows> <sql:row-results> <row> <sql:get-column type="oxf:xmlFragment" column="clob_column"/>
</row> </sql:row-results> </rows> </sql:results> </sql:execute>
For each row returned, the character data stored in the CLOB column is read as
text and parsed into an XML fragment. The fragment must be well-formed,
otherwise an exception is thrown. The resulting fragment is then embedded into
the SQL processor output.
With Oracle 9, it is also possible to write to the native Oracle
XMLType data type:
<sql:execute xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <sql:update>insert into test_xmltype_table (xmltype_column) values (<sql:param select="/*" type="oxf:xmlFragment" sql-type="xmltype"/>)
</sql:update> </sql:execute>
Note
The benefit of using the Oracle
XMLType data type is that XML is
stored in a structured way in the database. This allows creating indexes on XML
data, doing partial document updates, etc. This however requires creating an XML
schema. For more information, please refer to the
Oracle
XML DB Developer's Guide.
Reading from an XMLType column is done the same way as with a CLOB
column:
<sql:execute xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <sql:query>select xmltype_column from test_xmltype_table
</sql:query> <sql:results> <rows> <sql:row-results> <row> <sql:get-column type="oxf:xmlFragment" column="xmltype_column"/>
</row> </sql:row-results> </rows> </sql:results> </sql:execute>
Warning
Writing to CLOB columns, as well as writing and reading to and from
XMLType columns, is currently only supported with the
following application server / database combinations:
- Tomcat 4.1 and Oracle 9
- WebLogic 8.1 and Oracle 9
Please contact Orbeon to inquire about
supporting additional systems.
Reading from CLOB columns on the other hand is supported with all JDBC
drivers that support the CLOB API.
3.9 Reading and Writing Binary Data
Reading and writing binary data is supported to and from database Binary Large
OBjects (BLOBs) as well as binary types (BINARY, VARBINARY
and LONGVARBINARY SQL types). The
xs:base64Binary type (read and write) or the xs:anyURI
type (write only) must be used. To write to a BLOB, use the
xs:base64Binary type as follows:
<sql:execute xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <sql:update>insert into test_blob_table (blob_column) values (<sql:param select="/*" type="xs:base64Binary"/>)
</sql:update> </sql:execute>
The result of the XPath expression specified in the select
attribute is converted into a character string, following the XPath semantics.
That string is then interpreted as Base64-encoded data, before being written to
the BLOB column. For example, the following input document:
<root>/9j/4AAQSkZJRgABAQEBygHKAAD/2wBDAAQDAwQDAwQEBAQFBQQFBwsHBwYGBw4KCggLEA4R ... KKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//2Q==
</root>
Is converted to the following string when the expression /* is
applied:
/9j/4AAQSkZJRgABAQEBygHKAAD/2wBDAAQDAwQDAwQEBAQFBQQFBwsHBwYGBw4KCggLEA4R
...
KKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//2Q==
With xs:anyURI, the result of the XPath expression is converted
into a string and interpreted as a URL. The URL is read, and the resulting data
is stored into the BLOB column. For example:
<sql:execute xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <sql:update>insert into test_blob_table (blob_column) values (<sql:param select="/my/uri" type="xs:anyURI"/>)
</sql:update> </sql:execute>
Note
XForms file uploads typically generate URLs in XForms instances if the type
chosen for the uploaded file in the XForms model is xs:anyURI. The
advantage of using xs:anyURI is that large resources do not have
to reside entirely in memory.
To read a BLOB or BINARY column, use the xs:base64Binary type as
follows:
<sql:execute xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <sql:query>select blob_column from test_blob_table
</sql:query> <sql:results> <rows> <sql:row-results> <row> <sql:get-column type="xs:base64Binary" column="blob_column"/>
</row> </sql:row-results> </rows> </sql:results> </sql:execute>
This will produce the following result if the document above was written to the
database first:
<rows> <row>/9j/4AAQSkZJRgABAQEBygHKAAD/2wBDAAQDAwQDAwQEBAQFBQQFBwsHBwYGBw4KCggLEA4R ... KKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//2Q==
</row> </rows>
Note
Base64-encoded binary documents are widely used in PresentationServer, in
particular in the following cases:
-
Request
generator: as the result of certain types of HTML form
submissions (typically file uploads) or request body submission,
Base64-encoded representation of the uploaded files may be stored
into the request document.
-
XForms upload: as the result
of a file upload, a Base64-encoded representation of the uploaded
file may be stored into the XForms instance.
-
URL generator:
can read binary documents and produce Base64-encoded output
according to the standard binary document
format.
-
HTTP
serializer: can convert Base64-encoded input according to the
binary document
format into a binary stream.
-
SQL processor: as described in this document, it is able to read
and write Base64-encoded binaries.
Warning
Writing to BLOB columns is currently only supported with the following
application server / database combinations:
- Tomcat 4.1 and Oracle 9
- WebLogic 8.1 and Oracle 9
Please contact Orbeon to inquire about
supporting additional systems.
Reading from BLOB columns on the other hand is supported with all JDBC
drivers that support the BLOB API.
3.10 sql:value-of and sql:copy-of
The sql:value-of and sql:copy-of elements have the
same semantics as their XSLT 1.0 couterparts. They work against the SQL
processor's input XML document.
Those elements support functions in the
http://orbeon.org/oxf/xml/sql namespace. The only function
supported for the moment is sql:row-position(), which returns, in a
sql:row-results, the index of the current row in the result set,
starting with row number 1. For example:
<sql:results xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <sql:row-results> <employee> <position>
<sql:value-of select="sql:row-position()"/>
</position> <first-name>
<sql:get-column type="xs:string" column="first_name"/>
</first-name> <last-name>
<sql:get-column type="xs:string" column="last_name"/>
</last-name> </employee> </sql:row-results> </sql:results>
This generates the following results:
<employee> <position>1</position>
<first-name>John</first-name>
<last-name>Doe</last-name>
</employee> <employee> <position>2</position>
<first-name>Billy</first-name>
<last-name>Smith</last-name>
</employee>
3.11 Multiple Updates
The sql:update element supports an optional
select attribute. It is evaluated as an XPath
expression against the input XML document. The expression must
return a node-set (which may be empty). The update statement is
executed once for every node returned. select
attributes on nested sql:param elements are
evaluated using the selected node as current node. With the
following input XML document:
<employees> <employee> <first-name>John</first-name>
<last-name>Doe</last-name>
</employee> <employee> <first-name>Billy</first-name>
<last-name>Smith</last-name>
</employee> </employees>
The following update inserts two rows in the database:
<sql:update select="/employees/employee" xmlns:sql="
http://orbeon.org/oxf/xml/sql"
>insert into employee (first_name, last_name) values (<sql:param type="xs:string" select="first-name"/>,<sql:param type="xs:string" select="last-name"/>)
</sql:update>
3.12 Nested queries
Consider the following three SQL tables organized in a tree.
The level2 table references the level1 table, and the level3
table references the level2 table.
level1
level2
level2_id |
level1_id |
value |
1 |
1 |
a |
2 |
1 |
b |
3 |
2 |
c |
4 |
2 |
d |
level3
level2_id |
value |
1 |
a |
1 |
b |
2 |
c |
2 |
d |
3 |
e |
3 |
f |
4 |
g |
4 |
h |
A flat representation of the three tables joined on their
respective foreign keys yields the following rows:
level1.value |
level2.value |
level3.value |
a |
a |
a |
a |
a |
b |
a |
b |
c |
a |
b |
d |
b |
c |
e |
b |
c |
f |
b |
d |
g |
b |
d |
h |
Often it is useful to group results in order to output certain
values only once. In a table, this can look like the following:
level1.value |
level2.value |
level3.value |
a |
a |
a |
b |
b |
c |
d |
b |
c |
e |
f |
d |
g |
h |
A generalization of this consists in generating output of the form:
<result> <group1> <group1-header>
<level1-value>a</level1-value>
</group1-header> <group1-members> <group2> <group2-header>
<level2-value>a</level2-value>
</group2-header> <group2-members> <level3-value>a</level3-value>
<level3-value>b</level3-value>
</group2-members> <group2-footer>
<level2-value>a</level2-value>
</group2-footer> </group2> <group2> <group2-header>
<level2-value>b</level2-value>
</group2-header> <group2-members> <level3-value>c</level3-value>
<level3-value>d</level3-value>
</group2-members> <group2-footer>
<level2-value>b</level2-value>
</group2-footer> </group2> </group1-members> <group1-footer>
<level1-value>a</level1-value>
</group1-footer> </group1> <group1> <group1-header>
<level1-value>b</level1-value>
</group1-header> <group1-members> <group2> <group2-header>
<level2-value>c</level2-value>
</group2-header> <group2-members> <level3-value>e</level3-value>
<level3-value>f</level3-value>
</group2-members> <group2-footer>
<level2-value>c</level2-value>
</group2-footer> </group2> <group2> <group2-header>
<level2-value>d</level2-value>
</group2-header> <group2-members> <level3-value>g</level3-value>
<level3-value>h</level3-value>
</group2-members> <group2-footer>
<level2-value>d</level2-value>
</group2-footer> </group2> </group1-members> <group1-footer>
<level1-value>b</level1-value>
</group1-footer> </group1> </result>
There are two ways of generating such results with XML-SQL.
The first way is to use nested queries. A first query returns all the
rows in the level1 table. Then, for each row returned, a second query
returns all rows in the level2 table referencing the current row's
level1_id. Similarly, for each row returned by that second query,
a new query is done to get the relevant level3 rows. The code would look
as follows:
<sql:config xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <results> <sql:connection> <sql:datasource>my-datasource</sql:datasource>
<sql:execute> <sql:query>select level1.value value, level1.level1_id id from level1 order by level1.value
</sql:query> <sql:results> <sql:row-results> <group1> <group1-header> <level1-value>
<sql:get-column type="xs:string" column="value"/>
</level1-value> </group1-header> <group1-members> <sql:execute> <sql:query>select level2.value value, level2.level2_id id from level2 where level2.level1_id =<sql:param type="xs:int"><sql:get-column type="xs:int" column="id"/></sql:param>
order by level2.value </sql:query> <sql:results> <sql:row-results> <group2> <group2-header> <level2-value>
<sql:get-column type="xs:string" column="value"/>
</level2-value> </group2-header> <group2-members> <sql:execute> <sql:query>select level3.value value from level3 where level3.level2_id =<sql:param type="xs:int"><sql:get-column type="xs:int" column="id"/></sql:param>
order by level3.value </sql:query> <sql:results> <sql:row-results> <level3-value>
<sql:get-column type="xs:string" column="value"/>
</level3-value> </sql:row-results> </sql:results> </sql:execute> </group2-members> <group2-footer> <level2-value>
<sql:get-column type="xs:string" column="value"/>
</level2-value> </group2-footer> </group2> </sql:row-results> </sql:results> </sql:execute> </group1-members> <group1-footer> <level1-value>
<sql:get-column type="xs:string" column="value"/>
</level1-value> </group1-footer> </group1> </sql:row-results> </sql:results> </sql:execute> </sql:connection> </results> </sql:config>
A nested query can access parameters from the input XML
document like any regular query. It can also access results
from outer queries by nesting a getter in a
sql:param element. In that case, getters
can take an optional ancestor
attribute that specifies which level of outer query to
access. If omitted, the ancestor attribute
takes the value 1 when used in a sql:query or
sql:update, which means the first outer query;
it defaults to the value 0 when used in a
sql:row-results, which means the query at the
current level.
3.13 Grouping
While nested queries have their uses, in the example above 3 queries have to be
written and no less than 7 queries are executed to produce the final result. It can
be elegantly rewritten using the sql:group and sql:member
elements. sql:group has to be the first element under a
sql:row-results or sql:member element. It takes a
mandatory column attribute that specifies the name of the column on
which grouping is done.
For every group, a header is output only once. Then, the content under the
sql:member element is output for each row. Finally, the footer is
output. The header and the footer can access columns There is no limit in
XML-SQL as to how deep grouping can be done.
The code below generates with a single SQL query the same results as the
example above:
<sql:config xmlns:sql="
http://orbeon.org/oxf/xml/sql"
> <results> <sql:connection> <sql:datasource>my-datasource</sql:datasource>
<sql:execute> <sql:query>select level1.value v1, level2.value v2, level3.value v3 from level1, level2, level3 where level1.level1_id = level2.level1_id
and level2.level2_id = level3.level2_id order by level1.value, level2.value, level3.value
</sql:query> <sql:results> <sql:row-results> <sql:group column="v1"> <group1> <group1-header> <level1-value>
<sql:get-column type="xs:string" column="v1"/>
</level1-value> </group1-header> <group1-members> <sql:member> <sql:group column="v2"> <group2> <group2-header> <level2-value>
<sql:get-column type="xs:string" column="v2"/>
</level2-value> </group2-header> <group2-members> <sql:member> <level3-value>
<sql:get-column type="xs:string" column="v3"/>
</level3-value> </sql:member> </group2-members> <group2-footer> <level2-value>
<sql:get-column type="xs:string" column="v2"/>
</level2-value> </group2-footer> </group2> </sql:group> </sql:member> </group1-members> <group1-footer> <level1-value>
<sql:get-column type="xs:string" column="v1"/>
</level1-value> </group1-footer> </group1> </sql:group> </sql:row-results> </sql:results> </sql:execute> </sql:connection> </results> </sql:config>
Note that correct ordering of the rows in the SQL query is
important because headers and footers are output when the
columns on which grouping is done change, in the order
returned by the result set.
3.14 Text and Whitespace Handling
Like in XSLT 1.0, text nodes in the XML-SQL document containing
only whitespace characters are stripped. Text nodes that contain
at least one non-whitespace character are not stripped and copied
to the output.
To better control the output of text, the sql:text element is
provided. It is similar to the xsl:text element.
xsl:text encapsulate text that is output as is. In particular, it
can encapsulate all whitespace characters.
4. Transactions
PresentationServer executes each HTTP request in its own transaction. If a
request fails for any reason, the SQL Processor rolls back the
transaction. The transaction is committed only when the pipeline
execution in complete.