Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion exist-core/src/main/antlr/org/exist/xquery/parser/XQueryTree.g
Original file line number Diff line number Diff line change
Expand Up @@ -2360,7 +2360,13 @@ throws PermissionDeniedException, EXistException, XPathException
(s.getTest().getType() == Type.ATTRIBUTE && s.getAxis() == Constants.CHILD_AXIS))
// combines descendant-or-self::node()/attribute:*
s.setAxis(Constants.DESCENDANT_ATTRIBUTE_AXIS);
else {
else if (s.getAxis() <= Constants.PRECEDING_SIBLING_AXIS) {
// Reverse axis: insert explicit descendant-or-self::node() step
LocationStep descStep = new LocationStep(context, Constants.DESCENDANT_SELF_AXIS, new TypeTest(Type.NODE));
descStep.setAbbreviated(true);
path.replaceLastExpression(descStep);
path.add(step);
} else {
s.setAxis(Constants.DESCENDANT_SELF_AXIS);
s.setAbbreviated(true);
}
Expand Down Expand Up @@ -2984,6 +2990,13 @@ throws PermissionDeniedException, EXistException, XPathException
rs.setAxis(Constants.DESCENDANT_AXIS);
} else if (rs.getAxis() == Constants.SELF_AXIS) {
rs.setAxis(Constants.DESCENDANT_SELF_AXIS);
} else if (rs.getAxis() <= Constants.PRECEDING_SIBLING_AXIS) {
// Reverse axis: cannot merge with descendant-or-self,
// insert explicit descendant-or-self::node() step before the reverse axis step
LocationStep descStep = new LocationStep(context, Constants.DESCENDANT_SELF_AXIS, new TypeTest(Type.NODE));
descStep.setAbbreviated(true);
path.replaceLastExpression(descStep);
path.add(rightStep);
} else {
rs.setAxis(Constants.DESCENDANT_SELF_AXIS);
rs.setAbbreviated(true);
Expand Down
59 changes: 59 additions & 0 deletions exist-core/src/test/java/org/exist/xquery/XPathQueryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,65 @@ public void precedingAxis() throws XMLDBException {
queryResource(service, "siblings.xml", "//a/n[. = '3']/preceding::s", 3);
}

/**
* Tests that // followed by a reverse axis correctly expands to
* /descendant-or-self::node()/ + reverse axis, rather than
* collapsing the reverse axis into descendant-or-self.
*
* @see <a href="https://github.com/eXist-db/exist/issues/691">#691</a>
*/
@Test
public void dslashWithReverseAxis() throws XMLDBException {
final String xml =
"<root>" +
" <a>" +
" <b>1</b>" +
" <b>2</b>" +
" </a>" +
" <a>" +
" <b>3</b>" +
" <b>4</b>" +
" </a>" +
"</root>";

final XQueryService service =
storeXMLStringAndGetQueryService("dslash_reverse.xml", xml);

// //preceding::b should produce the same count as the expanded form
queryAndAssert(service,
"let $d := doc('/db/test/dslash_reverse.xml') " +
"return count($d//preceding::b) eq count($d/descendant-or-self::node()/preceding::b)",
1, "//preceding::b count should match expanded form");

// //ancestor::a should produce the same count as the expanded form
queryAndAssert(service,
"let $d := doc('/db/test/dslash_reverse.xml') " +
"return count($d//ancestor::a) eq count($d/descendant-or-self::node()/ancestor::a)",
1, "//ancestor::a count should match expanded form");

// Note: //preceding-sibling::b skipped due to pre-existing NPE in
// NewArrayNodeSet.selectPrecedingSiblings when evaluating preceding-sibling
// on descendant-or-self::node() context (affects both abbreviated and expanded forms)

// //ancestor-or-self::a should produce the same count as the expanded form
queryAndAssert(service,
"let $d := doc('/db/test/dslash_reverse.xml') " +
"return count($d//ancestor-or-self::a) eq count($d/descendant-or-self::node()/ancestor-or-self::a)",
1, "//ancestor-or-self::a count should match expanded form");

// //parent::a should produce the same count as the expanded form
queryAndAssert(service,
"let $d := doc('/db/test/dslash_reverse.xml') " +
"return count($d//parent::a) eq count($d/descendant-or-self::node()/parent::a)",
1, "//parent::a count should match expanded form");

// Relative path: $node//preceding::b should match expanded form
queryAndAssert(service,
"let $node := doc('/db/test/dslash_reverse.xml')/root/a[2] " +
"return count($node//preceding::b) eq count($node/descendant-or-self::node()/preceding::b)",
1, "$node//preceding::b count should match expanded form");
}

@Test
public void position() throws XMLDBException, IOException, SAXException {

Expand Down