Virtuoso compiles XPath and XQuery expressions as early as it is possible. E.g. if the first argument of xquery_eval() is a string constant then the SQL compiler will invoke the XQuery compiler to avoid on-demand compilation(s) of this text.

This feature significantly enhances performance of XQuery expressions embedded in SQL. For a simple search on XML document of average size the compilation time can be three times greater than execution time. In addition, the use of sql:column() special XQuery function is possible only when pre-compilation can be done by SQL compiler.

Pre-compilation is impossible if the text of the expression is not a constant. The typical case is passing an XQuery expression as parameter to a function. In this case the expression is compiled during the call of xquery_eval() and stored for future use. If the same string is passed again to the same invocation of xquery_eval() then a stored compiled expression is used.

Only partial pre-compilation is possible if XQuery expression refers to not-yet defined extension functions or to external resources. Partial pre-compilation gives little gain in speed, but it allows the use of sql:column()

The most important fact about pre-compilation is that passing parameters into XQuery statement is much more efficient than printing then into the text of the query. This is similar to SQL queries.

Example 15.22. Good and Poor Coding Practices

GOOD The expression is compiled once when SQL query is compiled:

select xquery_eval('count(//abstract)', SOURCE_XML) from LIB..ARTICLES;

GOOD The expression is compiled once when SQL query is compiled:

select xquery_eval('count(//article[@id=$main_id]/abstract)', SOURCE_XML, 1, vector('main_id', MAIN_ID))
  from LIB..ARTICLES;

POOR The expression is compiled once per data row. In addition, a hard-to-find error will occur if a value of MAIN_ID may contain double quote or a backslash character.

select xquery_eval(sprintf('count(//article[@id="%s"]/abstract)', MAIN_ID), SOURCE_XML)
  from LIB..ARTICLES;

GOOD The XQuery expression is compiled once per execution of the SQL query. The SQL compiler pays special attention to queries that import external resources, because the content and availability of these resources may differ from call to call. In addition, importing an external resource is usually not possible during SQL compilation due to deadlock danger, so the compilation is postponed until run time, but this is not too bad anyway. Even in this sophisticated case, XQuery can contain calls of sql:column() .

select xquery_eval('
    namespace tools="http://www.example.com/lib/tools/"
    import define "http://www.example.com/lib/tools/common.xqr"
    tools:extract-keywords(//abstract)',
  SOURCE_XML)
from LIB..ARTICLES;

GOOD Two XQuery expressions are compiled during SQL compilation.

select
  case
    when SOURCE_IS_DOCBOOK then xquery_eval ('//formalpara[title="See Also"]/para', SOURCE_XML)
    else xquery_eval ('//p[@style="seealso"]', SOURCE_XML)
  end
from LIB..ARTICLES;

POOR Virtuoso can not pre-compile XQquery expressions. Moreover, only one precompiled expression is cached per occurrence of xquery_eval() in the SQL statement so it is possible that an XQuery compiler will start once per data row.

select
  xquery_eval (
    case
      when SOURCE_IS_DOCBOOK then '//formalpara[title="See Also"]/para'
      else '//p[@style="seealso"]'
    end,
  SOURCE_XML)
from LIB..ARTICLES;