Author: thomasm Date: Thu Dec 20 10:13:29 2012 New Revision: 1424389 URL: http://svn.apache.org/viewvc?rev=1424389&view=rev Log: OAK-309 ParentNodeTest fails (XPath parent node navigation using "..")
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java?rev=1424389&r1=1424388&r2=1424389&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java Thu Dec 20 10:13:29 2012 @@ -85,9 +85,6 @@ public class XPathToSQL2Converter { currentSelector.name = "a"; ArrayList<Expression> columnList = new ArrayList<Expression>(); - - // TODO support "..", example: - // /jcr:root/etc/.. String pathPattern = ""; boolean startOfQuery = true; @@ -225,6 +222,29 @@ public class XPathToSQL2Converter { } } } + } else if (readIf(".")) { + // just "." this is simply ignored, so that + // "a/./b" is the same as "a/b" + if (readIf(".")) { + // ".." means "the parent of the node" + // handle like a regular path restriction + String name = ".."; + pathPattern += name; + if (!currentSelector.isChild) { + currentSelector.nodeName = name; + } else { + if (currentSelector.isChild) { + currentSelector.isChild = false; + currentSelector.isParent = true; + } + } + } else { + if (selectors.size() > 0) { + currentSelector = selectors.remove(selectors.size() - 1); + currentSelector.condition = null; + currentSelector.joinCondition = null; + } + } } else { throw getSyntaxError(); } @@ -346,7 +366,7 @@ public class XPathToSQL2Converter { boolean isFirstSelector = selectors.size() == 0; String path = currentSelector.path; Expression condition = currentSelector.condition; - Expression joinCondition = currentSelector.joinCondition; + Expression joinCondition = null; if (currentSelector.nodeName != null) { Function f = new Function("name"); f.params.add(new SelectorExpr(currentSelector)); @@ -372,6 +392,15 @@ public class XPathToSQL2Converter { c.params.add(new SelectorExpr(selectors.get(selectors.size() - 1))); joinCondition = c; } + } else if (currentSelector.isParent) { + if (isFirstSelector) { + throw getSyntaxError(); + } else { + Function c = new Function("ischildnode"); + c.params.add(new SelectorExpr(selectors.get(selectors.size() - 1))); + c.params.add(new SelectorExpr(currentSelector)); + joinCondition = c; + } } else if (currentSelector.isChild) { if (isFirstSelector) { if (!path.isEmpty()) { @@ -408,7 +437,7 @@ public class XPathToSQL2Converter { Selector nextSelector = new Selector(); nextSelector.name = nextSelectorName; currentSelector.condition = condition; - currentSelector.joinCondition = joinCondition; + currentSelector.joinCondition = add(currentSelector.joinCondition, joinCondition); selectors.add(currentSelector); currentSelector = nextSelector; } @@ -417,6 +446,8 @@ public class XPathToSQL2Converter { private static Expression add(Expression old, Expression add) { if (old == null) { return add; + } else if (add == null) { + return old; } return new Condition(old, "and", add, Expression.PRECEDENCE_AND); } @@ -985,22 +1016,33 @@ public class XPathToSQL2Converter { /** * Whether this is a child node of the previous selector or a given path. + * Examples: + * <ul><li>/jcr:root/* + * </li><li>/jcr:root/test/* + * </li><li>/jcr:root/element() + * </li><li>/jcr:root/element(*) + * </li></ul> */ - // queries of the type - // jcr:root/* - // jcr:root/test/* - // jcr:root/element() - // jcr:root/element(*) boolean isChild; - + + /** + * Whether this is a parent node of the previous selector or given path. + * Examples: + * <ul><li>testroot//child/..[@foo1] + * </li><li>/jcr:root/test/descendant/..[@test] + * </li></ul> + */ + boolean isParent; + /** * Whether this is a descendant of the previous selector or a given path. + * Examples: + * <ul><li>/jcr:root//descendant + * </li><li>/jcr:root/test//descendant + * </li><li>/jcr:root[@x] + * </li><li>/jcr:root (just by itself) + * </li></ul> */ - // queries of the type - // /jcr:root//... - // /jcr:root/test//... - // /jcr:root[...] - // /jcr:root (just by itself) boolean isDescendant; /** Modified: jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt?rev=1424389&r1=1424388&r2=1424389&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt (original) +++ jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt Thu Dec 20 10:13:29 2012 @@ -23,9 +23,24 @@ # * new tests are typically be added on top, after the syntax docs # * use ascii character only +# test parent join + +commit / + "test": { "a": { "x": "1", "yes": { } }, "b": { "yes" : { } }, "c": { "x": "1", "no" : { } }} + +xpath test//yes/..[@x] +/test/a, null, /test/a + +select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from [nt:base] as a inner join [nt:base] as b on ischildnode(a, b) where name(a) = 'yes' and isdescendantnode(a, '/test') and b.[x] is not null +/test/a, null, /test/a + +explain select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from [nt:base] as a inner join [nt:base] as b on ischildnode(a, b) where name(a) = 'yes' and isdescendantnode(a, '/test') and b.[x] is not null +[nt:base] as [a] /* traverse "/test//*" where (name([a]) = cast('yes' as string)) and (isdescendantnode([a], [/test])) */ inner join [nt:base] as [b] /* traverse "//*" where [b].[x] is not null */ on ischildnode([a], [b]) + +commit / - "test" + # test spaces in identifiers -commit / + "test": { "space space": { }} +commit / + "test": { "space space": { "x": "1" }} select * from [nt:base] where issamenode([/test/space space]) /test/space space Modified: jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt?rev=1424389&r1=1424388&r2=1424389&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt (original) +++ jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt Thu Dec 20 10:13:29 2012 @@ -25,6 +25,24 @@ # jackrabbit test queries +xpath2sql testroot//child/..[@foo1] +select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from [nt:base] as a inner join [nt:base] as b on ischildnode(a, b) where name(a) = 'child' and isdescendantnode(a, '/testroot') and b.[foo1] is not null + +xpath2sql testroot//child/.[@foo1] +select [jcr:path], [jcr:score], * from [nt:base] as a where [foo1] is not null and name(a) = 'child' and isdescendantnode(a, '/testroot') + +xpath2sql testroot//child[@foo1] +select [jcr:path], [jcr:score], * from [nt:base] as a where [foo1] is not null and name(a) = 'child' and isdescendantnode(a, '/testroot') + +xpath2sql /jcr:root/testroot/node11 +select [jcr:path], [jcr:score], * from [nt:base] as a where issamenode(a, '/testroot/node11') + +xpath2sql /jcr:root/testroot/./node11 +select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from [nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where ischildnode(a, '/testroot') and name(b) = 'node11' + +xpath2sql /jcr:root/testroot/././node11 +select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from [nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where ischildnode(a, '/testroot') and name(b) = 'node11' + xpath /jcr:root/testroot//*[0] java.text.ParseException: /jcr:root/testroot//*[0] converted to SQL-2 Query: select [jcr:path], [jcr:score], * from [nt:base] as a where 0(*)is not null and isdescendantnode(a, '/testroot'); expected: NOT, ( @@ -32,22 +50,22 @@ xpath2sql /test select [jcr:path], [jcr:score], * from [nt:base] as a where name(a) = 'test' and issamenode(a, '/') xpath2sql / -invalid: Query: (*)/; expected: jcr:root, /, *, text, element, @, ( +invalid: Query: (*)/; expected: jcr:root, /, *, text, element, @, (, . xpath2sql /[@name='data'] -invalid: Query: /[(*)@name='data']; expected: jcr:root, /, *, text, element, @, ( +invalid: Query: /[(*)@name='data']; expected: jcr:root, /, *, text, element, @, (, . xpath2sql //[@name='data'] -invalid: Query: //[(*)@name='data']; expected: *, text, element, @, ( +invalid: Query: //[(*)@name='data']; expected: *, text, element, @, (, . xpath2sql //child/[@id='2.1'] -invalid: Query: //child/[(*)@id='2.1']; expected: jcr:root, /, *, text, element, @, ( +invalid: Query: //child/[(*)@id='2.1']; expected: jcr:root, /, *, text, element, @, (, . xpath2sql // -invalid: Query: /(*)/; expected: *, text, element, @, ( +invalid: Query: /(*)/; expected: *, text, element, @, (, . xpath2sql [@name='data'] -invalid: Query: [(*)@name='data']; expected: /, *, text, element, @, ( +invalid: Query: [(*)@name='data']; expected: /, *, text, element, @, (, . xpath2sql test select [jcr:path], [jcr:score], * from [nt:base] as a where issamenode(a, '/test') @@ -130,9 +148,6 @@ select [jcr:path], [jcr:score], * from [ xpath2sql /jcr:root/testroot/*[jcr:contains(., 'jackrabbit')]/rep:excerpt(.) invalid: Query: /jcr:root/testroot/*[jcr:contains(., 'jackrabbit')]/rep:excerpt((*).); expected: <end> -xpath2sql /jcr:root/testroot//child/..[@foo1] -invalid: Query: /jcr:root/testroot//child/.(*).[@foo1]; expected: jcr:root, /, *, text, element, @, ( - xpath2sql //testroot/*[@jcr:primaryType='nt:unstructured' and fn:not(@mytext)] select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from [nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where name(a) = 'testroot' and b.[jcr:primaryType] = 'nt:unstructured' and b.[mytext] is null @@ -352,7 +367,7 @@ xpath2sql //element(*, my:type)[jcr:cont select [jcr:path], [jcr:score], * from [my:type] as a where contains([my:title], 'jcr') order by score(a) desc xpath2sql [invalid/query -invalid: Query: [(*)invalid/query; expected: /, *, text, element, @, ( +invalid: Query: [(*)invalid/query; expected: /, *, text, element, @, (, . xpath2sql //element(*, my:type)[@my:value = -'x'] invalid: Query: //element(*, my:type)[@my:value = -'x'(*)]