jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From thom...@apache.org
Subject svn commit: r1530610 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/
Date Wed, 09 Oct 2013 13:58:39 GMT
Author: thomasm
Date: Wed Oct  9 13:58:39 2013
New Revision: 1530610

URL: http://svn.apache.org/r1530610
Log:
OAK-1085 Compatibility for queries with not(child/@prop) conditions

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyInexistenceImpl.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitor.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java?rev=1530610&r1=1530609&r2=1530610&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
(original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
Wed Oct  9 13:58:39 2013
@@ -48,6 +48,7 @@ import org.apache.jackrabbit.oak.query.a
 import org.apache.jackrabbit.oak.query.ast.OrImpl;
 import org.apache.jackrabbit.oak.query.ast.OrderingImpl;
 import org.apache.jackrabbit.oak.query.ast.PropertyExistenceImpl;
+import org.apache.jackrabbit.oak.query.ast.PropertyInexistenceImpl;
 import org.apache.jackrabbit.oak.query.ast.PropertyValueImpl;
 import org.apache.jackrabbit.oak.query.ast.SameNodeImpl;
 import org.apache.jackrabbit.oak.query.ast.SameNodeJoinConditionImpl;
@@ -207,6 +208,13 @@ public class QueryImpl implements Query 
                 node.bindSelector(source);
                 return true;
             }
+            
+            @Override
+            public boolean visit(PropertyInexistenceImpl node) {
+                node.setQuery(query);
+                node.bindSelector(source);
+                return true;
+            }
 
             @Override
             public boolean visit(PropertyValueImpl node) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java?rev=1530610&r1=1530609&r2=1530610&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java
(original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java
Wed Oct  9 13:58:39 2013
@@ -42,6 +42,7 @@ import org.apache.jackrabbit.oak.query.a
 import org.apache.jackrabbit.oak.query.ast.Operator;
 import org.apache.jackrabbit.oak.query.ast.OrderingImpl;
 import org.apache.jackrabbit.oak.query.ast.PropertyExistenceImpl;
+import org.apache.jackrabbit.oak.query.ast.PropertyInexistenceImpl;
 import org.apache.jackrabbit.oak.query.ast.PropertyValueImpl;
 import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
 import org.apache.jackrabbit.oak.query.ast.SourceImpl;
@@ -399,9 +400,10 @@ public class SQL2Parser {
                 throw getSyntaxError("propertyName (NOT NULL is only supported for properties)");
             }
             PropertyValueImpl p = (PropertyValueImpl) left;
-            c = getPropertyExistence(p);
-            if (!not) {
-                c = factory.not(c);
+            if (not) {
+                c = getPropertyExistence(p);
+            } else {
+                c = getPropertyInexistence(p);
             }
         } else if (readIf("NOT")) {
             if (readIf("IS")) {
@@ -427,6 +429,10 @@ public class SQL2Parser {
     private PropertyExistenceImpl getPropertyExistence(PropertyValueImpl p) throws ParseException
{
         return factory.propertyExistence(p.getSelectorName(), p.getPropertyName());
     }
+    
+    private PropertyInexistenceImpl getPropertyInexistence(PropertyValueImpl p) throws ParseException
{
+        return factory.propertyInexistence(p.getSelectorName(), p.getPropertyName());
+    }
 
     private ConstraintImpl parseConditionFunctionIf(String functionName) throws ParseException
{
         ConstraintImpl c;

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java?rev=1530610&r1=1530609&r2=1530610&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java
(original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java
Wed Oct  9 13:58:39 2013
@@ -114,6 +114,10 @@ public class AstElementFactory {
     public PropertyExistenceImpl propertyExistence(String selectorName, String propertyName)
{
         return new PropertyExistenceImpl(selectorName, propertyName);
     }
+    
+    public PropertyInexistenceImpl propertyInexistence(String selectorName, String propertyName)
{
+        return new PropertyInexistenceImpl(selectorName, propertyName);
+    }
 
     public PropertyValueImpl propertyValue(String selectorName, String propertyName) {
         return new PropertyValueImpl(selectorName, propertyName);

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitor.java?rev=1530610&r1=1530609&r2=1530610&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitor.java
(original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitor.java
Wed Oct  9 13:58:39 2013
@@ -67,6 +67,8 @@ public interface AstVisitor {
 
     boolean visit(PropertyExistenceImpl node);
 
+    boolean visit(PropertyInexistenceImpl node);
+
     boolean visit(PropertyValueImpl node);
 
     boolean visit(SameNodeImpl node);

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyInexistenceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyInexistenceImpl.java?rev=1530610&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyInexistenceImpl.java
(added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyInexistenceImpl.java
Wed Oct  9 13:58:39 2013
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.query.ast;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.query.index.FilterImpl;
+
+/**
+ * A condition to check if the property does not exist ("is null").
+ * <p>
+ * For Jackrabbit 2.x compatibility: if the property is relative (as in
+ * "child/propertyName"), then this requires that the given child node exists.
+ */
+public class PropertyInexistenceImpl extends ConstraintImpl {
+
+    private final String selectorName;
+    private final String propertyName;
+    private SelectorImpl selector;
+
+    public PropertyInexistenceImpl(SelectorImpl selector, String selectorName, String propertyName)
{
+        this.selector = selector;
+        this.selectorName = selectorName;
+        this.propertyName = propertyName;
+    }
+    
+    public PropertyInexistenceImpl(String selectorName, String propertyName) {
+        this.selectorName = selectorName;
+        this.propertyName = propertyName;
+    }
+
+    @Override
+    public boolean evaluate() {
+        boolean relative = propertyName.indexOf('/') >= 0;
+        if (!relative) {
+            return selector.currentProperty(propertyName) == null;
+        }
+        Tree t = selector.currentTree();
+        if (t == null) {
+            return true;
+        }
+        String relativePath = PathUtils.getParentPath(propertyName);
+        String name = PathUtils.getName(propertyName);
+        for (String p : PathUtils.elements(relativePath)) {
+            if (t == null || !t.exists()) {
+                return false;
+            }
+            if (p.equals("..")) {
+                t = t.isRoot() ? null : t.getParent();
+            } else if (p.equals(".")) {
+                // same node
+            } else {
+                t = t.getChild(p);
+            }
+        }
+        return t != null && t.exists() && !t.hasProperty(name);
+    }
+
+    @Override
+    public Set<PropertyExistenceImpl> getPropertyExistenceConditions() {
+        return Collections.emptySet();
+    }
+    
+    @Override
+    public Set<SelectorImpl> getSelectors() {
+        return Collections.singleton(selector);
+    }
+    
+    @Override 
+    public Map<DynamicOperandImpl, Set<StaticOperandImpl>> getInMap() {
+        return Collections.emptyMap();
+    }  
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        return quote(selectorName) + '.' + quote(propertyName) + " is null";
+    }
+
+    public void bindSelector(SourceImpl source) {
+        selector = source.getExistingSelector(selectorName);
+    }
+
+    @Override
+    public void restrict(FilterImpl f) {
+        // we don't support covering indexes, 
+        // so there is no optimization anyway, and
+        // we need to be careful with "property IS NULL"
+        // because this might cause an index
+        // to ignore the join condition "property = x"
+        // for example in:
+        // "select * from a left outer join b on a.x = b.y
+        // where b.y is null"
+        // must not result in the index to check for
+        // "b.y is null", because that would alter the
+        // result
+    }
+
+    @Override
+    public void restrictPushDown(SelectorImpl s) {
+        if (s.outerJoinRightHandSide) {
+            // we need to be careful with "property IS NULL"
+            // because this might cause an index
+            // to ignore the join condition "property = x"
+            // for example in:
+            // "select * from a left outer join b on a.x = b.y
+            // where b.y is null"
+            // must not check for "b.y is null" too early, 
+            // because that would alter the result
+            return;
+        }
+        if (s == selector) {
+            s.restrictSelector(this);
+        }
+    }
+    
+    @Override
+    public int hashCode() {
+        return ((selectorName == null) ? 0 : selectorName.hashCode()) * 31 +
+                ((propertyName == null) ? 0 : propertyName.hashCode());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        } else if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        PropertyInexistenceImpl other = (PropertyInexistenceImpl) obj;
+        return equalsStrings(selectorName, other.selectorName) &&
+                equalsStrings(propertyName, other.propertyName);
+    }
+    
+    private static boolean equalsStrings(String a, String b) {
+        return a == null || b == null ? a == b : a.equals(b);
+    }
+
+}
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java?rev=1530610&r1=1530609&r2=1530610&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
(original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
Wed Oct  9 13:58:39 2013
@@ -315,21 +315,18 @@ public class SelectorImpl extends Source
     public String currentPath() {
         return cursor == null ? null : currentRow.getPath();
     }
-
-    public PropertyValue currentProperty(String propertyName) {
-        boolean relative = propertyName.indexOf('/') >= 0;
-        if (cursor == null) {
-            return null;
-        }
-        IndexRow r = currentRow;
-        if (r == null) {
-            return null;
-        }
-        String path = r.getPath();
+    
+    public Tree currentTree() {
+        String path = currentPath();
         if (path == null) {
             return null;
         }
-        Tree t = getTree(path);
+        return getTree(path);
+    }
+
+    public PropertyValue currentProperty(String propertyName) {
+        boolean relative = propertyName.indexOf('/') >= 0;
+        Tree t = currentTree();
         if (relative) {
             for (String p : PathUtils.elements(PathUtils.getParentPath(propertyName))) {
                 if (t == null) {
@@ -349,6 +346,7 @@ public class SelectorImpl extends Source
             return null;
         }
         if (propertyName.equals(QueryImpl.JCR_PATH)) {
+            String path = currentPath();
             String local = getLocalPath(path);
             if (local == null) {
                 // not a local path

Modified: jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java?rev=1530610&r1=1530609&r2=1530610&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java
(original)
+++ jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java
Wed Oct  9 13:58:39 2013
@@ -55,6 +55,23 @@ public class QueryTest extends AbstractR
     public QueryTest(NodeStoreFixture fixture) {
         super(fixture);
     }
+    
+    @Test
+    public void relativeNotExistsProperty() throws Exception {
+        Session session = getAdminSession();
+        Node content = session.getRootNode().addNode("test");
+        content.addNode("one").addNode("child").setProperty("prop", "hello");
+        content.addNode("two").addNode("child");
+        session.save();
+        String query = "//*[not(child/@prop)]";
+        QueryResult r = session.getWorkspace().getQueryManager().createQuery(
+                query, "xpath").execute();
+        NodeIterator it = r.getNodes();
+        assertTrue(it.hasNext());
+        String path = it.nextNode().getPath();
+        assertEquals("/test/two", path);
+        assertFalse(it.hasNext());
+    }
 
     @SuppressWarnings("deprecation")
     @Test



Mime
View raw message