Chapter 6 Application Programming Interfaces (APIS)
The quickest way to learn how to use the APIs is as follows:
-
Read Section 6.1 ``Galax API Support''.
- Read Section 6.2 ``Quick Start to the Galax APIs''.
- Read the example programs in the galax/examples/ directory
while reading Section 6.2.
Every Galax API has functions for:
-
Converting values in the XQuery data model to/from values in
the native programming language (O'Caml, C or Java);
- Accessing values in XQuery data model from the native
programming language;
- Loading XML documents into the XQuery data model;
- Creating and modifying the query evaluation environment (also
known as the dynamic context);
- Evaluating queries given a dynamic context; and
- Serializing XQuery data model values as XML documents.
This chapter describes how to use each kind of functions.
6.1 Galax API Functionality
Galax currently supports application-program interfaces for the
O'Caml, C, and Java programming languages.
All APIs support the same set of functions; only their names differ
in each language API. This file describes the API functions. The
interfaces for each language are defined in:
-
O'Caml
- $GALAXHOME/lib/caml/galax.mli
- C
- $GALAXHOME/lib/c/{galax,galax_util,galax_types,itemlist}.h
- Java
- $GALAXHOME/lib/java/doc/*.html
If you use the C API, see Section 6.5.1 ``Memory Management in C API''.
Example programs that use these APIs are in:
-
O'Caml
- $GALAXHOME/examples/caml_api
- C
- $GALAXHOME/examples/c_api
- Java
- $GALAXHOME/examples/java_api
To try out the API programs, edit examples/Makefile.config to set up your environment, then
execute: cd $GALAXHOME/examples; make all.
This will compile and run the examples. Each directory contains a "test"
program that exercises every function in the API and an "example"
programs that illustrates some simple uses of the API.
The Galax query engine is implemented in O'Caml. This means that
values in the native language (C or Java) are converted into
values in the XQuery data model (which are represented are by O'Caml
objects) before sending them to the Galax engine. The APIs provide
functions for converting between native-language values and XQuery
data-model values.
6.1.1 Linking and Running
There are two kinds of Galax libraries: byte code and native code.
The C and Java libraries require native code libraries, and Java
requires dynamically linked libraries. Here are the libraries:
O'Caml libraries in $GALAXHOME/lib/caml:
-
glx.cma
- Byte code
- glx.cmxa
- Native code
C libraries in $GALAXHOME/lib/c:
-
libgalaxopt.a
- Native code, statically linked
- libgalaxopt.so
- Native code, dynamically linked
Java libraies in $GALAXHOME/lib/java:
-
libglxoptj.so
- Native code, dynamically linked
Note that Java applications MUST link with a dynamically linked
library and that C applications MAY link with a dynamically linked
library.
For Linux users, set LD_LIBRARY_PATH to $GALAXHOME/lib/c:$GALAXHOME/lib/java.
The Makefiles in examples/c_api and examples/java_api show how to
compile, link, and run applications that use the C and Java APIs.
6.2 Quick Start to using the APIs
The simplest API functions allow you to evaluate an XQuery statement
in a string. If the statement is an update, these functions return
the empty list, otherwise if the statement is an Xquery expression,
these functions return a list of XML values.
The example programs in
$(GALAXHOME)/examples/caml_api/example.ml,
$(GALAXHOME)/examples/c_api/example.c,
$(GALAXHOME)/examples/java_api/Example.java
illustrate how to use these query evaluation functions.
Galax accepts input (documents and queries) from files, string buffers, channels and HTTP, and
emits output (XML values) in files, string buffers, channels, and formatters. See
$(GALAXHOME)/lib/caml/galax_io.mli.
All the evaluation functions require a processing context. The
default processing context is constructed by calling the function Processing_context.default_processing_context():
val default_processing_context : unit -> processing_context
There are three ways to evaluate an XQuery statement:
val eval_statement_with_context_item :
Processing_context.processing_context -> Galax_io.input_spec ->
Galax_io.input_spec -> item list
Bind the context item (the XPath "." expression) to the XML
document in the resource named by the second argument, and
evaluate the XQuery statement in the third argument.
val eval_statement_with_context_item_as_xml :
Processing_context.processing_context -> item ->
Galax_io.input_spec -> item list
Bind the context item (the XPath "." expression) to the XML value
in the second argument and evaluate the XQuery statement in
the third argument.
val eval_statement_with_variables_as_xml :
Processing_context.processing_context ->
(string * item list) list ->
Galax_io.input_spec -> item list
The second argument is a list of variable name and XML value pairs.
Bind each variable to the corresponding XML value and evaluate the
XQuery statement in the third argument.
Sometimes you need more control over query evaluation, because, for
example, you want to load XQuery libraries and/or main modules and
evaluate statements incrementally. The following two sections
describe the API functions that provide finer-grained control.
6.3 XQuery Data Model
6.3.1 Types and Constructors
In the XQuery data model, a value is a sequence (or list) of
items. An item is either an node or an atomic value. An
node is an element, attribute, text, comment, or
processing-instruction. An atomic value is one of the nineteen
XML Schema data types plus the XQuery type xdt:untypedAtomic.
The Galax APIs provide constructors for the following data model
values:
-
lists/sequences of items
- element, attribute, text, comment, and processing instruction
nodes
- xs:string, xs:boolean, xs:int, xs:integer, xs:decimal, xs:float,
xs:double, xs:anyURI, xs:QName, xs:dateTime, xs:date, xs:time,
xdt:yearMonthDuration, xdt:dayTimeDuration, and xdt:untypedAtomic.
Atomic values
The constructor functions for atomic values take values in the
native language and return atomic values in the XQuery data
model. For example, the O'Caml constructor:
val atomicFloat : float -> atomicFloat
takes an O'Caml float value (as defined in the Datatypes module) and
returns a float in the XQuery data model. Similarly, the C
constructor:
extern galax_err galax_atomicDecimal(int i, atomicDecimal *decimal);
takes a C integer value and returns a decimal in the XQuery data
model.
Nodes
The constructor functions for nodes typically take other data model
values as arguments. For example, the O'Caml constructor for
elements:
val elementNode : atomicQName * attribute list * node list * atomicQName -> element
takes a QName value, a list of attribute nodes, a list of children
nodes, and the QName of the element's type. Simliarly, the C
constructor for text nodes takes an XQuery string value:
extern galax_err galax_textNode(atomicString str, text *);
Sequences
The constructor functions for sequences are language specific. In
O'Caml, the sequence constructor is simply the O'Caml list
constructor. In C, the sequence constructor is defined in
galapi/itemlist.h as:
extern itemlist itemlist_cons(item i, itemlist cdr);
6.3.2 Using XQuery data model values
The APIs are written in an "object-oriented" style, meaning that any
use of a type in a function signature denotes any value of that type
or a value derived from that type. For example, the function
Dm_functions.string_of_atomicvalue takes any atomic value (i.e., xs_string,
xs_boolean, xs_int, xs_float, etc.) and returns an O'Caml string
value:
val string_of_atomicValue : atomicValue -> string
Similarly, the function galax_parent in the C API takes any node value (i.e., an
element, attribute, text, comment, or processing instruction node)
and returns a list of nodes:
extern galax_err galax_parent(node n, node_list *);
The accessor functions take XQuery values and return constituent
parts of the value. For example, the children accessor takes an
element node and returns the sequence of children nodes contained in
that element:
val children : node -> node list (* O'Caml *)
extern galax_err galax_children(node n, node_list *); /* C */
The XQuery data model accessors are described in detail in
http://www.w3c.org/TR/query-datamodel.
6.3.4 Loading documents
Galax provides the load_document function for loading documents.
The load_document function takes the name of an XML file in the
local file system and returns a sequence of nodes that are the
top-level nodes in the document (this may include zero or more
comments and processing instructions and zero or one element node.)
val load_document : Processing_context.processing_context ->
Galax_io.input_spec -> node list (* O'Caml *)
extern galax_err galax_load_document(char* filename, node_list *);
extern galax_err galax_load_document_from_string(char* string, node_list *);
6.4 Query Evaluation
The general model for evaluating an XQuery expression or statement
proceeds as follows (each function is described in detail below):
-
Create default processing context:
let proc_ctxt = default_processing_context() in
- Load Galax's standard library:
let mod_ctxt = load_standard_library(proc_ctxt) in
- (Optionally) load any imported library modules:
let library_input = File_Input "some-xquery-library.xq" in
let mod_ctxt = import_library_module pc mod_ctxt library_input in
- (Optionally) load one main XQuery module:
let (mod_ctxt, stmts) = import_main_module mod_ctxt (File_Input "some-main-module.xq") in
- (Optionally) initialize the context item and/or global variables
defined in application (i.e., external environment):
let ext_ctxt = build_external_context proc_ctxt opt_context_item var_value_list in
let mod_ctxt = add_external_context mod_ctxt ext_ctxt in
- Evaluate all global variables in module context:
let mod_ctxt = eval_global_variables mod_ctxt
** NB: This step is necessary if the module contains *any*
global variables, whether defined in the XQuery module or
defined externally by the application. **
- Finally, evaluate a statement from the main module or one defined
in the application or call some XQuery function defined in the
module context:
let result = eval_statement proc_ctxt mod_ctxt stmt in
let result = eval_statement_from_io proc_ctxt mod_ctxt
(Buffer_Input some-XQuery-statement) in
let result = eval_query_function proc_ctxt mod_ctxt
"some-function" argument-values in
6.4.1 Module context
Every query is evaluated in a module context, which includes:
-
the built-in types, namespaces, and functions;
- the user-defined types, namespaces, and functions specified in
any imported library modules; and
- any additional context defined by the application (e.g., the values of
the context item and any global variables).
The functions for creating a module context include:
val default_processing_context : unit -> processing_context
The default processing context, which just contains flags for
controlling debugging, printing, and the processing phases. You
can change the default processing context yourself if you want
to print out debugging info.
val load_standard_library : processing_context -> module_context
Load the standard Galax library, which contains the built-in
types, namespaces, and functions.
val import_library_module : processing_context ->
module_context -> input_spec -> module_context
If you need to import other library modules, this function
returns the module_context argument extended with the module
in the second argument.
val import_main_module : processing_context ->
module_context -> input_spec ->
module_context * (Xquery_ast.cstatement list)
If you want to import a main module defined in a file, this
function returns the module_context argument extended with the
main module in the second argument and a list of
statements to evaluate.
The functions for creating an external context (context item and
global variable values):
val build_external_context : processing_context -> (item option) ->
(atomicDayTimeDuration option) -> (string * item list) list -> external_context
The external context includes an optional value for the context
item (known as "."), the (optional) local timezone, and a list of
variable name, item-list value pairs.
val add_external_context : module_context -> external_context -> module_context
This function extends the given module context with the external context.
val eval_global_variables : processing_context -> xquery_module -> xquery_module
This function evaluates the expressions for all (possibly
mutually dependent) global variables. It must be called before
calling the eval_* functions otherwise you will get an
"Undefined variable" error at evaluation time.
Analogous functions are defined in the C and Java APIs.
6.4.2 Evaluating queries/expressions
The APIs support three functions for evaluating a query:
eval_statement_from_io, eval_statement, and eval_query_function.
Note: If the module context contains (possibly mutually
dependent) global variables, the function eval_global_variables must be called before
calling the eval_* functions otherwise you will get an
"Undefined variable" error at evaluation time.
val eval_statement_from_io : processing_context -> xquery_module -> Galax_io.input_spec -> item list
Given the module context, evaluates the XQuery statement in
the third argument. If the statement is an XQuery expression,
returns Some (item list); otherwise if the statement is an
XQuery update, returns None (because update statements have
side effects on the data model store, but do not return values).
val eval_statement : processing_context -> xquery_module -> xquery_statement -> item list
Given the module context, evaluates the XQuery statement
val eval_query_function : processing_context -> xquery_module -> string -> item list list -> item list
Given the module context, evaluates the function with name in the
string argument applied to the list of item-list arguments.
Note: Each actual function argument is bound to one item list.
Analogous functions are defined in the C and Java APIs.
6.4.3 Serializing XQuery data model values
Once an application program has a handle on the result of evaluating
a query, it can either use the accessor functions in the API or it
can serialize the result value into an XML document. There are
three serialization functions: serialize_to_string,
serialize_to_output_channel and serialize_to_file.
val serialize : processing_context -> Galax_io.output_spec -> item list -> unit
Serialize an XML value to the given galax output.
val serialize_to_string : processing_context -> item list -> string
Serializes an XML value to a string.
Analogous functions are defined in the C and Java APIs.
6.5 C API Specifics
6.5.1 Memory Management
The Galax query engine is implemented in O'Caml. This means that
values in the native language (C or Java) are converted into
values in the XQuery data model (which represented are by O'Caml
objects) before sending them to the Galax engine. Similarly, the
values returned from the Galax engine are also O'Caml values -- the
native language values are "opaque handles" to the O'Caml values.
All O'Caml values live in the O'Caml memory heap and are therefore
managed by the O'Caml garbage collector. The C API guarantees that
any items returned from Galax to a C application will not be
de-allocated by the O'Caml garbage collector, unless the C
appliation explicitly frees those items, indicating that they are no
longer accessible in the C appliation. The C API provides two
functions in galapi/itemlist.h for freeing XQuery item values:
extern void item_free(item i);
Frees one XQuery item value.
extern void itemlist_free(itemlist il);
Frees every XQuery item value in the given item list.
6.5.2 Exceptions
The Galax query engine may raise an exception in O'Caml, which must
be conveyed to the C application. Every function in the C API
returns an integer error value :
-
0 if no exception was raised or
- -1 if an exception was raised.
The global variable galax_error_string contains the string value of
the exception raised in Galax. In future APIs, we will provide a
better mapping between error codes and Galax exceptions
6.6 Java API Specifics
6.6.1 General Info
The Galax query engine is implemented in O'Caml. This means that
values in the native language (C or Java) are converted into values
in the XQuery data model (which represented are by O'Caml objects)
before sending them to the Galax engine.
The Java API uses JNI to call the C API, which in turn calls the
O'Caml API (it's not as horrible as it sounds).
There is one class for each of the built-in XML Schema types
supported by Galax and one class for each kind of node:
Atomic |
Node |
Item |
xsAnyURI |
Attribute |
|
xsBoolean |
Comment |
xsDecimal |
Element |
xsDouble |
ProcessingInstruction |
xsFloat |
Text |
xsInt |
xsInteger |
xsQName |
xsString |
xsUntyped |
There is one class for each kind of sequence:
-
ItemList
- AtomicList
- NodeList
- AttributeList
There is one class for each kind of context used by Galax:
-
ExternalContext
- ModuleContext
- ProcessingContext
- QueryContext
Finally, the procedures for loading documents, constructing new
contexts and running queries are in the Galax class.
6.6.2 Exceptions
All Galax Java API functions can raise the exception class
GalapiException, which must be handled by the Java application.
6.6.3 Memory Management
All Java-C-O'Caml memory management is handled automatically in the
Java API.
6.7 Caveats
Currently, Galax is not re-entrant, which means multi-threaded
applications cannot create multiple, independent instances of the
Galax query engine to evaluate queries.