1

To mimic an auto-increment value in XQuery Update, the following works fine, assuming <root count="0"/> when running this for the first time:

let $count := /root/@count
return (
  insert node <node id='{ $count }'/> into /root,
  replace value of node $count with $count + 1  
)

...nicely yielding:

<root count="1">
  <node id="0">
</root>

However, I'd like to define the node in my Java code, and then bind that as an org.w3c.dom.Node or Document, or even String. Like:

String expr =
     " declare variable $n external; "
   + " let $count := /root/@count; "
   + " return ( "
   + "   insert node $n into /root, "
   + "   replace value of node $count with $count + 1 "
   + " ) ";
XQConnection xqc = ...;
XQPreparedExpression xqp = xqc.prepareExpression(expr);
// org.w3c.dom.Node node is <node id='{ $count }'/>
xqp.bindNode(new QName("n"), node, null);
xqp.executeQuery();

However, this just leaves me the text { $count } in the attribute. Binding the node as an xs:string value has the same effect.

Of course, this is a nice protection against "XQuery injection". Still then: is there any way to make XQuery Update process an enclosed expression I have in the variables themselves?

(Any other smart ideas to use auto-increment values in XQuery are very welcome too, but then see Auto increment with XQuery Update?)

Community
  • 1
  • 1
Arjan
  • 22,808
  • 11
  • 61
  • 71

2 Answers2

3

The transform expression of XQuery Update may help you here. It can be used to modify existing nodes in main memory. Your example could be rewritten as follows:

declare variable $n external;

let $count := /root/@count
let $n :=
  copy $c := $n
  modify replace value of node $c/node/@id with $count
  return $c
return (
  insert node $n into /root,
  replace value of node $count with $count + 1
)

The external node $n is copied to the variable $c, and the @id attribute of the root node is replaced with the current value of $count.

Of course there are many variants to this approach. You could e.g. insert a new @id attribute instead of replacing the dummy..

  copy $c := $n
  modify insert node attribute id { $count } into $c/node
  return $c

The current syntax of the transform expression is something one needs to get used to first. A better readable syntax may be subject to future versions of the official spec.

Christian Grün
  • 6,012
  • 18
  • 34
  • Very, very nice! I think this actually answers [Auto increment with XQuery Update?](http://stackoverflow.com/questions/2561067/auto-increment-with-xquery-update) (rather than actually making XQuery interpret the enclosed expression from the XQJ statement in my Java code). – Arjan Nov 03 '12 at 18:25
2

Speaking of injection...why not just pass the node as a string and use basex:eval()?

String node = "<node id='{ $count }'/>";
String expr =
   ...
   + "   insert node xquery:eval($n) into /root, "
   ...

Above, xquery: refers to a BaseX module.

Arjan
  • 22,808
  • 11
  • 61
  • 71
wst
  • 11,681
  • 1
  • 24
  • 39
  • Ah, I didn't know about `basex:` functions. It would introduce a lock-in for BaseX, but it might indeed be the way to go. I guess I'd then first insert an empty node to get the `id=...` fixed, and then in a second call to the the database update that the regular way with the actual user-generated data, to avoid injection. Alternatively, I could also first insert the whole node with a Java-generated UUID for `id=...`, and then replace that (known) UUID in a second call to the database. – Arjan Oct 03 '12 at 05:13
  • This [might be `xquery:eval` nowadays](http://docs.basex.org/wiki/XQuery_Module)? *"This module was introduced with Version 7.3. Functions have been adopted from the obsolete Utility Module."* – Arjan Oct 03 '12 at 05:20
  • Ah, indeed the `basex:` thing is no more; the BaseX 6.3.2 release notes state: *[ADD] XQuery: new "db:" and "util:" functions, replacing "basex:"* – Arjan Oct 03 '12 at 08:48
  • Ok, yes I see the function namespace has been updated. And as you noted, be very careful with any implementation of eval(). – wst Oct 03 '12 at 14:46