Address #56 - Builtin Saxon functions: being adding 9.8 function signature additions.

xquery version "1.0";
(:~
 : Saxon extension functions
 : @see http://www.saxonica.com/html/documentation/functions/saxon
 :
 :)
module namespace saxon = "http://saxon.sf.net/";

declare namespace jt = "http://saxon.sf.net/java-type";
declare namespace xsl = "http://www.w3.org/1999/XSL/Transform";

declare namespace a = "http://reecedunn.co.uk/xquery/annotations";
declare namespace o = "http://reecedunn.co.uk/xquery/options";

declare %a:since("saxon/pe", "9.2") %a:since("saxon/ee", "9.2") function saxon:adjust-to-civil-time($in as xs:dateTime, $tz as xs:string) as xs:dateTime? external; (: reqs PE or EE :)
declare %a:since("saxon/pe", "9.8") %a:since("saxon/ee", "9.8") function saxon:array-member($value as item()*) as jt:com.saxonica.functions.extfn.ArrayMemberValue external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:base64Binary-to-octets($input as xs:base64Binary) as xs:integer* external;
declare %a:since("saxon/pe", "8.5") %a:since("saxon/ee", "8.5") function saxon:base64Binary-to-string($input as xs:base64Binary?, $encoding as xs:string) as xs:string external;
declare %a:since("saxon/pe", "9.1") %a:since("saxon/ee", "9.1") function saxon:column-number() as xs:integer external;
declare %a:since("saxon/pe", "9.1") %a:since("saxon/ee", "9.1") function saxon:column-number($node as node()) as xs:integer external;
declare %a:since("saxon/pe", "9.0") %a:since("saxon/ee", "9.0") function saxon:compile-query($query as xs:string) as jt:net.sf.saxon.query.XQueryExpression external;
declare %a:since("saxon/pe", "8.5") %a:since("saxon/ee", "8.5") function saxon:compile-stylesheet($stylesheet as document-node()) as jt.net.sf.saxon.PreparedStylesheet external;
declare %a:since("saxon/pe", "9.3") %a:since("saxon/ee", "9.3") function saxon:current-mode-name() as xs:QName? external; (: @TODO the documentation claims that this was designed for XSLT :)
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:decimal-divide($arg1 as xs:decimal?, $arg2 as xs:decimal, $precision as xs:integer) as xs:decimal? external;
declare %a:since("saxon/pe", "8.6.1") %a:since("saxon/ee", "8.6.1") function saxon:deep-equal($arg1 as item()*, $arg2 as item()*, $collation as xs:string?, $flags as xs:string) as xs:boolean external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:discard-document($doc as document-node()) as document-node() external;
declare %a:since("saxon/pe", "7.2") %a:since("saxon/ee", "7.2") function saxon:eval($stored-expression as jt:net.sf.saxon.functions.Evaluate-PreparedExpression, $param1 as item()*, $param2 as item()*, $param3 as item()*) as xs:double external;
declare %a:since("saxon/pe", "7.2") %a:since("saxon/ee", "7.2") function saxon:evaluate($xpath as xs:string, $param1 as item()*, $param2 as item()*, $param3 as item()*) as item()* external;
declare %a:since("saxon/pe", "8.2") %a:since("saxon/ee", "8.2") function saxon:evaluate-node($node as node()) as item()* external;
declare %a:since("saxon/pe", "7.2") %a:since("saxon/ee", "7.2") function saxon:expression($string as xs:string) as jt:net.sf.saxon.functions.Evaluate-PreparedExpression external;
declare %a:since("saxon/pe", "7.2") %a:since("saxon/ee", "7.2") function saxon:expression($string as xs:string, $ns as element()) as jt:net.sf.saxon.functions.Evaluate-PreparedExpression external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:get-pseudo-attribute($att as xs:string) as xs:string? external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:has-same-nodes($arg1 as node()*, $arg2 as node()*) as xs:boolean external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:hexBinary-to-octets($input as xs:hexBinary) as xs:integer* external;
declare %a:since("saxon/pe", "8.5") %a:since("saxon/ee", "8.5") function saxon:hexBinary-to-string($input as xs:hexBinary, $encoding as xs:string) as xs:string external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:highest($input as item()*) as item()* external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:highest($input as item()*, $key as function(*) (: $key [8.1] as jt:net.sf.saxon.functions.Evaluate-PreparedExpression [9.2] as function() [9.5] as function(*) :)) as item()* external;
declare %a:since("saxon/pe", "9.1") %a:since("saxon/ee", "9.1") function saxon:in-summer-time($date as xs:dateTime, $region as xs:string) as xs:boolean external;
declare %a:since("saxon/pe", "8.3") %a:since("saxon/ee", "8.3") %a:until("saxon/pe", "9.4") %a:until("saxon/ee", "9.4") function saxon:index($sequence as item()*, $expression as jt:net.sf.saxon.functions.Evaluate-PreparedExpression, $collation as xs:string) as jt:com.saxonica.expr.IndexedSequence external;
declare %a:since("saxon/pe", "9.5") %a:since("saxon/ee", "9.5") function saxon:index($sequence as item()*, $function as function(item()) as xs:anyAtomicType* (: [9.5] as xs:integer  :)) as map(*) (: [8.3] as jt:com.saxonica.expr.IndexedSequence [9.5] as xs:double [9.7] as map(*) :) external;
declare %a:since("saxon/ee", "9.7") function saxon:is-defaulted($node as node()) as xs:boolean external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:is-whole-number($arg as xs:numeric?) as xs:boolean external;
declare %a:since("saxon/pe", "9.0") %a:since("saxon/ee", "9.0") function saxon:last-modified($uri as xs:string? (: [9.0] () as document-uri($node)) [9.2] $uri as xs:string? :)) as xs:dateTime? external;
declare %a:since("saxon/pe", "9.0") %a:since("saxon/ee", "9.0") %a:until("saxon/pe", "9.1") %a:until("saxon/ee", "9.1") function saxon:last-modified($node as node()) as xs:dateTime? external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:leading($input as item()*) as item() external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:leading($input as item()*, $test as function(*) (: [8.0] as jt:net.sf.saxon.functions.Evaluate-PreparedExpression [9.5] as function(*) :)) as item() external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:line-number() as xs:integer external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:line-number($node as node()) as xs:integer external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.1") function saxon:lowest($input as item()*) as item()* external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.1") function saxon:lowest($input as item()*, $key as function(*) (: $key [8.0] as jt:net.sf.saxon.functions.Evaluate-PreparedExpression [9.2] as function() [9.5] as function(*) :)) as item()* external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") %a:deprecated("saxon/pe", "9.2") %a:deprecated("saxon/ee", "9.2") function saxon:namespace-node($prefix as xs:string, $uri as xs:string) as namespace-node() external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:octets-to-base64Binary($octets as xs:integer*) as xs:base64Binary external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:octets-to-hexBinary($octets as xs:integer*) as xs:hexBinary external;
declare %a:since("saxon/pe", "7.1") %a:since("saxon/ee", "7.1") %a:deprecated("saxon/pe", "9.6", "fn:parse-xml") %a:deprecated("saxon/ee", "9.6", "fn:parse-xml") function saxon:parse($xml as xs:string) as document-node() external;
declare %a:since("saxon/pe", "9.2") %a:since("saxon/ee", "9.2") function saxon:parse-html($html as xs:string) as document-node() external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") %a:deprecated("saxon/pe", "9.6", "fn:parse-xml") %a:deprecated("saxon/ee", "9.6", "fn:path") function saxon:path() as xs:string external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") %a:deprecated("saxon/pe", "9.6", "fn:parse-xml") %a:deprecated("saxon/ee", "9.6", "fn:path") function saxon:path($node as node()) as xs:string external;
declare %a:since("saxon/pe", "9.1") %a:since("saxon/ee", "9.1") function saxon:print-stack() as xs:string external;
declare %a:since("saxon/pe", "9.1") %a:since("saxon/ee", "9.1") function saxon:query($name as jt:net.sf.saxon.query.XQueryExpression?) as item()* external;
declare %a:since("saxon/pe", "9.1") %a:since("saxon/ee", "9.1") function saxon:query($name as jt:net.sf.saxon.query.XQueryExpression?, $context-item as item()?) as item()* external;
declare %a:since("saxon/pe", "9.1") %a:since("saxon/ee", "9.1") function saxon:query($name as jt:net.sf.saxon.query.XQueryExpression?, $context-item as item()?, $params as node()*) as item()* external;
declare %a:since("saxon/pe", "9.7") %a:since("saxon/ee", "9.7") function saxon:read-binary-resource($uri as xs:string) as xs:base64Binary external;
declare %a:since("saxon/ee", "9.5") function saxon:schema() as function(*)? external;
declare %a:since("saxon/ee", "9.5") function saxon:schema($kind as xs:string, $name as xs:QName) as function(*)? external;
declare %a:since("saxon/pe", "9.5") %a:since("saxon/ee", "9.5") function saxon:send-mail($emailConfig as map(*), $subject as xs:string, $content as item()) as xs:boolean external;
declare %a:since("saxon/pe", "9.5") %a:since("saxon/ee", "9.5") function saxon:send-mail($emailConfig as map(*), $subject as xs:string, $content as item(), $attachment as item()*) as xs:boolean external;
declare %a:since("saxon/pe", "7.1") %a:since("saxon/ee", "7.1") %a:deprecated("saxon/pe", "9.6", "fn:serialize") %a:deprecated("saxon/ee", "9.6", "fn:serialize") function saxon:serialize($node as node(), $format as xs:string) as xs:string external;
declare %a:since("saxon/pe", "7.1") %a:since("saxon/ee", "7.1") %a:deprecated("saxon/pe", "9.6", "fn:serialize") %a:deprecated("saxon/ee", "9.6", "fn:serialize") function saxon:serialize($node as node(), $format as element(xsl:output)) as xs:string external;
declare %a:since("saxon/pe", "8.9") %a:since("saxon/ee", "8.9") %a:deprecated("saxon/pe", "9.6", "fn:sort") %a:deprecated("saxon/ee", "9.6", "fn:sort") function saxon:sort($seq as item()*) as item()* external;
declare %a:since("saxon/pe", "7.1") %a:since("saxon/ee", "7.1") %a:deprecated("saxon/pe", "9.6", "fn:sort") %a:deprecated("saxon/ee", "9.6", "fn:sort") function saxon:sort($seq as item()*, $sort-key as function(item()) as xs:anyAtomicType) as item()* external;
declare %a:since("saxon/ee", "8.0") function saxon:stream($input as item()*) as item()* external;
declare %a:since("saxon/pe", "8.4") %a:since("saxon/ee", "8.4") function saxon:string-to-base64Binary($in as xs:string?, $encoding as xs:string) as xs:base64Binary? external;
declare %a:since("saxon/pe", "8.4") %a:since("saxon/ee", "8.4") function saxon:string-to-hexBinary($in as xs:string?, $encoding as xs:string) as xs:hexBinary? external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:string-to-utf8($name as xs:string) as xs:integer* external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:system-id() as xs:string external;
declare %a:since("saxon/pe", "8.5") %a:since("saxon/ee", "8.5") function saxon:transform($stylesheet as jt:net.sf.saxon.PreparedStylesheet, $source as node()) as document-node() external;
declare %a:since("saxon/pe", "8.5") %a:since("saxon/ee", "8.5") function saxon:transform($stylesheet as jt:net.sf.saxon.PreparedStylesheet, $source as node(), $params as item()*) as document-node() external;
declare %a:since("saxon/pe", "9.5") %a:since("saxon/ee", "9.5") function saxon:type($item as item()?) as function(*)? external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:type-annotation($item as item()?) as xs:QName? external;
declare %a:since("saxon/pe", "9.1") %a:since("saxon/ee", "9.1") function saxon:unparsed-entities($doc as document-node()) as xs:string* external;
declare %a:since("saxon/ee", "9.5") function saxon:validate($node as node()?) as map(xs:string, item()*)? external;
declare %a:since("saxon/ee", "9.5") function saxon:validate($node as node()?, $options as map(xs:string, item()*)) as map(xs:string, item()*)? external;
declare %a:since("saxon/ee", "9.5") function saxon:validate($node as node()?, $options as map(xs:string, item()*), $params as map(xs:string, item()*)) as map(xs:string, item()*)? external;

xquery version "1.0";
(:~
 : Saxon extension functions
 : @see http://www.saxonica.com/html/documentation/functions/saxon
 :
 :)
module namespace saxon = "http://saxon.sf.net/";

declare namespace jt = "http://saxon.sf.net/java-type";
declare namespace xsl = "http://www.w3.org/1999/XSL/Transform";

declare namespace a = "http://reecedunn.co.uk/xquery/annotations";
declare namespace o = "http://reecedunn.co.uk/xquery/options";

declare %a:since("saxon/pe", "9.2") %a:since("saxon/ee", "9.2") function saxon:adjust-to-civil-time($in as xs:dateTime, $tz as xs:string) as xs:dateTime? external; (: reqs PE or EE :)
declare %a:since("saxon/pe", "9.8") function saxon:array-member($value as item()*) as jt:com.saxonica.functions.extfn.ArrayMemberValue external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:base64Binary-to-octets($input as xs:base64Binary) as xs:integer* external;
declare %a:since("saxon/pe", "8.5") %a:since("saxon/ee", "8.5") function saxon:base64Binary-to-string($input as xs:base64Binary?, $encoding as xs:string) as xs:string external;
declare %a:since("saxon/pe", "9.1") %a:since("saxon/ee", "9.1") function saxon:column-number() as xs:integer external;
declare %a:since("saxon/pe", "9.1") %a:since("saxon/ee", "9.1") function saxon:column-number($node as node()) as xs:integer external;
declare %a:since("saxon/pe", "9.0") %a:since("saxon/ee", "9.0") function saxon:compile-query($query as xs:string) as jt:net.sf.saxon.query.XQueryExpression external;
declare %a:since("saxon/pe", "8.5") %a:since("saxon/ee", "8.5") function saxon:compile-stylesheet($stylesheet as document-node()) as jt.net.sf.saxon.PreparedStylesheet external;
declare %a:since("saxon/pe", "9.3") %a:since("saxon/ee", "9.3") function saxon:current-mode-name() as xs:QName? external; (: @TODO the documentation claims that this was designed for XSLT :)
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:decimal-divide($arg1 as xs:decimal?, $arg2 as xs:decimal, $precision as xs:integer) as xs:decimal? external;
declare %a:since("saxon/pe", "8.6.1") %a:since("saxon/ee", "8.6.1") function saxon:deep-equal($arg1 as item()*, $arg2 as item()*, $collation as xs:string?, $flags as xs:string) as xs:boolean external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:discard-document($doc as document-node()) as document-node() external;
declare %a:since("saxon/pe", "7.2") %a:since("saxon/ee", "7.2") function saxon:eval($stored-expression as jt:net.sf.saxon.functions.Evaluate-PreparedExpression, $param1 as item()*, $param2 as item()*, $param3 as item()*) as xs:double external;
declare %a:since("saxon/pe", "7.2") %a:since("saxon/ee", "7.2") function saxon:evaluate($xpath as xs:string, $param1 as item()*, $param2 as item()*, $param3 as item()*) as item()* external;
declare %a:since("saxon/pe", "8.2") %a:since("saxon/ee", "8.2") function saxon:evaluate-node($node as node()) as item()* external;
declare %a:since("saxon/pe", "7.2") %a:since("saxon/ee", "7.2") function saxon:expression($string as xs:string) as jt:net.sf.saxon.functions.Evaluate-PreparedExpression external;
declare %a:since("saxon/pe", "7.2") %a:since("saxon/ee", "7.2") function saxon:expression($string as xs:string, $ns as element()) as jt:net.sf.saxon.functions.Evaluate-PreparedExpression external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:get-pseudo-attribute($att as xs:string) as xs:string? external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:has-same-nodes($arg1 as node()*, $arg2 as node()*) as xs:boolean external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:hexBinary-to-octets($input as xs:hexBinary) as xs:integer* external;
declare %a:since("saxon/pe", "8.5") %a:since("saxon/ee", "8.5") function saxon:hexBinary-to-string($input as xs:hexBinary, $encoding as xs:string) as xs:string external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:highest($input as item()*) as item()* external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:highest($input as item()*, $key as function(*) (: $key [8.1] as jt:net.sf.saxon.functions.Evaluate-PreparedExpression [9.2] as function() [9.5] as function(*) :)) as item()* external;
declare %a:since("saxon/pe", "9.1") %a:since("saxon/ee", "9.1") function saxon:in-summer-time($date as xs:dateTime, $region as xs:string) as xs:boolean external;
declare %a:since("saxon/pe", "8.3") %a:since("saxon/ee", "8.3") %a:until("saxon/pe", "9.4") %a:until("saxon/ee", "9.4") function saxon:index($sequence as item()*, $expression as jt:net.sf.saxon.functions.Evaluate-PreparedExpression, $collation as xs:string) as jt:com.saxonica.expr.IndexedSequence external;
declare %a:since("saxon/pe", "9.5") %a:since("saxon/ee", "9.5") function saxon:index($sequence as item()*, $function as function(item()) as xs:anyAtomicType* (: [9.5] as xs:integer  :)) as map(*) (: [8.3] as jt:com.saxonica.expr.IndexedSequence [9.5] as xs:double [9.7] as map(*) :) external;
declare %a:since("saxon/ee", "9.7") function saxon:is-defaulted($node as node()) as xs:boolean external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:is-whole-number($arg as xs:numeric?) as xs:boolean external;
declare %a:since("saxon/pe", "9.0") %a:since("saxon/ee", "9.0") function saxon:last-modified($uri as xs:string? (: [9.0] () as document-uri($node)) [9.2] $uri as xs:string? :)) as xs:dateTime? external;
declare %a:since("saxon/pe", "9.0") %a:since("saxon/ee", "9.0") %a:until("saxon/pe", "9.1") %a:until("saxon/ee", "9.1") function saxon:last-modified($node as node()) as xs:dateTime? external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:leading($input as item()*) as item() external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") function saxon:leading($input as item()*, $test as function(*) (: [8.0] as jt:net.sf.saxon.functions.Evaluate-PreparedExpression [9.5] as function(*) :)) as item() external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:line-number() as xs:integer external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:line-number($node as node()) as xs:integer external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.1") function saxon:lowest($input as item()*) as item()* external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.1") function saxon:lowest($input as item()*, $key as function(*) (: $key [8.0] as jt:net.sf.saxon.functions.Evaluate-PreparedExpression [9.2] as function() [9.5] as function(*) :)) as item()* external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") %a:deprecated("saxon/pe", "9.2") %a:deprecated("saxon/ee", "9.2") function saxon:namespace-node($prefix as xs:string, $uri as xs:string) as namespace-node() external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:octets-to-base64Binary($octets as xs:integer*) as xs:base64Binary external;
declare %a:since("saxon/pe", "8.1") %a:since("saxon/ee", "8.1") function saxon:octets-to-hexBinary($octets as xs:integer*) as xs:hexBinary external;
declare %a:since("saxon/pe", "7.1") %a:since("saxon/ee", "7.1") %a:deprecated("saxon/pe", "9.6", "fn:parse-xml") %a:deprecated("saxon/ee", "9.6", "fn:parse-xml") function saxon:parse($xml as xs:string) as document-node() external;
declare %a:since("saxon/pe", "9.2") %a:since("saxon/ee", "9.2") function saxon:parse-html($html as xs:string) as document-node() external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") %a:deprecated("saxon/pe", "9.6", "fn:parse-xml") %a:deprecated("saxon/ee", "9.6", "fn:path") function saxon:path() as xs:string external;
declare %a:since("saxon/pe", "8.0") %a:since("saxon/ee", "8.0") %a:deprecated("saxon/pe", "9.6", "fn:parse-xml") %a:deprecated("saxon/ee", "9.6", "fn:path") function saxon:path($node as node()) as xs:string external;
declare %a:since("saxon/pe", "9.1") %a:since("saxon/ee", "9.1") function saxon:print-stack() as xs:string external;
declare %a:since("saxon/pe", "9.1") %a:since("saxon/ee", "9.1") function saxon:query($name as jt:net.sf.saxon.query.XQueryExpression?) as item()* external;
declare %a:since("saxon/pe", "9.1") %a:since("saxon/ee", "9.1") function saxon:query($name as jt:net.sf.saxon.query.XQueryExpression?, $context-item as item()?) as item()* external;
declare %a:since("saxon/pe", "9.1") %a:since("saxon/ee", "9.1") function saxon:query($name as jt:net.sf.saxon.query.XQueryExpression?, $context-item as item()?, $params as node()*) as item()* external;
declare %a:since("saxon/pe", "9.7") %a:since("saxon/ee", "9.7") function saxon:read-binary-resource($uri as xs:string) as xs:base64Binary external;
declare %a:since("saxon/ee", "9.5") function saxon:schema() as function(*)? external;
declare %a:since("saxon/ee", "9.5") function saxon:schema($kind as xs:string, $name as xs:QName) as function(*)? external;
declare %a:since("saxon/pe", "9.5") %a:since("saxon/ee", "9.5") function saxon:send-mail($emailConfig as map(*), $subject as xs:string, $content as item()) as xs:boolean external;
declare %a:since("saxon/pe", "9.5") %a:since("saxon/ee", "9.5") function saxon:send-mail($emailConfig as map(*), $subject as xs:string, $content as item(), $attachment as item()*) as xs:boolean external;
declare %a:since("saxon/pe", "7.1") %a:since("saxon/ee", "7.1") %a:deprecated("saxon/pe", "9.6", "fn:serialize") %a:deprecated("saxon/ee", "9.6", "fn:serialize") function saxon:serialize($node as node(), $format as xs:string) as xs:string external;
declare %a:since("saxon/pe", "7.1") %a:since("saxon/ee", "7,1") %a:deprecated("saxon/pe", "9.6", "fn:serialize") %a:deprecated("saxon/ee", "9.6", "fn:serialize") function saxon:serialize($node as node(), $format as element(xsl:output)) as xs:string external;


added skip function to the modules

added skip function to the modules

xquery version "3.0";

(:
 : Copyright 2006-2009 The FLWOR Foundation.
 :
 : Licensed under the Apache License, Version 2.0 (the "License");
 : you may not use this file except in compliance with the License.
 : You may obtain a copy of the License at
 :
 : http://www.apache.org/licenses/LICENSE-2.0
 :
 : Unless required by applicable law or agreed to in writing, software
 : distributed under the License is distributed on an "AS IS" BASIS,
 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 : See the License for the specific language governing permissions and
 : limitations under the License.
:)

(:~
 : This module defines a set of functions to probe and refresh indexes which are
 : declared in the prolog of a module.
 :
 :
This module is part of Zorba's XQuery Data
 : Definition Facility. All the indexes managed by this module
 : have to be pre-declared in the prolog of a library module. Please refer to the
 : general documentation
 : for more information and examples.

 :
 : @see Data Lifecycle
 : @see XQuery Data Definition Facility
 : @see http://www.zorba-xquery.com/modules/store/static/indexes/ddl
 : @see http://www.zorba-xquery.com/modules/store/static/collections/dml
 : @see http://www.zorba-xquery.com/modules/store/static/collections/ddl
 : @see http://www.zorba-xquery.com/modules/store/static/integrity_constraints/ddl
 : @see http://www.zorba-xquery.com/modules/store/static/integrity_constraints/dml
 : @see http://www.zorba-xquery.com/errors
 :
 : @author Zorba Team
 :
 : @project store/indexes/static
 :
 :)
module namespace idml = "http://www.zorba-xquery.com/modules/store/static/indexes/dml";

declare namespace zerr = "http://www.zorba-xquery.com/errors";
declare namespace an = "http://www.zorba-xquery.com/annotations";

declare namespace ver = "http://www.zorba-xquery.com/options/versioning";

declare option ver:module-version "2.0";

(:~
 :
The probe-index-point-value function retrieves from an index the domain
 : nodes associated by value equality with a given search tuple.
 : The search tuple consists of a number of search keys, where
 : each search key is either an atomic item or the empty sequence.  The number
 : of search keys given must be equal to the number of keys declared for the
 : index. Since the number of keys differs from one index to another, this
 : function is variadic.

 :
 :
The result of the function is either an error or the set of domain nodes for
 : which the following xquery expression returns true:

 :
 :

 : $key1 eq $node/keyExpr1 and ... and $keyM eq $node/keyExprM
 :

 : where keyExpri is the expression specified in the i-th keyspec
 : of the index.
 :
 : @param $name The QName of the index to probe
 : @param $key_i the search keys used to probe the index with. The i-th search
 :        key corresponds to the i-th key expression in the index declaration.
 : @return The set of domain nodes that satisfy the search condition.
 :
 : @error zerr:ZDDY0021 if the index with name $name is not declared.
 : @error zerr:ZDDY0023 if the index with name $name does not exist.
 : @error zerr:ZDDY0025 if the number of search keys passed as arguments is not
 :        the same as the number of keys declared for the index.
 : @error err:XPTY0004 if a non-empty seach key is given, whose type does not match
 :        the sequence type specified in the corresponding keyspec.
 :
 :)
declare %an:variadic function idml:probe-index-point-value(
  $name as xs:QName,
  $key_i as xs:anyAtomicType?) as node()*  external;

(:~
 :
The probe-index-point-value-skip function retrieves from an index the domain
 : nodes associated by value equality with a given search tuple.
 : The search tuple consists of a number of search keys, where
 : each search key is either an atomic item or the empty sequence.  The number
 : of search keys given must be equal to the number of keys declared for the
 : index. Since the number of keys differs from one index to another, this
 : function is variadic.

 :
 :
The result of the function is either an error or the set of domain nodes for
 : which the following xquery expression returns true:

 :
 :

 : $key1 eq $node/keyExpr1 and ... and $keyM eq $node/keyExprM
 :

 : where keyExpri is the expression specified in the i-th keyspec
 : of the index.
 :
 : @param $name The QName of the index to probe
 : @param $skip The number of index items to skip.
 : @param $key_i the search keys used to probe the index with. The i-th search
 :        key corresponds to the i-th key expression in the index declaration.
 : @return The set of domain nodes that satisfy the search condition.
 :
 : @error zerr:ZDDY0021 if the index with name $name is not declared.
 : @error zerr:ZDDY0023 if the index with name $name does not exist.
 : @error zerr:ZDDY0025 if the number of search keys passed as arguments is not
 :        the same as the number of keys declared for the index.
 : @error err:XPTY0004 if a non-empty seach key is given, whose type does not match
 :        the sequence type specified in the corresponding keyspec.
 :
 :)
declare %an:variadic function idml:probe-index-point-value(
  $name as xs:QName,
  $key_i as xs:anyAtomicType?) as node()*  external;

(:~
 :
The probe-index-point-general function retrieves from an index the domain
 : nodes associated by general equality with a given search sequence
 : . The search sequence consists of an arbitrary number of
 : search keys, where each search key is an atomic item. The function
 : is supported by general indexes only. Its result is either an error or the
 : set of domain nodes for which the following xquery expression returns true:

 :

 : $keys = $node/keyExpr
 :

 : where keyExpr is the expression specified in the keyspec of the index (remember
 : that for general indexes, there can be only one keyspec).
 :
 : @param $name The QName of the index to probe.
 : @param $key The search sequence.
 : @return The set of domain nodes that satisfy the search condition.
 :
 : @error zerr:ZDDY0021 if the index with name $name is not declared.
 : @error zerr:ZDDY0023 if the index with name $name does not exist.
 : @error zerr:ZDDY0029 if the index is not general.
 : @error err:XPTY0004  if the search sequence contains a search key, whose
 :        type does not match the sequence type specified in the keyspec of
 :        the index.
 :)
declare function idml:probe-index-point-general(
  $name as xs:QName,
  $key  as xs:anyAtomicType*) as node()*  external;

(:~
 :
The probe-index-range-value function retrieves the domain nodes associated
 : by value order-comparison (operators le, lt, ge, gt) with a given search
 : box. The search box is specified as a number M of rangespecs
 : , where each rangespec consists of six values. The number M must be
 : greater than 0 and less than or equal to the number N of keyspecs found in the
 : index declaration. If M is less than N, then the "missing" rangespecs are
 : assumed to have the following value: [(), (), false, false, false, false].
 : As a result, we can assume that M is equal to N (Remember that for general
 : indexes, there can be only one IndexKeySpec, and as a result, for general
 : indexes, M = N = 1). Since the number of keys differs from one index to
 : another, this function is variadic.

 :
 :
The ith rangespec corresponds to the ith keyspec, and
 : specifies a search condition on the key values that are produced by evaluating
 : that keyspec for every domain node. Specifically, we define the ith
 : rangespec result as the set of domain nodes for which the
 : following xquery expression returns true:

 :
 :

 : if ($haveLowerBound-i and $haveUpperBound-i) then
 :   $lowerBound-i lop $node/keyExpr-i and $node/keyExpr-i uop $upperBound-i
 : else if ($haveLowerBound-i) then
 :   $lowerBound-i lop $node/keyExpr-i
 : else if ($haveUpperBound-i) then
 :   $node/keyExpr-i uop $upperBound-i
 : else
 :   fn:true()
 :

 :
 :
where keyExpr-i is the expression specified by the ith keyspec
 : of the index, lop is either the le or the lt operator depending on whether
 : $lowerBoundsIncluded-i is true or false, and uop is either the le or the
 : lt operator depending on whether $upperBoundsIncluded-i is true or false.

 :
 :
The result of the probe-index-range-value function is either an error,
 : or the intersection of all the rangespec results.

 :
 : @param $name The QName of the index to probe
 : @param $lowerBound-i The lower bound in a range of key values.
 : @param $upperBound-i The upper bound in a range of key values.
 : @param $haveLowerBound-i If false, then there is no lower bound, or
 :        equivalently, the lower bound is -INFINITY. Otherwise, the lower
 :        bound is the one given by the lowerBound-i value.
 : @param $haveUpperBound-i If false, then there is no upper bound, or
 :        equivalently, the upper bound is +INFINITY. Otherwise, the upper
 :        bound is the one given by the upperBound-i value.
 : @param $lowerBoundIncluded-i If false, then the range is open from below,
 :        i.e., the lowerBound-i value is not considered part of the
 :        range. Otherwise, the range is closed from below, i.e., the
 :        lowerBound-i value is part of the range.
 : @param $upperBoundIncluded-i If false, then the range is open from above,
 :        i.e., the upperBound-i value is not considered part of the
 :        range. Otherwise, the range is closed from above, i.e., the
 :        upperBound-i value is part of the range.
 : @return The set of domain nodes that satisfy the search condition.
 :
 : @error zerr:ZDDY0021 if the index with name $name is not declared.
 : @error zerr:ZDDY0023 if the index with name $name does not exist.
 : @error zerr:ZDDY0025 if the number of rangespecs passed as arguments
 :        is zero or greater than the number of keys declared for the index.
 : @error zerr:ZDDY0026 if the index is not a range index.
 : @error err:XPTY0004  if $haveLowerBound-i is true and $lowerBound-i is an
 :        atomic item whose type does not match the sequence type specified
 :        by the ith keyspec, or $haveUpperBound-i is true and
 :        $upperBound-i is an atomic item whose type does not match the
 :        sequence type specified by the ith keyspec.
 : @error zerr:ZDDY0034 if (a) the index is general (in which case there is
 :        only one rangespac), (b) the index is untyped, (c) there is both a
 :        lower and an upper bound, and (d) if T1 and T2 are the types of the
 :        lower and upper bound, neither T1 is a subtype of T2 nor T2 is a
 :        subtype of T1.
 :)
declare %an:variadic function idml:probe-index-range-value(
  $name                 as xs:QName,
  $lowerBound-i         as xs:anyAtomicType?,
  $upperBound-i         as xs:anyAtomicType?,
  $haveLowerBound-i     as xs:boolean,
  $haveUpperBound-i     as xs:boolean,
  $lowerBoundIncluded-i as xs:boolean,
  $upperBoundIncluded-i as xs:boolean) as node()*  external;

(:~
 :
The probe-index-range-value-skip function retrieves the domain nodes associated
 : by value order-comparison (operators le, lt, ge, gt) with a given search
 : box. The search box is specified as a number M of rangespecs
 : , where each rangespec consists of six values. The number M must be
 : greater than 0 and less than or equal to the number N of keyspecs found in the
 : index declaration. If M is less than N, then the "missing" rangespecs are
 : assumed to have the following value: [(), (), false, false, false, false].
 : As a result, we can assume that M is equal to N (Remember that for general
 : indexes, there can be only one IndexKeySpec, and as a result, for general
 : indexes, M = N = 1). Since the number of keys differs from one index to
 : another, this function is variadic.

 :
 :
The ith rangespec corresponds to the ith keyspec, and
 : specifies a search condition on the key values that are produced by evaluating
 : that keyspec for every domain node. Specifically, we define the ith
 : rangespec result as the set of domain nodes for which the
 : following xquery expression returns true:

 :
 :

 : if ($haveLowerBound-i and $haveUpperBound-i) then
 :   $lowerBound-i lop $node/keyExpr-i and $node/keyExpr-i uop $upperBound-i
 : else if ($haveLowerBound-i) then
 :   $lowerBound-i lop $node/keyExpr-i
 : else if ($haveUpperBound-i) then
 :   $node/keyExpr-i uop $upperBound-i
 : else
 :   fn:true()
 :

 :
 :
where keyExpr-i is the expression specified by the ith keyspec
 : of the index, lop is either the le or the lt operator depending on whether
 : $lowerBoundsIncluded-i is true or false, and uop is either the le or the
 : lt operator depending on whether $upperBoundsIncluded-i is true or false.

 :
 :
The result of the probe-index-range-value function is either an error,
 : or the intersection of all the rangespec results.

 :
 : @param $name The QName of the index to probe
 : @param $skip The number of collection items to skip.
 : @param $lowerBound-i The lower bound in a range of key values.
 : @param $upperBound-i The upper bound in a range of key values.
 : @param $haveLowerBound-i If false, then there is no lower bound, or
 :        equivalently, the lower bound is -INFINITY. Otherwise, the lower
 :        bound is the one given by the lowerBound-i value.
 : @param $haveUpperBound-i If false, then there is no upper bound, or
 :        equivalently, the upper bound is +INFINITY. Otherwise, the upper
 :        bound is the one given by the upperBound-i value.
 : @param $lowerBoundIncluded-i If false, then the range is open from below,
 :        i.e., the lowerBound-i value is not considered part of the
 :        range. Otherwise, the range is closed from below, i.e., the
 :        lowerBound-i value is part of the range.
 : @param $upperBoundIncluded-i If false, then the range is open from above,
 :        i.e., the upperBound-i value is not considered part of the
 :        range. Otherwise, the range is closed from above, i.e., the
 :        upperBound-i value is part of the range.
 : @return The set of domain nodes that satisfy the search condition.
 :
 : @error zerr:ZDDY0021 if the index with name $name is not declared.
 : @error zerr:ZDDY0023 if the index with name $name does not exist.
 : @error zerr:ZDDY0025 if the number of rangespecs passed as arguments
 :        is zero or greater than the number of keys declared for the index.
 : @error zerr:ZDDY0026 if the index is not a range index.
 : @error err:XPTY0004  if $haveLowerBound-i is true and $lowerBound-i is an
 :        atomic item whose type does not match the sequence type specified
 :        by the ith keyspec, or $haveUpperBound-i is true and
 :        $upperBound-i is an atomic item whose type does not match the
 :        sequence type specified by the ith keyspec.
 : @error zerr:ZDDY0034 if (a) the index is general (in which case there is
 :        only one rangespac), (b) the index is untyped, (c) there is both a
 :        lower and an upper bound, and (d) if T1 and T2 are the types of the
 :        lower and upper bound, neither T1 is a subtype of T2 nor T2 is a
 :        subtype of T1.
 :)
declare %an:variadic function idml:probe-index-range-value(
  $name                 as xs:QName,
  $lowerBound-i         as xs:anyAtomicType?,
  $upperBound-i         as xs:anyAtomicType?,
  $haveLowerBound-i     as xs:boolean,
  $haveUpperBound-i     as xs:boolean,
  $lowerBoundIncluded-i as xs:boolean,
  $upperBoundIncluded-i as xs:boolean) as node()*  external;

(:~
 :
The probe-index-range-general function retrieves the domain nodes associated
 : by general order-comparison (operators <=, <, >=, >) with one or two
 : search sequences. Each search sequence consists of an
 : arbitrary number of search keys, where each search key is
 : an atomic item. This method is supported by general range indexes only. Its
 : result is either an error or the set of domain nodes for which the following
 : xquery expression returns true:

 :
 :

 : if ($haveLowerBound and $haveUpperBound) then
 :   $lowerBoundKeys lop $node/keyExpr and $node/keyExpr uop $upperBoundKeys
 : else if ($haveLowerBound) then
 :   $lowerBoundKeys lop $node/keyExpr
 : else if ($haveUpperBound) then
 :   $node/keyExpr uop $upperBoundKeys
 : else
 :   fn:true()
 :

 :
 :
where keyExpr is the expression specified in the keyspec of the index,
 : lop is either the <= or the < operator depending on whether
 : $lowerBoundsIncluded is true or false, and uop is either the <= or the
 : < operator depending on whether $upperBoundsIncluded is true or false
.
 :
 : @param $name The QName of the index to probe.
 : @param $lowerBound The lower bound search sequence.
 : @param $upperBound The upper bound search sequence.
 : @param $haveLowerBound Whether a lower bound search sequence exists or not.
 : @param $haveUpperBound Whether an upper bound search sequence exists or not.
 : @param $lowerBoundIncluded Whether to use the <= or the < operator when
 :        comparing a search key from $lowerBound with an index key.
 : @param $upperBoundIncluded Whether to use the <= or the < operator when
 :        comparing an index key with a search key from $upperBound.
 : @return The set of domain nodes that satisfy the search condition
 :
 : @error zerr:ZDDY0021 if the index with name $name is not declared.
 : @error zerr:ZDDY0023 if the index with name $name does not exist.
 : @error zerr:ZDDY0030 if the index is not a general range index.
 : @error err:XPTY0004  if $haveLowerBound is true and $lowerBoundKeys constains
 : an atomic item whose type does not match the sequence type specified by the
 : index keyspec, or $haveUpperBound is true and $upperBoundKeys contains an
 : atomic item whose type does not match the sequence type specified by the
 : index keyspec.
 :)
declare function idml:probe-index-range-general(
  $name               as xs:QName,
  $lowerBound         as xs:anyAtomicType*,
  $upperBound         as xs:anyAtomicType*,
  $haveLowerBound     as xs:boolean,
  $haveUpperBound     as xs:boolean,
  $lowerBoundIncluded as xs:boolean,
  $upperBoundIncluded as xs:boolean) as node()*  external;

(:~
 : The refresh-index function is an updating function which updates
 : the index with the given name.
 : Note that if the maintenance property of the index is automatic,
 : this function is a NOP.
 :
 : @param $name The QName of the index to refresh.
 : @return The result of the function is an empty XDM instance and a
 :         pending update list which, when applied, refreshes the contents
 :         of the index.that consists of a
 :
 : @error zerr:ZDDY0021 if the index with name $name is not declared.
 : @error zerr:ZDDY0023 if the index with name $name does not exist.
 :)
declare updating function idml:refresh-index($name as xs:QName) external;

(:~
 : The keys function returns a sequence of all keys contained in the
 : index with the given name. Each element has the following structure:
 :

 :   <key xmlns="http://www.zorba-xquery.com/modules/store/static/indexes/dml">
 :     <attribute value="key1_value"/>
 :     <attribute value="key2_value"/>
 :     <attribute value="key3_value"/>
 :   </key>
 :

 :
 : @param $name The QName of the index
 : @return The result of the function is sequence of elements each representing
 :         one key contained in the index.
 :
 : Note that the order of the attribute elements reflects the order of
 : the keys in the index specification. Also note that the values in
 : these attributes have the type that is declared in the corresponding
 : index specification.
 :
 : @error zerr:ZDDY0021 if the index with name $name is not declared.
 : @error zerr:ZDDY0023 if the index with name $name does not exist.
 :)
declare function idml:keys($name as xs:QName) as node()* external;

xquery version "3.0";

(:
 : Copyright 2006-2009 The FLWOR Foundation.
 :
 : Licensed under the Apache License, Version 2.0 (the "License");
 : you may not use this file except in compliance with the License.
 : You may obtain a copy of the License at
 :
 : http://www.apache.org/licenses/LICENSE-2.0
 :
 : Unless required by applicable law or agreed to in writing, software
 : distributed under the License is distributed on an "AS IS" BASIS,
 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 : See the License for the specific language governing permissions and
 : limitations under the License.
:)

(:~
 : This module defines a set of functions to probe and refresh indexes which are
 : declared in the prolog of a module.
 :
 :
This module is part of Zorba's XQuery Data
 : Definition Facility. All the indexes managed by this module
 : have to be pre-declared in the prolog of a library module. Please refer to the
 : general documentation
 : for more information and examples.

 :
 : @see Data Lifecycle
 : @see XQuery Data Definition Facility
 : @see http://www.zorba-xquery.com/modules/store/static/indexes/ddl
 : @see http://www.zorba-xquery.com/modules/store/static/collections/dml
 : @see http://www.zorba-xquery.com/modules/store/static/collections/ddl
 : @see http://www.zorba-xquery.com/modules/store/static/integrity_constraints/ddl
 : @see http://www.zorba-xquery.com/modules/store/static/integrity_constraints/dml
 : @see http://www.zorba-xquery.com/errors
 :
 : @author Zorba Team
 :
 : @project store/indexes/static
 :
 :)
module namespace idml = "http://www.zorba-xquery.com/modules/store/static/indexes/dml";

declare namespace zerr = "http://www.zorba-xquery.com/errors";
declare namespace an = "http://www.zorba-xquery.com/annotations";

declare namespace ver = "http://www.zorba-xquery.com/options/versioning";

declare option ver:module-version "2.0";

(:~
 :
The probe-index-point-value function retrieves from an index the domain
 : nodes associated by value equality with a given search tuple.
 : The search tuple consists of a number of search keys, where
 : each search key is either an atomic item or the empty sequence.  The number
 : of search keys given must be equal to the number of keys declared for the
 : index. Since the number of keys differs from one index to another, this
 : function is variadic.

 :
 :
The result of the function is either an error or the set of domain nodes for
 : which the following xquery expression returns true:

 :
 :

 : $key1 eq $node/keyExpr1 and ... and $keyM eq $node/keyExprM
 :

 : where keyExpri is the expression specified in the i-th keyspec
 : of the index.
 :
 : @param $name The QName of the index to probe
 : @param $key_i the search keys used to probe the index with. The i-th search
 :        key corresponds to the i-th key expression in the index declaration.
 : @return The set of domain nodes that satisfy the search condition.
 :
 : @error zerr:ZDDY0021 if the index with name $name is not declared.
 : @error zerr:ZDDY0023 if the index with name $name does not exist.
 : @error zerr:ZDDY0025 if the number of search keys passed as arguments is not
 :        the same as the number of keys declared for the index.
 : @error err:XPTY0004 if a non-empty seach key is given, whose type does not match
 :        the sequence type specified in the corresponding keyspec.
 :
 :)
declare %an:variadic function idml:probe-index-point-value(
  $name as xs:QName,
  $key_i as xs:anyAtomicType?) as node()*  external;

(:~
 :
The probe-index-point-general function retrieves from an index the domain
 : nodes associated by general equality with a given search sequence
 : . The search sequence consists of an arbitrary number of
 : search keys, where each search key is an atomic item. The function
 : is supported by general indexes only. Its result is either an error or the
 : set of domain nodes for which the following xquery expression returns true:

 :

 : $keys = $node/keyExpr
 :

 : where keyExpr is the expression specified in the keyspec of the index (remember
 : that for general indexes, there can be only one keyspec).
 :
 : @param $name The QName of the index to probe.
 : @param $key The search sequence.
 : @return The set of domain nodes that satisfy the search condition.
 :
 : @error zerr:ZDDY0021 if the index with name $name is not declared.
 : @error zerr:ZDDY0023 if the index with name $name does not exist.
 : @error zerr:ZDDY0029 if the index is not general.
 : @error err:XPTY0004  if the search sequence contains a search key, whose
 :        type does not match the sequence type specified in the keyspec of
 :        the index.
 :)
declare function idml:probe-index-point-general(
  $name as xs:QName,
  $key  as xs:anyAtomicType*) as node()*  external;

(:~
 :
The probe-index-range-value function retrieves the domain nodes associated
 : by value order-comparison (operators le, lt, ge, gt) with a given search
 : box. The search box is specified as a number M of rangespecs
 : , where each rangespec consists of six values. The number M must be
 : greater than 0 and less than or equal to the number N of keyspecs found in the
 : index declaration. If M is less than N, then the "missing" rangespecs are
 : assumed to have the following value: [(), (), false, false, false, false].
 : As a result, we can assume that M is equal to N (Remember that for general
 : indexes, there can be only one IndexKeySpec, and as a result, for general
 : indexes, M = N = 1). Since the number of keys differs from one index to
 : another, this function is variadic.

 :
 :
The ith rangespec corresponds to the ith keyspec, and
 : specifies a search condition on the key values that are produced by evaluating
 : that keyspec for every domain node. Specifically, we define the ith
 : rangespec result as the set of domain nodes for which the
 : following xquery expression returns true:

 :
 :

 : if ($haveLowerBound-i and $haveUpperBound-i) then
 :   $lowerBound-i lop $node/keyExpr-i and $node/keyExpr-i uop $upperBound-i
 : else if ($haveLowerBound-i) then
 :   $lowerBound-i lop $node/keyExpr-i
 : else if ($haveUpperBound-i) then
 :   $node/keyExpr-i uop $upperBound-i
 : else
 :   fn:true()
 :

 :
 :
where keyExpr-i is the expression specified by the ith keyspec
 : of the index, lop is either the le or the lt operator depending on whether
 : $lowerBoundsIncluded-i is true or false, and uop is either the le or the
 : lt operator depending on whether $upperBoundsIncluded-i is true or false.

 :
 :
The result of the probe-index-range-value function is either an error,
 : or the intersection of all the rangespec results.

 :
 : @param $name The QName of the index to probe
 : @param $lowerBound-i The lower bound in a range of key values.
 : @param $upperBound-i The upper bound in a range of key values.
 : @param $haveLowerBound-i If false, then there is no lower bound, or
 :        equivalently, the lower bound is -INFINITY. Otherwise, the lower
 :        bound is the one given by the lowerBound-i value.
 : @param $haveUpperBound-i If false, then there is no upper bound, or
 :        equivalently, the upper bound is +INFINITY. Otherwise, the upper
 :        bound is the one given by the upperBound-i value.
 : @param $lowerBoundIncluded-i If false, then the range is open from below,
 :        i.e., the lowerBound-i value is not considered part of the
 :        range. Otherwise, the range is closed from below, i.e., the
 :        lowerBound-i value is part of the range.
 : @param $upperBoundIncluded-i If false, then the range is open from above,
 :        i.e., the upperBound-i value is not considered part of the
 :        range. Otherwise, the range is closed from above, i.e., the
 :        upperBound-i value is part of the range.
 : @return The set of domain nodes that satisfy the search condition.
 :
 : @error zerr:ZDDY0021 if the index with name $name is not declared.
 : @error zerr:ZDDY0023 if the index with name $name does not exist.
 : @error zerr:ZDDY0025 if the number of rangespecs passed as arguments
 :        is zero or greater than the number of keys declared for the index.
 : @error zerr:ZDDY0026 if the index is not a range index.
 : @error err:XPTY0004  if $haveLowerBound-i is true and $lowerBound-i is an
 :        atomic item whose type does not match the sequence type specified
 :        by the ith keyspec, or $haveUpperBound-i is true and
 :        $upperBound-i is an atomic item whose type does not match the
 :        sequence type specified by the ith keyspec.
 : @error zerr:ZDDY0034 if (a) the index is general (in which case there is
 :        only one rangespac), (b) the index is untyped, (c) there is both a
 :        lower and an upper bound, and (d) if T1 and T2 are the types of the
 :        lower and upper bound, neither T1 is a subtype of T2 nor T2 is a
 :        subtype of T1.
 :)
declare %an:variadic function idml:probe-index-range-value(
  $name                 as xs:QName,
  $lowerBound-i         as xs:anyAtomicType?,
  $upperBound-i         as xs:anyAtomicType?,
  $haveLowerBound-i     as xs:boolean,
  $haveUpperBound-i     as xs:boolean,
  $lowerBoundIncluded-i as xs:boolean,
  $upperBoundIncluded-i as xs:boolean) as node()*  external;

(:~
 :
The probe-index-range-general function retrieves the domain nodes associated
 : by general order-comparison (operators <=, <, >=, >) with one or two
 : search sequences. Each search sequence consists of an
 : arbitrary number of search keys, where each search key is
 : an atomic item. This method is supported by general range indexes only. Its
 : result is either an error or the set of domain nodes for which the following
 : xquery expression returns true:

 :
 :

 : if ($haveLowerBound and $haveUpperBound) then
 :   $lowerBoundKeys lop $node/keyExpr and $node/keyExpr uop $upperBoundKeys
 : else if ($haveLowerBound) then
 :   $lowerBoundKeys lop $node/keyExpr
 : else if ($haveUpperBound) then
 :   $node/keyExpr uop $upperBoundKeys
 : else
 :   fn:true()
 :

 :
 :
where keyExpr is the expression specified in the keyspec of the index,
 : lop is either the <= or the < operator depending on whether
 : $lowerBoundsIncluded is true or false, and uop is either the <= or the
 : < operator depending on whether $upperBoundsIncluded is true or false
.
 :
 : @param $name The QName of the index to probe.
 : @param $lowerBound The lower bound search sequence.
 : @param $upperBound The upper bound search sequence.
 : @param $haveLowerBound Whether a lower bound search sequence exists or not.
 : @param $haveUpperBound Whether an upper bound search sequence exists or not.
 : @param $lowerBoundIncluded Whether to use the <= or the < operator when
 :        comparing a search key from $lowerBound with an index key.
 : @param $upperBoundIncluded Whether to use the <= or the < operator when
 :        comparing an index key with a search key from $upperBound.
 : @return The set of domain nodes that satisfy the search condition
 :
 : @error zerr:ZDDY0021 if the index with name $name is not declared.
 : @error zerr:ZDDY0023 if the index with name $name does not exist.
 : @error zerr:ZDDY0030 if the index is not a general range index.
 : @error err:XPTY0004  if $haveLowerBound is true and $lowerBoundKeys constains
 : an atomic item whose type does not match the sequence type specified by the
 : index keyspec, or $haveUpperBound is true and $upperBoundKeys contains an
 : atomic item whose type does not match the sequence type specified by the
 : index keyspec.
 :)
declare function idml:probe-index-range-general(
  $name               as xs:QName,
  $lowerBound         as xs:anyAtomicType*,
  $upperBound         as xs:anyAtomicType*,
  $haveLowerBound     as xs:boolean,
  $haveUpperBound     as xs:boolean,
  $lowerBoundIncluded as xs:boolean,
  $upperBoundIncluded as xs:boolean) as node()*  external;

(:~
 : The refresh-index function is an updating function which updates
 : the index with the given name.
 : Note that if the maintenance property of the index is automatic,
 : this function is a NOP.
 :
 : @param $name The QName of the index to refresh.
 : @return The result of the function is an empty XDM instance and a
 :         pending update list which, when applied, refreshes the contents
 :         of the index.that consists of a
 :
 : @error zerr:ZDDY0021 if the index with name $name is not declared.
 : @error zerr:ZDDY0023 if the index with name $name does not exist.
 :)
declare updating function idml:refresh-index($name as xs:QName) external;

(:~
 : The keys function returns a sequence of all keys contained in the
 : index with the given name. Each element has the following structure:
 :

 :   <key xmlns="http://www.zorba-xquery.com/modules/store/static/indexes/dml">
 :     <attribute value="key1_value"/>
 :     <attribute value="key2_value"/>
 :     <attribute value="key3_value"/>
 :   </key>
 :

 :
 : @param $name The QName of the index
 : @return The result of the function is sequence of elements each representing
 :         one key contained in the index.
 :
 : Note that the order of the attribute elements reflects the order of
 : the keys in the index specification. Also note that the values in
 : these attributes have the type that is declared in the corresponding
 : index specification.
 :
 : @error zerr:ZDDY0021 if the index with name $name is not declared.
 : @error zerr:ZDDY0023 if the index with name $name does not exist.
 :)
declare function idml:keys($name as xs:QName) as node()* external;


This should make tracing look a lot better

(:
  Copyright 2012-2018 MarkLogic Corporation

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
:)
xquery version "1.0-ml";

module namespace trace = "http://marklogic.com/data-hub/trace";

import module namespace config = "http://marklogic.com/data-hub/config"
  at "/com.marklogic.hub/config.xqy";

import module namespace consts = "http://marklogic.com/data-hub/consts"
  at "/MarkLogic/data-hub-framework/impl/consts.xqy";

import module namespace err = "http://marklogic.com/data-hub/err"
  at "/MarkLogic/data-hub-framework/impl/error-lib.xqy";

import module namespace hul = "http://marklogic.com/data-hub/hub-utils-lib"
  at "/MarkLogic/data-hub-framework/impl/hub-utils-lib.xqy";

import module namespace json="http://marklogic.com/xdmp/json"
  at "/MarkLogic/json/json.xqy";

import module namespace rfc = "http://marklogic.com/data-hub/run-flow-context"
  at "/MarkLogic/data-hub-framework/impl/run-flow-context.xqy";

import module namespace search = "http://marklogic.com/appservices/search"
  at "/MarkLogic/appservices/search/search.xqy";

declare option xdmp:mapping "false";

(: new trace-settings are initialized for each transaction :)
declare variable $current-trace-settings := map:map();

declare function trace:new-trace() as map:map
{
  map:new((
    map:entry("traceId", xdmp:random()),
    map:entry("created", fn:current-dateTime())
  ))
};

declare function trace:enable-tracing($enabled as xs:boolean)
{
  xdmp:eval('
    declare namespace trace = "http://marklogic.com/data-hub/trace";
    declare variable $enabled external;
    xdmp:document-insert(
      "/com.marklogic.hub/settings/__tracing_enabled__.xml",
      element trace:is-tracing-enabled { if ($enabled) then 1 else 0 },
      xdmp:default-permissions(),
      "hub-core-module")
    ',
    map:new((map:entry("enabled", $enabled))),
    map:new((map:entry("database", xdmp:modules-database()), map:entry("ignoreAmps", fn:true())))
  ),
  hul:invalidate-field-cache("tracing-enabled")
};

declare function trace:enabled() as xs:boolean
{
  let $key := "tracing-enabled"
  let $flag :=  hul:from-field-cache-or-empty($key, ())
  return
    if (exists($flag)) then
      $flag
    else
      hul:set-field-cache(
        $key,
        xdmp:eval('
          declare namespace trace = "http://marklogic.com/data-hub/trace";
          fn:exists(
            cts:search(
              fn:doc("/com.marklogic.hub/settings/__tracing_enabled__.xml"),
              cts:element-value-query(xs:QName("trace:is-tracing-enabled"), "1", ("exact")),
              ("unfiltered", "score-zero", "unchecked", "unfaceted")
            )
          )
        ',(), map:new(map:entry("database", xdmp:modules-database()))),
        xs:dayTimeDuration("PT1M")
      )
};

declare function trace:has-errors() as xs:boolean
{
  (map:get($current-trace-settings, "_has_errors"), fn:false())[1] eq fn:true()
};

declare %private function trace:increment-error-count()
{
  map:put($current-trace-settings, "error-count", trace:get-error-count() + 1)
};

declare function trace:get-error-count()
{
  (map:get($current-trace-settings, "error-count"), 0)[1]
};

declare %private function trace:add-failed-item($item as xs:string)
{
  json:array-push(trace:get-failed-items(), $item)
};

declare %private function trace:add-completed-item($item as xs:string)
{
  json:array-push(trace:get-completed-items(), $item)
};

declare function trace:set-plugin-label(
  $label as xs:string)
{
  trace:set-plugin-label(
    rfc:get-trace($rfc:item-context),
    $label
  )
};

(:
 : Sets the label of the currently running plugin
 :
 : @param $label - the label of the running plugin
 :)
declare function trace:set-plugin-label(
  $current-trace as map:map,
  $label as xs:string)
{
  map:put($current-trace, "plugin-label", $label)
};

declare function trace:get-plugin-label(
  $current-trace as map:map)
as xs:string
{
  map:get($current-trace, "plugin-label")
};

declare function trace:reset-plugin-input()
{
  trace:reset-plugin-input(rfc:get-trace($rfc:item-context))
};

declare function trace:reset-plugin-input(
  $current-trace as map:map)
{
  map:put($current-trace, "plugin-input", json:object())
};

declare %private function trace:get-plugin-input(
  $current-trace as map:map)
{
  let $o := map:get($current-trace, "plugin-input")
  where fn:exists($o)
  return
    if (rfc:is-json()) then
      let $oo := json:object()
      let $_ :=
        for $key in map:keys($o)
        let $value := map:get($o, $key)
        let $value := trace:sanitize-data($value)
        return
         map:put($oo, $key, $value)
      return $oo
    else
      for $key in map:keys($o)
      return
        element { $key } {
          let $value := map:get($o, $key)
          return
            trace:sanitize-data($value)
        }
};

(:
 : Registers an input with the trace library
 :
 : @param $label - the label of the input being registered
 : @param $input - the value of the input being registered
 :)
declare function trace:set-plugin-input(
  $label as xs:string,
  $input)
{
  trace:set-plugin-input(rfc:get-trace($rfc:item-context), $label, $input)
};

declare function trace:set-plugin-input(
  $current-trace as map:map,
  $label as xs:string,
  $input)
{
  let $existing := (map:get($current-trace, "plugin-input"), json:object())[1]
  let $_ := map:put($existing, $label, $input)
  return
    map:put($current-trace, "plugin-input", $existing)
};

declare function trace:get-completed-items()
{
  if (map:contains($current-trace-settings, "completed-items")) then
    map:get($current-trace-settings, "completed-items")
  else
    let $value := json:array()
    let $_ := map:put($current-trace-settings, "completed-items", $value)
    return
      $value
};

declare function trace:get-failed-items()
{
  if (map:contains($current-trace-settings, "failed-items")) then
    map:get($current-trace-settings, "failed-items")
  else
    let $value := json:array()
    let $_ := map:put($current-trace-settings, "failed-items", $value)
    return
      $value
};

declare function trace:write-trace(
  $item-context as map:map)
{
  let $identifier := rfc:get-id($item-context)
  where $identifier instance of xs:string
  return
    trace:add-completed-item($identifier),
  trace:write-error-trace($item-context)
};

declare %private function trace:write-error-trace(
  $item-context as map:map)
{
  let $current-trace := rfc:get-trace($item-context)
  return
    if (trace:enabled() or trace:has-errors()) then (
      let $trace :=
        if (rfc:is-json()) then
          xdmp:to-json((
            map:entry("trace",
              map:new((
                map:entry("jobId", rfc:get-job-id()),
                map:entry("format", rfc:get-data-format()),
                map:entry("traceId", map:get($current-trace, "traceId")),
                map:entry("created", map:get($current-trace, "created")),
                map:entry("identifier", rfc:get-id($item-context)),
                map:entry("flowType", rfc:get-flow-type()),
                map:entry("hasError", trace:has-errors()),
                let $steps := json:array()
                let $_ :=
                  for $step in map:get($current-trace, "traceSteps")
                  return
                    json:array-push($steps, $step)
                return
                  map:entry("steps", $steps)
              ))
            )
          ))
        else
          document {
            element trace {
              element jobId { rfc:get-job-id() },
              element format { rfc:get-data-format() },
              element traceId { map:get($current-trace, "traceId") },
              element created { map:get($current-trace, "created") },
              element identifier { rfc:get-id($item-context) },
              element flowType { rfc:get-flow-type() },
              element hasError { trace:has-errors() },
              element steps {
                for $step in map:get($current-trace, "traceSteps")
                return
                  element step {
                    element label { map:get($step, "label") },
                    element input { map:get($step, "input") },
                    element output { map:get($step, "output") },
                    element error { map:get($step, "error") },
                    element duration { map:get($step, "duration") },
                    element options { map:get($step, "options") }
                  }
              }
            }
          }
      return
        xdmp:eval('
          xquery version "1.0-ml";

          declare option xdmp:mapping "false";

          declare variable $trace external;
          declare variable $extension external;

          xdmp:document-insert(
            "/" || $trace/*:trace/*:traceId || $extension,
            $trace,
            xdmp:default-permissions(),
            ("trace", $trace/*:trace/*:flow-type, $trace/*:trace/*:flowType)
          )
        ',
        map:new((
          map:entry("trace", $trace),
          map:entry("extension", if (rfc:is-json()) then ".json" else ".xml")
        )),
        map:new((
          map:entry("database", xdmp:database($config:TRACE-DATABASE)),
          map:entry("commit", "auto"),
          map:entry("update", "true"),
          map:entry("ignoreAmps", fn:true())
        )))
    )
    else ()
};

declare function trace:sanitize-data($data)
{
  if ($data instance of binary()) then xs:hexBinary($data)
  else if (fn:not(rfc:is-json())) then
    if ($data instance of null-node()) then ()
    else $data
  else $data
};

declare function trace:plugin-trace(
  $output,
  $duration) as empty-sequence()
{
  trace:plugin-trace($rfc:item-context, $output, $duration)
};

declare function trace:plugin-trace(
  $item-context as map:map,
  $output,
  $duration) as empty-sequence()
{
  let $current-trace := rfc:get-trace($item-context)
  let $output := trace:sanitize-data($output)
  return
    if (trace:enabled()) then(
      let $new-step := map:map()
      let $_ := (
        map:put($new-step, "label", get-plugin-label($current-trace)),
        trace:get-plugin-input($current-trace) ! map:put($new-step, "input", .),
        map:put($new-step, "output", $output),
        map:put($new-step, "duration", $duration),
        map:put($new-step, "options", json:object(document { rfc:get-options($item-context) }/node()))
      )
      let $trace-steps := (
        map:get($current-trace, "traceSteps"),
        $new-step
      )
      return
        map:put($current-trace, "traceSteps", $trace-steps)
    )
    else ()
};

declare function trace:error-trace(
  $item-context as map:map,
  $error as element(error:error),
  $duration as xs:dayTimeDuration)
{
  let $current-trace := rfc:get-trace($item-context)
  let $identifier := rfc:get-id($item-context)
  let $_ := trace:increment-error-count()
  let $_ := $identifier ! trace:add-failed-item(.)
  return (
    map:put($current-trace-settings, "_has_errors", fn:true()),
    let $trace-steps := (
      map:get($current-trace, "traceSteps"),
      map:new((
        map:entry("label", get-plugin-label($current-trace)),
        map:entry("input", get-plugin-input($current-trace)),
        map:entry("error",
          if (rfc:is-json()) then
            $error/err:error-to-json(.)
          else
            $error
        ),
        map:entry("duration", $duration),
        map:entry("options", rfc:get-options($item-context))
      ))
    )
    let $_ := map:put($current-trace, "traceSteps", $trace-steps)
    let $_ := trace:write-error-trace($item-context)
    return ()
  )
};

declare function trace:_walk_json($nodes as node()* ,$o)
{
  let $quote-options :=
    <options xmlns="xdmp:quote">
      <indent>yes</indent>
      <indent-untyped>yes</indent-untyped>
      <omit-xml-declaration>yes</omit-xml-declaration>
    </options>

  for $n in $nodes
  return
    typeswitch($n)
      case array-node() return
        let $name as xs:string := fn:string(fn:node-name($n))
        return
          if ($name = "steps") then
            let $a := json:array()
            let $_ :=
              for $value in $n/node()
              let $oo := json:object()
              let $_ := trace:_walk_json($value/node(), $oo)
              return
                json:array-push($a, $oo)
            return
              map:put($o, $name, $a)
          else
            map:put($o, $name, xdmp:quote($n))
      case object-node() return
        let $oo := map:new()
        let $name as xs:string := fn:string(fn:node-name($n))
        return
          if ($name = "input") then
            if ($n/node()) then
              let $_ :=
                for $x in $n/node()
                (: try to unquote xml for formatting :)
                let $unquoted :=
                  if ($x instance of text()) then
                    try {
                      xdmp:quote(xdmp:unquote(fn:string($x)), $quote-options)
                    }
                    catch($ex) {
                      $x
                    }
                  else
                    $x
                let $nn := fn:string(fn:node-name($x))
                return
                  map:put($oo, $nn, $unquoted)
              return
                map:put($o, $name, $oo)
            else
              map:put($o, $name, null-node {})
          else if ($name = "output") then
            (: try to unquote xml for formatting :)
            let $unquoted :=
              if ($n instance of text()) then
                try {
                  xdmp:quote(xdmp:unquote(fn:string($n)), $quote-options)
                }
                catch($ex) {
                  $n
                }
              else
                $n
            return
              map:put($o, $name, $unquoted)
          else if ($name = "error") then
            map:put($o, $name, $n)
          else if ($name = ("collectorPlugin", "contentPlugin", "headersPlugin", "triplesPlugin", "writerPlugin")) then
            ()
          else
            let $_ := trace:_walk_json($n/node(), $oo)
            return
              map:put($o, $name, $oo)
      case number-node() |
           boolean-node() |
           null-node() return
        map:put($o, fn:string(fn:node-name($n)), fn:data($n))
      case text() return
        let $unquoted :=
          try {
            xdmp:quote(xdmp:unquote(fn:string($n)), $quote-options)
          }
          catch($ex) {
            $n
          }
        return
          map:put($o, fn:string(fn:node-name($n)), $unquoted)
      case element(input) return
        let $oo := json:object()
        let $_ :=
          for $x in $n/*
          return
            if($x instance of element()) then (map:put($oo, fn:local-name($x), xdmp:quote($x/node(), $quote-options)))
            else (map:put($oo, fn:local-name($x), xdmp:quote($x/node()/data(), $quote-options)))
        return
          map:put($o, "input", $oo)
      case element(output) return
        map:put($o, fn:local-name($n), xdmp:quote($n/node(), $quote-options))
      case element(error) return
        map:put($o, fn:local-name($n), $n/error:error/err:error-to-json(.))
      case element(duration) return
        map:put($o, "duration", fn:seconds-from-duration(xs:dayTimeDuration($n)))
      case element(hasError) return
        map:put($o, "hasError", xs:boolean($n))
      case element(steps) return
        let $a := json:array()
        let $_ :=
          for $step in $n/step
          let $oo := json:object()
          let $_ := trace:_walk_json($step/*, $oo)
          return
            json:array-push($a, $oo)
        return
          map:put($o, "steps", $a)
      case element() return
        if ($n/*) then
          let $oo := json:object()
          let $_ := trace:_walk_json($n/*, $oo)
          return
            map:put($o, fn:local-name($n), $oo)
        else
          map:put($o, fn:local-name($n), $n/fn:data(.))
      default return
        $n
};

declare function trace:trace-to-json-legacy($trace)
{
  let $o := json:object()
  let $_ :=
    for $n in $trace/node()
    let $name := fn:string(fn:node-name($n))
    where fn:not($name = ("collectorPlugin", "contentPlugin", "headersPlugin", "triplesPlugin", "writerPlugin"))
    return
      map:put($o, $name, $n/data())
  let $steps := json:array()
  let $_build_steps :=
    for $n in $trace/node()
    let $name := fn:string(fn:node-name($n))
    where $name = ("collectorPlugin", "contentPlugin", "headersPlugin", "triplesPlugin", "writerPlugin")
    return
      let $step := json:object()
      let $_ := map:put($step, "label", fn:replace($name, "Plugin", ""))
      let $_ :=
        trace:_walk_json($n/node(), $step)
      return
        json:array-push($steps, $step)
  let $_ := map:put($o, "steps", $steps)
  return $o
};

declare function trace:trace-to-json($trace)
{
  if (fn:exists($trace/steps) or
      xdmp:node-kind($trace) = "object" and
      fn:exists($trace/trace/steps)) then
    let $o := json:object()
    let $walk-me :=
      let $n := $trace/node()
      return
        if ($n instance of object-node()) then
          $n/node()
        else
          $n
    let $_ := trace:_walk_json($walk-me, $o)
    return
      $o
  else
    trace:trace-to-json-legacy($trace)
};

declare function trace:trace-to-json-slim($trace)
{
  let $o := json:object()
  let $_ := (
    map:put($o, "traceId", $trace/trace/traceId/string()),
    map:put($o, "jobId", $trace/trace/jobId/string()),
    map:put($o, "created", $trace/trace/created/string()),
    map:put($o, "hasError", $trace/trace/hasError/xs:boolean(.)),
    map:put($o, "identifier", $trace/trace/identifier/string()),
    map:put($o, "flowType", $trace/trace/flowType/string()),
    map:put($o, "format", $trace/trace/format/string())
  )
  return
    $o
};

declare function trace:find-traces(
  $q,
  $page as xs:unsignedLong,
  $page-length as xs:unsignedLong)
{
  let $options :=
    <options xmlns="http://marklogic.com/appservices/search">
      <additional-query>{cts:collection-query("trace")}</additional-query>
      <return-results>false</return-results>
      <return-facets>true</return-facets>
      <sort-order type="xs:dateTime"
          direction="descending">
        <path-index>/trace/created</path-index>
      </sort-order>
    </options>
  let $query := search:parse($q)
  let $count := search:estimate($query, $options)
  let $start := ($page - 1) * $page-length + 1
  let $end := fn:min(($start + $page-length - 1, $count))
  let $results :=
    for $result in search:resolve-nodes($query, $options, $start, $page-length)
    return
      trace:trace-to-json-slim($result/node())
  return
    object-node {
      "start": $start,
      "end": $end,
      "total": $count,
      "page": $page,
      "pageCount": $page-length,
      "traces": json:to-array($results)
    }
};

declare function trace:get-traces($page as xs:int, $page-length as xs:int)
{
  let $start := ($page - 1) * $page-length + 1
  let $end := $start + $page-length - 1
  let $count := xdmp:estimate(/trace)
  let $traces :=
    for $trace in cts:search(/trace, cts:true-query(), ("unfiltered", cts:index-order(cts:path-reference("/trace/created"), "descending")))[$start to $end]
    return
      trace:trace-to-json($trace)
  return
    object-node {
      "start": $start,
      "end": $end,
      "total": $count,
      "page": $page,
      "pageCount": $page-length,
      "traces": json:to-array($traces)
    }
};

declare function trace:get-trace($id as xs:string)
{
  let $query :=
    cts:or-query((
      cts:path-range-query("/trace/traceId", "=", $id, ("collation=http://marklogic.com/collation/codepoint"))
    ))
  return
    trace:trace-to-json(cts:search(fn:doc(), $query)[1]/node())
};

declare function trace:get-traceIds($q as xs:string?)
{
  let $query :=
    if ($q) then
      cts:element-value-query(xs:QName("identifier"), fn:lower-case($q) || "*", "wildcarded")
    else ()
  let $results :=
    cts:value-co-occurrences(
      cts:path-reference("/trace/traceId"),
      cts:path-reference("/trace/identifier"),
      (
        "limit=10"
      ),
      $query)
  let $results :=
    for $r in $results
    return
      object-node {
        "traceId": fn:data($r/*:value[1]),
        "identifier": fn:data($r/*:value[2])
      }
  return
    json:to-array($results)
};

(:
  Copyright 2012-2018 MarkLogic Corporation

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
:)
xquery version "1.0-ml";

module namespace trace = "http://marklogic.com/data-hub/trace";

import module namespace config = "http://marklogic.com/data-hub/config"
  at "/com.marklogic.hub/config.xqy";

import module namespace consts = "http://marklogic.com/data-hub/consts"
  at "/MarkLogic/data-hub-framework/impl/consts.xqy";

import module namespace err = "http://marklogic.com/data-hub/err"
  at "/MarkLogic/data-hub-framework/impl/error-lib.xqy";

import module namespace hul = "http://marklogic.com/data-hub/hub-utils-lib"
  at "/MarkLogic/data-hub-framework/impl/hub-utils-lib.xqy";

import module namespace json="http://marklogic.com/xdmp/json"
  at "/MarkLogic/json/json.xqy";

import module namespace rfc = "http://marklogic.com/data-hub/run-flow-context"
  at "/MarkLogic/data-hub-framework/impl/run-flow-context.xqy";

import module namespace search = "http://marklogic.com/appservices/search"
  at "/MarkLogic/appservices/search/search.xqy";

declare option xdmp:mapping "false";

(: new trace-settings are initialized for each transaction :)
declare variable $current-trace-settings := map:map();

declare function trace:new-trace() as map:map
{
  map:new((
    map:entry("traceId", xdmp:random()),
    map:entry("created", fn:current-dateTime())
  ))
};

declare function trace:enable-tracing($enabled as xs:boolean)
{
  xdmp:eval('
    declare namespace trace = "http://marklogic.com/data-hub/trace";
    declare variable $enabled external;
    xdmp:document-insert(
      "/com.marklogic.hub/settings/__tracing_enabled__.xml",
      element trace:is-tracing-enabled { if ($enabled) then 1 else 0 },
      xdmp:default-permissions(),
      "hub-core-module")
    ',
    map:new((map:entry("enabled", $enabled))),
    map:new((map:entry("database", xdmp:modules-database()), map:entry("ignoreAmps", fn:true())))
  ),
  hul:invalidate-field-cache("tracing-enabled")
};

declare function trace:enabled() as xs:boolean
{
  let $key := "tracing-enabled"
  let $flag :=  hul:from-field-cache-or-empty($key, ())
  return
    if (exists($flag)) then
      $flag
    else
      hul:set-field-cache(
        $key,
        xdmp:eval('
          declare namespace trace = "http://marklogic.com/data-hub/trace";
          fn:exists(
            cts:search(
              fn:doc("/com.marklogic.hub/settings/__tracing_enabled__.xml"),
              cts:element-value-query(xs:QName("trace:is-tracing-enabled"), "1", ("exact")),
              ("unfiltered", "score-zero", "unchecked", "unfaceted")
            )
          )
        ',(), map:new(map:entry("database", xdmp:modules-database()))),
        xs:dayTimeDuration("PT1M")
      )
};

declare function trace:has-errors() as xs:boolean
{
  (map:get($current-trace-settings, "_has_errors"), fn:false())[1] eq fn:true()
};

declare %private function trace:increment-error-count()
{
  map:put($current-trace-settings, "error-count", trace:get-error-count() + 1)
};

declare function trace:get-error-count()
{
  (map:get($current-trace-settings, "error-count"), 0)[1]
};

declare %private function trace:add-failed-item($item as xs:string)
{
  json:array-push(trace:get-failed-items(), $item)
};

declare %private function trace:add-completed-item($item as xs:string)
{
  json:array-push(trace:get-completed-items(), $item)
};

declare function trace:set-plugin-label(
  $label as xs:string)
{
  trace:set-plugin-label(
    rfc:get-trace($rfc:item-context),
    $label
  )
};

(:
 : Sets the label of the currently running plugin
 :
 : @param $label - the label of the running plugin
 :)
declare function trace:set-plugin-label(
  $current-trace as map:map,
  $label as xs:string)
{
  map:put($current-trace, "plugin-label", $label)
};

declare function trace:get-plugin-label(
  $current-trace as map:map)
as xs:string
{
  map:get($current-trace, "plugin-label")
};

declare function trace:reset-plugin-input()
{
  trace:reset-plugin-input(rfc:get-trace($rfc:item-context))
};

declare function trace:reset-plugin-input(
  $current-trace as map:map)
{
  map:put($current-trace, "plugin-input", json:object())
};

declare %private function trace:get-plugin-input(
  $current-trace as map:map)
{
  let $o := map:get($current-trace, "plugin-input")
  where fn:exists($o)
  return
    if (rfc:is-json()) then
      let $oo := json:object()
      let $_ :=
        for $key in map:keys($o)
        let $value := map:get($o, $key)
        let $value := trace:sanitize-data($value)
        return
         map:put($oo, $key, $value)
      return $oo
    else
      for $key in map:keys($o)
      return
        element { $key } {
          let $value := map:get($o, $key)
          return
            trace:sanitize-data($value)
        }
};

(:
 : Registers an input with the trace library
 :
 : @param $label - the label of the input being registered
 : @param $input - the value of the input being registered
 :)
declare function trace:set-plugin-input(
  $label as xs:string,
  $input)
{
  trace:set-plugin-input(rfc:get-trace($rfc:item-context), $label, $input)
};

declare function trace:set-plugin-input(
  $current-trace as map:map,
  $label as xs:string,
  $input)
{
  let $existing := (map:get($current-trace, "plugin-input"), json:object())[1]
  let $_ := map:put($existing, $label, $input)
  return
    map:put($current-trace, "plugin-input", $existing)
};

declare function trace:get-completed-items()
{
  if (map:contains($current-trace-settings, "completed-items")) then
    map:get($current-trace-settings, "completed-items")
  else
    let $value := json:array()
    let $_ := map:put($current-trace-settings, "completed-items", $value)
    return
      $value
};

declare function trace:get-failed-items()
{
  if (map:contains($current-trace-settings, "failed-items")) then
    map:get($current-trace-settings, "failed-items")
  else
    let $value := json:array()
    let $_ := map:put($current-trace-settings, "failed-items", $value)
    return
      $value
};

declare function trace:write-trace(
  $item-context as map:map)
{
  let $identifier := rfc:get-id($item-context)
  where $identifier instance of xs:string
  return
    trace:add-completed-item($identifier),
  trace:write-error-trace($item-context)
};

declare %private function trace:write-error-trace(
  $item-context as map:map)
{
  let $current-trace := rfc:get-trace($item-context)
  return
    if (trace:enabled() or trace:has-errors()) then (
      let $trace :=
        if (rfc:is-json()) then
          xdmp:to-json((
            map:entry("trace",
              map:new((
                map:entry("jobId", rfc:get-job-id()),
                map:entry("format", rfc:get-data-format()),
                map:entry("traceId", map:get($current-trace, "traceId")),
                map:entry("created", map:get($current-trace, "created")),
                map:entry("identifier", rfc:get-id($item-context)),
                map:entry("flowType", rfc:get-flow-type()),
                map:entry("hasError", trace:has-errors()),
                let $steps := json:array()
                let $_ :=
                  for $step in map:get($current-trace, "traceSteps")
                  return
                    json:array-push($steps, $step)
                return
                  map:entry("steps", $steps)
              ))
            )
          ))
        else
          document {
            element trace {
              element jobId { rfc:get-job-id() },
              element format { rfc:get-data-format() },
              element traceId { map:get($current-trace, "traceId") },
              element created { map:get($current-trace, "created") },
              element identifier { rfc:get-id($item-context) },
              element flowType { rfc:get-flow-type() },
              element hasError { trace:has-errors() },
              element steps {
                for $step in map:get($current-trace, "traceSteps")
                return
                  element step {
                    element label { map:get($step, "label") },
                    element input { map:get($step, "input") },
                    element output { map:get($step, "output") },
                    element error { map:get($step, "error") },
                    element duration { map:get($step, "duration") },
                    element options { map:get($step, "options") }
                  }
              }
            }
          }
      return
        xdmp:eval('
          xquery version "1.0-ml";

          declare option xdmp:mapping "false";

          declare variable $trace external;
          declare variable $extension external;

          xdmp:document-insert(
            "/" || $trace/*:trace/*:traceId || $extension,
            $trace,
            xdmp:default-permissions(),
            ("trace", $trace/*:trace/*:flow-type, $trace/*:trace/*:flowType)
          )
        ',
        map:new((
          map:entry("trace", $trace),
          map:entry("extension", if (rfc:is-json()) then ".json" else ".xml")
        )),
        map:new((
          map:entry("database", xdmp:database($config:TRACE-DATABASE)),
          map:entry("commit", "auto"),
          map:entry("update", "true"),
          map:entry("ignoreAmps", fn:true())
        )))
    )
    else ()
};

declare function trace:sanitize-data($data)
{
  if ($data instance of binary()) then xs:hexBinary($data)
  else if (fn:not(rfc:is-json())) then
    if ($data instance of null-node()) then ()
    else $data
  else $data
};

declare function trace:plugin-trace(
  $output,
  $duration) as empty-sequence()
{
  trace:plugin-trace($rfc:item-context, $output, $duration)
};

declare function trace:plugin-trace(
  $item-context as map:map,
  $output,
  $duration) as empty-sequence()
{
  let $current-trace := rfc:get-trace($item-context)
  let $output := trace:sanitize-data($output)
  return
    if (trace:enabled()) then(
      let $new-step := map:map()
      let $_ := (
        map:put($new-step, "label", get-plugin-label($current-trace)),
        trace:get-plugin-input($current-trace) ! map:put($new-step, "input", .),
        map:put($new-step, "output", $output),
        map:put($new-step, "duration", $duration),
        map:put($new-step, "options", json:object(document { rfc:get-options($item-context) }/node()))
      )
      let $trace-steps := (
        map:get($current-trace, "traceSteps"),
        $new-step
      )
      return
        map:put($current-trace, "traceSteps", $trace-steps)
    )
    else ()
};

declare function trace:error-trace(
  $item-context as map:map,
  $error as element(error:error),
  $duration as xs:dayTimeDuration)
{
  let $current-trace := rfc:get-trace($item-context)
  let $identifier := rfc:get-id($item-context)
  let $_ := trace:increment-error-count()
  let $_ := $identifier ! trace:add-failed-item(.)
  return (
    map:put($current-trace-settings, "_has_errors", fn:true()),
    let $trace-steps := (
      map:get($current-trace, "traceSteps"),
      map:new((
        map:entry("label", get-plugin-label($current-trace)),
        map:entry("input", get-plugin-input($current-trace)),
        map:entry("error",
          if (rfc:is-json()) then
            $error/err:error-to-json(.)
          else
            $error
        ),
        map:entry("duration", $duration),
        map:entry("options", rfc:get-options($item-context))
      ))
    )
    let $_ := map:put($current-trace, "traceSteps", $trace-steps)
    let $_ := trace:write-error-trace($item-context)
    return ()
  )
};

declare function trace:_walk_json($nodes as node()* ,$o)
{
  let $quote-options :=
    <options xmlns="xdmp:quote">
      <indent>yes</indent>
      <indent-untyped>yes</indent-untyped>
      <omit-xml-declaration>yes</omit-xml-declaration>
    </options>

  for $n in $nodes
  return
    typeswitch($n)
      case array-node() return
        let $name as xs:string := fn:string(fn:node-name($n))
        return
          if ($name = "steps") then
            let $a := json:array()
            let $_ :=
              for $value in $n/node()
              let $oo := json:object()
              let $_ := trace:_walk_json($value/node(), $oo)
              return
                json:array-push($a, $oo)
            return
              map:put($o, $name, $a)
          else
            map:put($o, $name, xdmp:quote($n))
      case object-node() return
        let $oo := map:new()
        let $name as xs:string := fn:string(fn:node-name($n))
        return
          if ($name = "input") then
            if ($n/node()) then
              let $_ :=
                for $x in $n/node()
                (: try to unquote xml for formatting :)
                let $unquoted :=
                  if ($x instance of text()) then
                    try {
                      xdmp:quote(xdmp:unquote(fn:string($x)), $quote-options)
                    }
                    catch($ex) {
                      $x
                    }
                  else
                    $x
                let $nn := fn:string(fn:node-name($x))
                return
                  map:put($oo, $nn, $unquoted)
              return
                map:put($o, $name, $oo)
            else
              map:put($o, $name, null-node {})
          else if ($name = "output") then
            (: try to unquote xml for formatting :)
            let $unquoted :=
              if ($n instance of text()) then
                try {
                  xdmp:quote(xdmp:unquote(fn:string($n)), $quote-options)
                }
                catch($ex) {
                  $n
                }
              else
                $n
            return
              map:put($o, $name, $unquoted)
          else if ($name = "error") then
            map:put($o, $name, $n)
          else if ($name = ("collectorPlugin", "contentPlugin", "headersPlugin", "triplesPlugin", "writerPlugin")) then
            ()
          else
            let $_ := trace:_walk_json($n/node(), $oo)
            return
              map:put($o, $name, $oo)
      case number-node() |
           boolean-node() |
           null-node() return
        map:put($o, fn:string(fn:node-name($n)), fn:data($n))
      case text() return
        let $unquoted :=
          try {
            xdmp:quote(xdmp:unquote(fn:string($n)), $quote-options)
          }
          catch($ex) {
            $n
          }
        return
          map:put($o, fn:string(fn:node-name($n)), $unquoted)
      case element(input) return
        let $oo := json:object()
        let $_ :=
          for $x in $n/*
          return
            map:put($oo, fn:local-name($x), xdmp:quote($x/*, $quote-options))
        return
          map:put($o, "input", $oo)
      case element(output) return
        map:put($o, fn:local-name($n), xdmp:quote($n/node(), $quote-options))
      case element(error) return
        map:put($o, fn:local-name($n), $n/error:error/err:error-to-json(.))
      case element(duration) return
        map:put($o, "duration", fn:seconds-from-duration(xs:dayTimeDuration($n)))
      case element(hasError) return
        map:put($o, "hasError", xs:boolean($n))
      case element(steps) return
        let $a := json:array()
        let $_ :=
          for $step in $n/step
          let $oo := json:object()
          let $_ := trace:_walk_json($step/*, $oo)
          return
            json:array-push($a, $oo)
        return
          map:put($o, "steps", $a)
      case element() return
        if ($n/*) then
          let $oo := json:object()
          let $_ := trace:_walk_json($n/*, $oo)
          return
            map:put($o, fn:local-name($n), $oo)
        else
          map:put($o, fn:local-name($n), $n/fn:data(.))
      default return
        $n
};

declare function trace:trace-to-json-legacy($trace)
{
  let $o := json:object()
  let $_ :=
    for $n in $trace/node()
    let $name := fn:string(fn:node-name($n))
    where fn:not($name = ("collectorPlugin", "contentPlugin", "headersPlugin", "triplesPlugin", "writerPlugin"))
    return
      map:put($o, $name, $n/data())
  let $steps := json:array()
  let $_build_steps :=
    for $n in $trace/node()
    let $name := fn:string(fn:node-name($n))
    where $name = ("collectorPlugin", "contentPlugin", "headersPlugin", "triplesPlugin", "writerPlugin")
    return
      let $step := json:object()
      let $_ := map:put($step, "label", fn:replace($name, "Plugin", ""))
      let $_ :=
        trace:_walk_json($n/node(), $step)
      return
        json:array-push($steps, $step)
  let $_ := map:put($o, "steps", $steps)
  return $o
};

declare function trace:trace-to-json($trace)
{
  if (fn:exists($trace/steps) or
      xdmp:node-kind($trace) = "object" and
      fn:exists($trace/trace/steps)) then
    let $o := json:object()
    let $walk-me :=
      let $n := $trace/node()
      return
        if ($n instance of object-node()) then
          $n/node()
        else
          $n
    let $_ := trace:_walk_json($walk-me, $o)
    return
      $o
  else
    trace:trace-to-json-legacy($trace)
};

declare function trace:trace-to-json-slim($trace)
{
  let $o := json:object()
  let $_ := (
    map:put($o, "traceId", $trace/trace/traceId/string()),
    map:put($o, "jobId", $trace/trace/jobId/string()),
    map:put($o, "created", $trace/trace/created/string()),
    map:put($o, "hasError", $trace/trace/hasError/xs:boolean(.)),
    map:put($o, "identifier", $trace/trace/identifier/string()),
    map:put($o, "flowType", $trace/trace/flowType/string()),
    map:put($o, "format", $trace/trace/format/string())
  )
  return
    $o
};

declare function trace:find-traces(
  $q,
  $page as xs:unsignedLong,
  $page-length as xs:unsignedLong)
{
  let $options :=
    <options xmlns="http://marklogic.com/appservices/search">
      <additional-query>{cts:collection-query("trace")}</additional-query>
      <return-results>false</return-results>
      <return-facets>true</return-facets>
      <sort-order type="xs:dateTime"
          direction="descending">
        <path-index>/trace/created</path-index>
      </sort-order>
    </options>
  let $query := search:parse($q)
  let $count := search:estimate($query, $options)
  let $start := ($page - 1) * $page-length + 1
  let $end := fn:min(($start + $page-length - 1, $count))
  let $results :=
    for $result in search:resolve-nodes($query, $options, $start, $page-length)
    return
      trace:trace-to-json-slim($result/node())
  return
    object-node {
      "start": $start,
      "end": $end,
      "total": $count,
      "page": $page,
      "pageCount": $page-length,
      "traces": json:to-array($results)
    }
};

declare function trace:get-traces($page as xs:int, $page-length as xs:int)
{
  let $start := ($page - 1) * $page-length + 1
  let $end := $start + $page-length - 1
  let $count := xdmp:estimate(/trace)
  let $traces :=
    for $trace in cts:search(/trace, cts:true-query(), ("unfiltered", cts:index-order(cts:path-reference("/trace/created"), "descending")))[$start to $end]
    return
      trace:trace-to-json($trace)
  return
    object-node {
      "start": $start,
      "end": $end,
      "total": $count,
      "page": $page,
      "pageCount": $page-length,
      "traces": json:to-array($traces)
    }
};

declare function trace:get-trace($id as xs:string)
{
  let $query :=
    cts:or-query((
      cts:path-range-query("/trace/traceId", "=", $id, ("collation=http://marklogic.com/collation/codepoint"))
    ))
  return
    trace:trace-to-json(cts:search(fn:doc(), $query)[1]/node())
};

declare function trace:get-traceIds($q as xs:string?)
{
  let $query :=
    if ($q) then
      cts:element-value-query(xs:QName("identifier"), fn:lower-case($q) || "*", "wildcarded")
    else ()
  let $results :=
    cts:value-co-occurrences(
      cts:path-reference("/trace/traceId"),
      cts:path-reference("/trace/identifier"),
      (
        "limit=10"
      ),
      $query)
  let $results :=
    for $r in $results
    return
      object-node {
        "traceId": fn:data($r/*:value[1]),
        "identifier": fn:data($r/*:value[2])
      }
  return
    json:to-array($results)
};