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'(*)]


Reply via email to