Могу ли я заставить XQuery вычислять {$ expression} в переменных, связанных с XQJ?

Чтобы имитировать значение автоинкремента в XQuery Update, следующее работает нормально, предполагая <root count="0"/> при первом запуске:

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

... приятно урожайность:

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

Однако я хотел бы определить узел в моем коде Java, а затем выполнить привязку это как org.w3c.dom.Node, Document или даже String. Нравится:

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

Однако это оставляет мне только текст { $count } в атрибуте. Связывание узла как значения xs:string имеет тот же эффект.

Конечно, это хорошая защита от «XQuery-инъекции». Тем не менее: есть ли способ сделать процесс обновления XQuery закрытым выражением, которое есть у меня в самих переменных?

(Любые другие разумные идеи по использованию значений автоинкремента в XQuery также очень приветствуются, но тогда см. Авто увеличить с помощью XQuery Update?)


person Arjan    schedule 02.10.2012    source источник


Ответы (2)


Кстати о внедрении ... почему бы просто не передать узел в виде строки и не использовать basex: eval ()?

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

Выше xquery: относится к модулю BaseX.

person wst    schedule 02.10.2012
comment
Ах, я не знал о basex: функциях. Это приведет к блокировке BaseX, но, возможно, это действительно правильный путь. Я предполагаю, что сначала я бы вставил пустой узел, чтобы зафиксировать id=..., а затем во втором вызове обновления базы данных, как обычно, с фактическими данными, сгенерированными пользователем, чтобы избежать инъекции. В качестве альтернативы я мог бы сначала вставить весь узел с UUID, сгенерированным Java для id=..., а затем заменить этот (известный) UUID во втором вызове базы данных. - person Arjan; 03.10.2012
comment
Этот может быть xquery:eval в наши дни? Этот модуль был представлен в Версии 7.3. Функции были заимствованы из устаревшего служебного модуля. - person Arjan; 03.10.2012
comment
Ах, действительно, basex: вещи больше нет; в примечаниях к выпуску BaseX 6.3.2 указано: [ADD] XQuery: new db: and util: functions, заменяющие basex: - person Arjan; 03.10.2012
comment
Хорошо, да, я вижу, что пространство имен функций обновлено. И, как вы заметили, будьте очень осторожны с любой реализацией eval (). - person wst; 03.10.2012

Здесь вам может помочь выражение преобразования XQuery Update. Его можно использовать для изменения существующих узлов в основной памяти. Ваш пример можно переписать следующим образом:

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
)

Внешний узел $n копируется в переменную $c, а атрибут @id корневого узла заменяется текущим значением $count.

Конечно, у этого подхода много вариантов. Вы могли бы, например, вставьте новый атрибут @id вместо замены фиктивного ..

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

К текущему синтаксису выражения преобразования нужно сначала привыкнуть. Более читаемый синтаксис может зависеть от будущих версий официальной спецификации.

person Christian Grün    schedule 03.11.2012
comment
Очень очень хорошо! Я думаю, что это действительно отвечает на Автоинкремент с обновлением XQuery? (вместо того, чтобы фактически интерпретировать XQuery заключенное выражение из оператора XQJ в моем коде Java). - person Arjan; 03.11.2012