Previous Up Next

Chapter 6  Application Programming Interfaces (APIS)

The quickest way to learn how to use the APIs is as follows:

  1. Read Section 6.1 “Galax API Support”.
  2. Read Section 6.2 “Quick Start to the Galax APIs”.
  3. Read the example programs in the galax/examples/ directory while reading Section 6.2.

Every Galax API has functions for:

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:

galax.cma
Byte code
galax.cmxa
Native code

C libraries in $GALAXHOME/lib/c:

libgalaxopt.a
Native code, statically linked
libgalaxopt.so
Native code, dynamically linked

Java libraries 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 xs:untypedAtomic.

The Galax APIs provide constructors for the following data model values:

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 *);

6.3.3  Accessors

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):

  1. Create default processing context:

    let proc_ctxt = default_processing_context() in

  2. Load Galax’s standard library:

    let mod_ctxt = load_standard_library(proc_ctxt) in

  3. (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

  4. (Optionally) load one main XQuery module:

    let (mod_ctxt, stmts) = import_main_module mod_ctxt (File_Input "some-main-module.xq") in

  5. (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

  6. 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. **

  7. 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 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 :

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 NodeItem
xsAnyURIAttribute 
xsBooleanComment
xsDecimalElement
xsDoubleProcessingInstruction
xsFloatText
xsInt
xsInteger
xsQName
xsString
xsUntyped

There is one class for each kind of sequence:

There is one class for each kind of context used by Galax:

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  Operation-System Notes

Currently, Galax is not re-entrant, which means multi-threaded applications cannot create multiple, independent instances of the Galax query engine to evaluate queries.

6.7.1  Windows

The C API library libgalaxopt.a,so does not link properly under MinGW. A user reported that if you have the source distribution, you can link directly with the object files in galapi/c_api/*.o and adding the library -lasmrun on the command line works.


Previous Up Next