jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From thom...@apache.org
Subject svn commit: r1302927 [3/4] - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/query/ main/java/org/apache/jackrabbit/oak/query/ast/ main/java/org/apache/jackrabbit/oak/query/index/ test/java/org/apache/jackrabbit/oak/ test/jav...
Date Tue, 20 Mar 2012 15:05:06 GMT
Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,181 @@
+/*
+ * 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 org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.mk.simple.NodeImpl;
+import org.apache.jackrabbit.oak.query.Query;
+
+public class JoinImpl extends SourceImpl {
+
+    private final JoinConditionImpl joinCondition;
+    private JoinType joinType;
+    private SourceImpl left;
+    private SourceImpl right;
+
+    private boolean leftNeedExecute, rightNeedExecute;
+    private boolean leftNeedNext;
+    private boolean foundJoinedRow;
+    private boolean end;
+    private String revisionId;
+
+    public JoinImpl(SourceImpl left, SourceImpl right, JoinType joinType,
+            JoinConditionImpl joinCondition) {
+        this.left = left;
+        this.right = right;
+        this.joinType = joinType;
+        this.joinCondition = joinCondition;
+    }
+
+    public JoinConditionImpl getJoinCondition() {
+        return joinCondition;
+    }
+
+    public String getJoinType() {
+        return joinType.toString();
+    }
+
+    public SourceImpl getLeft() {
+        return left;
+    }
+
+    public SourceImpl getRight() {
+        return right;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String getPlan() {
+        return left.getPlan() + ' ' + joinType.name() + " JOIN " + right.getPlan();
+    }
+
+    @Override
+    public String toString() {
+        return left + " " + joinType.name() + " JOIN " + right;
+    }
+
+    @Override
+    public void init(Query qom) {
+        switch (joinType) {
+        case INNER:
+            left.setQueryConstraint(queryConstraint);
+            right.setQueryConstraint(queryConstraint);
+            right.setJoinCondition(joinCondition);
+            left.init(qom);
+            right.init(qom);
+            break;
+        case LEFT_OUTER:
+            left.setQueryConstraint(queryConstraint);
+            right.setOuterJoin(true);
+            right.setQueryConstraint(queryConstraint);
+            right.setJoinCondition(joinCondition);
+            left.init(qom);
+            right.init(qom);
+            break;
+        case RIGHT_OUTER:
+            right.setQueryConstraint(queryConstraint);
+            left.setOuterJoin(true);
+            left.setQueryConstraint(queryConstraint);
+            left.setJoinCondition(joinCondition);
+            right.init(qom);
+            left.init(qom);
+            // TODO right outer join: verify whether converting
+            // to left outer join is always correct (given the current restrictions)
+            joinType = JoinType.LEFT_OUTER;
+            // swap left and right
+            SourceImpl temp = left;
+            left = right;
+            right = temp;
+            break;
+        }
+    }
+
+    @Override
+    public void prepare(MicroKernel mk) {
+        left.prepare(mk);
+        right.prepare(mk);
+    }
+
+    @Override
+    public SelectorImpl getSelector(String selectorName) {
+        SelectorImpl s = left.getSelector(selectorName);
+        if (s == null) {
+            s = right.getSelector(selectorName);
+        }
+        return s;
+    }
+
+    @Override
+    public void execute(String revisionId) {
+        this.revisionId = revisionId;
+        leftNeedExecute = true;
+        end = false;
+    }
+
+    @Override
+    public boolean next() {
+        if (end) {
+            return false;
+        }
+        if (leftNeedExecute) {
+            left.execute(revisionId);
+            leftNeedExecute = false;
+            leftNeedNext = true;
+        }
+        while (true) {
+            if (leftNeedNext) {
+                if (!left.next()) {
+                    end = true;
+                    return false;
+                }
+                leftNeedNext = false;
+                rightNeedExecute = true;
+            }
+            if (rightNeedExecute) {
+                right.execute(revisionId);
+                foundJoinedRow = false;
+                rightNeedExecute = false;
+            }
+            if (!right.next()) {
+                leftNeedNext = true;
+            } else {
+                if (joinCondition.evaluate()) {
+                    foundJoinedRow = true;
+                    return true;
+                }
+            }
+            // for an outer join, if no matching result was found,
+            // one row returned (with all values set to null)
+            if (right.outerJoin && leftNeedNext && !foundJoinedRow) {
+                return true;
+            }
+        }
+    }
+
+    @Override
+    public String currentPath() {
+        // TODO
+        return left.currentPath();
+    }
+
+    @Override
+    public NodeImpl currentNode() {
+        return null;
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinType.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinType.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinType.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinType.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+/**
+ * Enumeration of the join types.
+ */
+public enum JoinType {
+
+    INNER("INNER JOIN"),
+
+    LEFT_OUTER("LEFT OUTER JOIN"),
+
+    RIGHT_OUTER("RIGHT OUTER JOIN");
+
+    /**
+     * The name of this join type.
+     */
+    private final String name;
+
+    JoinType(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Returns the join type.
+     */
+    @Override
+    public String toString() {
+        return name;
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,62 @@
+/*
+ * 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 org.apache.jackrabbit.oak.query.Value;
+import org.apache.jackrabbit.oak.query.index.Filter;
+
+public class LengthImpl extends DynamicOperandImpl {
+
+    private final PropertyValueImpl propertyValue;
+
+    public LengthImpl(PropertyValueImpl propertyValue) {
+        this.propertyValue = propertyValue;
+    }
+
+    public PropertyValueImpl getPropertyValue() {
+        return propertyValue;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        return "LENGTH(" + getPropertyValue() + ')';
+    }
+
+    @Override
+    public Value currentValue() {
+        Value v = propertyValue.currentValue();
+        if (v == null) {
+            return null;
+        }
+        String value = v.getString();
+        return query.getValueFactory().createValue(value.length());
+    }
+
+    @Override
+    public void apply(Filter f, Operator operator, Value v) {
+        // ignore
+        // TODO LENGTH(x) conditions: can use IS NOT NULL?
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LiteralImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LiteralImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LiteralImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LiteralImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,86 @@
+/*
+ * 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 org.apache.jackrabbit.oak.query.SQL2Parser;
+import org.apache.jackrabbit.oak.query.PropertyType;
+import org.apache.jackrabbit.oak.query.Value;
+
+public class LiteralImpl extends StaticOperandImpl {
+
+    private final Value value;
+
+    public LiteralImpl(Value value) {
+        this.value = value;
+    }
+
+    public Value getLiteralValue() {
+        return value;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        switch (value.getType()) {
+        case PropertyType.BINARY:
+            return cast("BINARY");
+        case PropertyType.BOOLEAN:
+            return cast("BOOLEAN");
+        case PropertyType.DATE:
+            return cast("DATE");
+        case PropertyType.DECIMAL:
+            return cast("DECIMAL");
+        case PropertyType.DOUBLE:
+        case PropertyType.LONG:
+            return value.getString();
+        case PropertyType.NAME:
+            return cast("NAME");
+        case PropertyType.PATH:
+            return cast("PATH");
+        case PropertyType.REFERENCE:
+            return cast("REFERENCE");
+        case PropertyType.STRING:
+            return escape();
+        case PropertyType.URI:
+            return cast("URI");
+        case PropertyType.WEAKREFERENCE:
+            return cast("WEAKREFERENCE");
+        default:
+            return escape();
+        }
+    }
+
+    private String cast(String type) {
+        return "CAST(" + escape() + " AS " + type + ')';
+    }
+
+    private String escape() {
+        return SQL2Parser.escapeStringLiteral(value.getString());
+    }
+
+    @Override
+    Value currentValue() {
+        return value;
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,62 @@
+/*
+ * 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 org.apache.jackrabbit.oak.query.Value;
+import org.apache.jackrabbit.oak.query.index.Filter;
+
+public class LowerCaseImpl extends DynamicOperandImpl {
+
+    private final DynamicOperandImpl operand;
+
+    public LowerCaseImpl(DynamicOperandImpl operand) {
+        this.operand = operand;
+    }
+
+    public DynamicOperandImpl getOperand() {
+        return operand;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        return "LOWER(" + operand + ')';
+    }
+
+    @Override
+    public Value currentValue() {
+        Value v = operand.currentValue();
+        if (v == null) {
+            return null;
+        }
+        String value = v.getString();
+        return query.getValueFactory().createValue(value.toLowerCase());
+    }
+
+    @Override
+    public void apply(Filter f, Operator operator, Value v) {
+        // ignore
+        // TODO UPPER(x) conditions: can use IS NOT NULL?
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,69 @@
+/*
+ * 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 org.apache.jackrabbit.mk.util.PathUtils;
+import org.apache.jackrabbit.oak.query.Value;
+import org.apache.jackrabbit.oak.query.index.Filter;
+
+public class NodeLocalNameImpl extends DynamicOperandImpl {
+
+    private final String selectorName;
+    private SelectorImpl selector;
+
+    public NodeLocalNameImpl(String selectorName) {
+        this.selectorName = selectorName;
+    }
+
+    public String getSelectorName() {
+        return selectorName;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        return "LOCALNAME(" + getSelectorName() + ')';
+    }
+
+    public void bindSelector(SourceImpl source) {
+        selector = source.getSelector(selectorName);
+        if (selector == null) {
+            throw new RuntimeException("Unknown selector: " + selectorName);
+        }
+    }
+
+    @Override
+    public  Value currentValue() {
+        String name = PathUtils.getName(selector.currentPath());
+        int colon = name.indexOf(':');
+        // TODO LOCALNAME: evaluation of local name might not be correct
+        String localName = colon < 0 ? name : name.substring(colon + 1);
+        return query.getValueFactory().createValue(localName);
+    }
+
+    @Override
+    public void apply(Filter f, Operator operator, Value v) {
+        // TODO support LOCALNAME index conditions
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,66 @@
+/*
+ * 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 org.apache.jackrabbit.mk.util.PathUtils;
+import org.apache.jackrabbit.oak.query.Value;
+import org.apache.jackrabbit.oak.query.index.Filter;
+
+public class NodeNameImpl extends DynamicOperandImpl {
+
+    private final String selectorName;
+    private SelectorImpl selector;
+
+    public NodeNameImpl(String selectorName) {
+        this.selectorName = selectorName;
+    }
+
+    public String getSelectorName() {
+        return selectorName;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        return "NAME(" + getSelectorName() + ')';
+    }
+
+    public void bindSelector(SourceImpl source) {
+        selector = source.getSelector(selectorName);
+        if (selector == null) {
+            throw new RuntimeException("Unknown selector: " + selectorName);
+        }
+    }
+
+    @Override
+    public  Value currentValue() {
+        String name = PathUtils.getName(selector.currentPath());
+        return query.getValueFactory().createValue(name);
+    }
+
+    @Override
+    public void apply(Filter f, Operator operator, Value v) {
+        // TODO support NAME(..) index conditions
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NotImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NotImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NotImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NotImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,56 @@
+/*
+ * 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 org.apache.jackrabbit.oak.query.index.Filter;
+
+public class NotImpl extends ConstraintImpl {
+
+    private final ConstraintImpl constraint;
+
+    public NotImpl(ConstraintImpl constraint) {
+        this.constraint = constraint;
+    }
+
+    public ConstraintImpl getConstraint() {
+        return constraint;
+    }
+
+    @Override
+    public boolean evaluate() {
+        return !constraint.evaluate();
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        return "NOT " + protect(constraint);
+    }
+
+    @Override
+    public void apply(Filter f) {
+        // ignore
+        // TODO convert NOT conditions
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/Operator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/Operator.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/Operator.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/Operator.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+/**
+ * Enumeration of operators.
+ */
+public enum Operator {
+
+    EQ("="),
+
+    NE("<>"),
+
+    GT(">"),
+
+    GE(">="),
+
+    LT("<"),
+
+    LE("<="),
+
+    LIKE("like");
+
+    /**
+     * The name of this operator.
+     */
+    private final String name;
+
+    Operator(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Returns the name of this query operator.
+     */
+    @Override
+    public String toString() {
+        return name;
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,62 @@
+/*
+ * 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 org.apache.jackrabbit.oak.query.index.Filter;
+
+public class OrImpl extends ConstraintImpl {
+
+    private final ConstraintImpl constraint1;
+    private final ConstraintImpl constraint2;
+
+    public OrImpl(ConstraintImpl constraint1, ConstraintImpl constraint2) {
+        this.constraint1 = constraint1;
+        this.constraint2 = constraint2;
+    }
+
+    public ConstraintImpl getConstraint1() {
+        return constraint1;
+    }
+
+    public ConstraintImpl getConstraint2() {
+        return constraint2;
+    }
+
+    @Override
+    public boolean evaluate() {
+        return constraint1.evaluate() || constraint2.evaluate();
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        return protect(constraint1) + " OR " + protect(constraint2);
+    }
+
+    @Override
+    public void apply(Filter f) {
+        // ignore
+        // TODO convert OR conditions to UNION
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/Order.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/Order.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/Order.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/Order.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+/**
+ * Enumeration of query orders.
+ */
+public enum Order {
+
+    ASCENDING("ASC"),
+
+    DESCENDING("DESC");
+
+    /**
+     * The name of this order.
+     */
+    private final String name;
+
+    Order(String name) {
+        this.name = name;
+    }
+
+    /**
+     * @return the name of this order.
+     */
+    public String getName() {
+        return name;
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrderingImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrderingImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrderingImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrderingImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+public class OrderingImpl extends AstElement {
+
+    private final DynamicOperandImpl operand;
+    private final Order order;
+
+    public OrderingImpl(DynamicOperandImpl operand, Order order) {
+        this.operand = operand;
+        this.order = order;
+    }
+
+    public DynamicOperandImpl getOperand() {
+        return operand;
+    }
+
+    public String getOrder() {
+        return order.getName();
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        if (order == Order.ASCENDING) {
+            return operand + " ASC";
+        } else {
+            return operand + " DESC";
+        }
+    }
+
+    public boolean isDescending() {
+        return order == Order.DESCENDING;
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,74 @@
+/*
+ * 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 org.apache.jackrabbit.oak.query.Value;
+import org.apache.jackrabbit.oak.query.index.Filter;
+
+public class PropertyExistenceImpl extends ConstraintImpl {
+
+    private final String selectorName;
+    private final String propertyName;
+    private SelectorImpl selector;
+
+    public PropertyExistenceImpl(String selectorName, String propertyName) {
+        this.selectorName = selectorName;
+        this.propertyName = propertyName;
+    }
+
+    public String getPropertyName() {
+        return propertyName;
+    }
+
+    public String getSelectorName() {
+        return selectorName;
+    }
+
+    @Override
+    public boolean evaluate() {
+        Value v = selector.currentProperty(propertyName);
+        return v != null;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        // TODO quote property names?
+        return getSelectorName() + '.' + propertyName + " IS NOT NULL";
+    }
+
+    public void bindSelector(SourceImpl source) {
+        selector = source.getSelector(selectorName);
+        if (selector == null) {
+            throw new RuntimeException("Unknown selector: " + selectorName);
+        }
+    }
+
+    @Override
+    public void apply(Filter f) {
+        if (f.getSelector() == selector) {
+            f.restrictProperty(propertyName, Operator.NE, (Value) null);
+        }
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,73 @@
+/*
+ * 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 org.apache.jackrabbit.oak.query.Value;
+import org.apache.jackrabbit.oak.query.index.Filter;
+
+public class PropertyValueImpl extends DynamicOperandImpl {
+
+    private final String selectorName;
+    private final String propertyName;
+    private SelectorImpl selector;
+
+    public PropertyValueImpl(String selectorName, String propertyName) {
+        this.selectorName = selectorName;
+        this.propertyName = propertyName;
+    }
+
+    public String getSelectorName() {
+        return selectorName;
+    }
+
+    public String getPropertyName() {
+        return propertyName;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        // TODO quote property names?
+        return getSelectorName() + '.' + propertyName;
+    }
+
+    @Override
+    public Value currentValue() {
+        return selector.currentProperty(propertyName);
+    }
+
+    public void bindSelector(SourceImpl source) {
+        selector = source.getSelector(selectorName);
+        if (selector == null) {
+            throw new RuntimeException("Unknown selector: " + selectorName);
+        }
+    }
+
+    @Override
+    public void apply(Filter f, Operator operator, Value v) {
+        if (f.getSelector() == selector) {
+            f.restrictProperty(propertyName, operator, v);
+        }
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,72 @@
+/*
+ * 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 org.apache.jackrabbit.oak.query.index.Filter;
+import org.apache.jackrabbit.oak.query.index.Filter.PathRestriction;
+
+public class SameNodeImpl extends ConstraintImpl {
+
+    private final String path;
+    private final String selectorName;
+    private SelectorImpl selector;
+
+    public SameNodeImpl(String selectorName, String path) {
+        this.selectorName = selectorName;
+        this.path = path;
+    }
+
+    public String getSelectorName() {
+        return selectorName;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    @Override
+    public boolean evaluate() {
+        return selector.currentPath().equals(path);
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        return "ISSAMENODE(" + getSelectorName() + ", " + quotePath(path) + ')';
+    }
+
+    public void bindSelector(SourceImpl source) {
+        selector = source.getSelector(selectorName);
+        if (selector == null) {
+            throw new RuntimeException("Unknown selector: " + selectorName);
+        }
+    }
+
+    @Override
+    public void apply(Filter f) {
+        if (f.getSelector() == selector) {
+            f.restrictPath(path, PathRestriction.EXACT);
+        }
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeJoinConditionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeJoinConditionImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeJoinConditionImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeJoinConditionImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,101 @@
+/*
+ * 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 org.apache.jackrabbit.oak.query.index.Filter;
+import org.apache.jackrabbit.oak.query.index.Filter.PathRestriction;
+
+public class SameNodeJoinConditionImpl extends JoinConditionImpl {
+
+    private final String selector1Name;
+    private final String selector2Name;
+    private final String selector2Path;
+    private SelectorImpl selector1;
+    private SelectorImpl selector2;
+
+    public SameNodeJoinConditionImpl(String selector1Name, String selector2Name,
+            String selector2Path) {
+        this.selector1Name = selector1Name;
+        this.selector2Name = selector2Name;
+        this.selector2Path = selector2Path;
+    }
+
+    public String getSelector1Name() {
+        return selector1Name;
+    }
+
+    public String getSelector2Name() {
+        return selector2Name;
+    }
+
+    public String getSelector2Path() {
+        return selector2Path;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("ISSAMENODE(");
+        builder.append(getSelector1Name());
+        builder.append(", ");
+        builder.append(getSelector2Name());
+        if (selector2Path != null) {
+            builder.append(", ");
+            builder.append(quotePath(selector2Path));
+        }
+        builder.append(')');
+        return builder.toString();
+    }
+
+    public void bindSelector(SourceImpl source) {
+        selector1 = source.getSelector(selector1Name);
+        if (selector1 == null) {
+            throw new RuntimeException("Unknown selector: " + selector1Name);
+        }
+        selector2 = source.getSelector(selector2Name);
+        if (selector2 == null) {
+            throw new RuntimeException("Unknown selector: " + selector2Name);
+        }
+    }
+
+    @Override
+    public boolean evaluate() {
+        String p1 = selector1.currentPath();
+        String p2 = selector2.currentPath();
+        return p1.equals(p2);
+    }
+
+    @Override
+    public void apply(Filter f) {
+        String p1 = selector1.currentPath();
+        String p2 = selector2.currentPath();
+        if (f.getSelector() == selector1) {
+            f.restrictPath(p2, PathRestriction.EXACT);
+        }
+        if (f.getSelector() == selector2) {
+            f.restrictPath(p1, PathRestriction.EXACT);
+        }
+    }
+
+}

Added: 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=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,141 @@
+/*
+ * 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 org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.mk.json.JsopTokenizer;
+import org.apache.jackrabbit.mk.simple.NodeImpl;
+import org.apache.jackrabbit.oak.query.Query;
+import org.apache.jackrabbit.oak.query.Value;
+import org.apache.jackrabbit.oak.query.index.Cursor;
+import org.apache.jackrabbit.oak.query.index.Filter;
+import org.apache.jackrabbit.oak.query.index.NodeReader;
+import org.apache.jackrabbit.oak.query.index.TraversingReader;
+
+public class SelectorImpl extends SourceImpl {
+
+    private static final String PATH = "jcr:path";
+
+    protected NodeReader reader;
+
+    private final String nodeTypeName, selectorName;
+    private Cursor cursor;
+
+    public SelectorImpl(String nodeTypeName, String selectorName) {
+        this.nodeTypeName = nodeTypeName;
+        this.selectorName = selectorName;
+    }
+
+    public String getNodeTypeName() {
+        return nodeTypeName;
+    }
+
+    public String getSelectorName() {
+        return selectorName;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        // TODO quote nodeTypeName?
+        return nodeTypeName + " AS " + getSelectorName();
+    }
+
+
+    @Override
+    public void prepare(MicroKernel mk) {
+        reader = new TraversingReader(mk);
+    }
+
+    @Override
+    public void execute(String revisionId) {
+        cursor = reader.query(createFilter(), revisionId);
+    }
+
+    @Override
+    public String getPlan() {
+        return  nodeTypeName + " AS " + getSelectorName() + " /* " + reader.getPlan(createFilter()) + " */";
+    }
+
+    private Filter createFilter() {
+        Filter f = new Filter(this);
+        if (joinCondition != null) {
+            joinCondition.apply(f);
+        }
+        if (!outerJoin) {
+            // for outer joins, query constraints can't be applied to the
+            // filter, because that would alter the result
+            if (queryConstraint != null) {
+                queryConstraint.apply(f);
+            }
+        }
+        return f;
+    }
+
+    @Override
+    public boolean next() {
+        return cursor == null ? false : cursor.next();
+    }
+
+    @Override
+    public String currentPath() {
+        return cursor == null ? null : cursor.currentPath();
+    }
+
+    @Override
+    public NodeImpl currentNode() {
+        return cursor == null ? null : cursor.currentNode();
+    }
+
+    public Value currentProperty(String propertyName) {
+        if (propertyName.equals(PATH)) {
+            String p = currentPath();
+            return p == null ? null : query.getValueFactory().createValue(p);
+        }
+        NodeImpl n = currentNode();
+        if (n == null) {
+            return null;
+        }
+        String value = n.getProperty(propertyName);
+        if (value == null) {
+            return null;
+        }
+        // TODO data type mapping
+        value = JsopTokenizer.decodeQuoted(value);
+        return query.getValueFactory().createValue(value);
+    }
+
+    @Override
+    public void init(Query qom) {
+        // nothing to do
+    }
+
+    @Override
+    public SelectorImpl getSelector(String selectorName) {
+        if (selectorName.equals(this.selectorName)) {
+            return this;
+        }
+        return null;
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,60 @@
+/*
+ * 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 org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.mk.simple.NodeImpl;
+import org.apache.jackrabbit.oak.query.Query;
+
+public abstract class SourceImpl extends AstElement {
+
+    protected ConstraintImpl queryConstraint;
+    protected JoinConditionImpl joinCondition;
+    protected boolean join;
+    protected boolean outerJoin;
+
+    public void setQueryConstraint(ConstraintImpl queryConstraint) {
+        this.queryConstraint = queryConstraint;
+    }
+
+    public void setJoinCondition(JoinConditionImpl joinCondition) {
+        this.joinCondition = joinCondition;
+    }
+
+    public void setOuterJoin(boolean outerJoin) {
+        this.outerJoin = outerJoin;
+    }
+
+    public abstract void init(Query qom);
+
+    public abstract SelectorImpl getSelector(String selectorName);
+
+    public abstract String getPlan();
+
+    public abstract void prepare(MicroKernel mk);
+
+    public abstract void execute(String revisionId);
+
+    public abstract boolean next();
+
+    public abstract String currentPath();
+
+    public abstract NodeImpl currentNode();
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/StaticOperandImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/StaticOperandImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/StaticOperandImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/StaticOperandImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,27 @@
+/*
+ * 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 org.apache.jackrabbit.oak.query.Value;
+
+public abstract class StaticOperandImpl extends AstElement {
+
+    abstract Value currentValue();
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,62 @@
+/*
+ * 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 org.apache.jackrabbit.oak.query.Value;
+import org.apache.jackrabbit.oak.query.index.Filter;
+
+public class UpperCaseImpl extends DynamicOperandImpl {
+
+    private final DynamicOperandImpl operand;
+
+    public UpperCaseImpl(DynamicOperandImpl operand) {
+        this.operand = operand;
+    }
+
+    public DynamicOperandImpl getOperand() {
+        return operand;
+    }
+
+    @Override
+    boolean accept(AstVisitor v) {
+        return v.visit(this);
+    }
+
+    @Override
+    public String toString() {
+        return "UPPER(" + operand + ')';
+    }
+
+    @Override
+    public Value currentValue() {
+        Value v = operand.currentValue();
+        if (v == null) {
+            return null;
+        }
+        String value = v.getString();
+        return query.getValueFactory().createValue(value.toUpperCase());
+    }
+
+    @Override
+    public void apply(Filter f, Operator operator, Value v) {
+        // ignore
+        // TODO UPPER(x) conditions: can use IS NOT NULL?
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/Cursor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/Cursor.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/Cursor.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/Cursor.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,49 @@
+/*
+ * 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.index;
+
+import org.apache.jackrabbit.mk.simple.NodeImpl;
+
+/**
+ * A cursor to read a number of nodes sequentially.
+ */
+public interface Cursor {
+
+    /**
+     * Skip to the next node if one is available.
+     *
+     * @return true if another row is available
+     */
+    boolean next();
+
+    /**
+     * The path of the current node.
+     *
+     * @return the path
+     */
+    String currentPath();
+
+    /**
+     * The current node.
+     *
+     * @return the node
+     */
+    NodeImpl currentNode();
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/Filter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/Filter.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/Filter.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/Filter.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,369 @@
+/*
+ * 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.index;
+
+import java.util.HashMap;
+import org.apache.jackrabbit.mk.util.PathUtils;
+import org.apache.jackrabbit.oak.query.Value;
+import org.apache.jackrabbit.oak.query.ast.Operator;
+import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
+
+/**
+ * An index filter / lookup condition.
+ */
+public class Filter {
+
+    /**
+     * The selector this filter applies to.
+     */
+    private final SelectorImpl selector;
+
+    /**
+     * Whether the filter is always false.
+     */
+    private boolean alwaysFalse;
+
+    /**
+     *  The path, or "/" (the root node, meaning no filter) if not set.
+     */
+    private String path = "/";
+
+    /**
+     * The path restriction type.
+     */
+    public static enum PathRestriction {
+
+        /**
+         * A parent of this node
+         */
+        PARENT("/.."),
+
+        /**
+         * This exact node only.
+         */
+        EXACT(""),
+
+        /**
+         * All direct child nodes.
+         */
+        DIRECT_CHILDREN("/*"),
+
+        /**
+         * All direct and indirect child nodes.
+         */
+        ALL_CHILDREN("//*");
+
+        private String name;
+
+        PathRestriction(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+
+    }
+
+    private PathRestriction pathRestriction = PathRestriction.ALL_CHILDREN;
+
+    /**
+     *  The node type, or null if not set.
+     */
+    private String nodeType;
+
+    /**
+     *  The value prefix, or null if not set.
+     */
+    private String valuePrefix;
+
+    private HashMap<String, PropertyValueCondition> propertyRanges = new HashMap<String, PropertyValueCondition>();
+
+    static class PropertyValueCondition {
+
+        /**
+         * The name of the property.
+         */
+        public String propertyName;
+
+        /**
+         * The first value to read, or null to read from the beginning.
+         */
+        public String first;
+
+        /**
+         * Whether only values bigger than the first value should be returned.
+         */
+        public boolean firstExcluding;
+
+        /**
+         * The last value to read, or null to read until the end.
+         */
+        public String last;
+
+        /**
+         * Whether only values smaller than the last value should be returned.
+         */
+        public boolean lastExcluding;
+
+    }
+
+    /**
+     * Only return distinct values.
+     */
+    private boolean distinct;
+
+    // TODO support "order by"
+
+    public Filter(SelectorImpl selector) {
+        this.selector = selector;
+    }
+
+    /**
+     * Get the path.
+     */
+    public String getPath() {
+        return path;
+    }
+
+    public PathRestriction getPathRestriction() {
+        return pathRestriction;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public String getNodeType() {
+        return nodeType;
+    }
+
+    public void setNodeType(String nodeType) {
+        this.nodeType = nodeType;
+    }
+
+    public String getValuePrefix() {
+        return valuePrefix;
+    }
+
+    public void setValuePrefix(String valuePrefix) {
+        this.valuePrefix = valuePrefix;
+    }
+
+    public boolean isDistinct() {
+        return distinct;
+    }
+
+    public void setDistinct(boolean distinct) {
+        this.distinct = distinct;
+    }
+
+    public void setAlwaysFalse() {
+        propertyRanges.clear();
+        valuePrefix = "none";
+        nodeType = "none";
+        path = "none";
+        pathRestriction = PathRestriction.EXACT;
+        alwaysFalse = true;
+    }
+
+    public boolean isAlwaysFalse() {
+        return alwaysFalse;
+    }
+
+    public SelectorImpl getSelector() {
+        return selector;
+    }
+
+    public void restrictProperty(String propertyName, Operator op, Value value) {
+        restrictProperty(propertyName, op, value == null ? null : value.getString());
+    }
+
+    public boolean testPath(String path) {
+        if (isAlwaysFalse()) {
+            return false;
+        }
+        switch (pathRestriction) {
+        case EXACT:
+            return path.matches(this.path);
+        case PARENT:
+            return PathUtils.isAncestor(path, this.path);
+        case DIRECT_CHILDREN:
+            return PathUtils.getParentPath(path).equals(this.path);
+        case ALL_CHILDREN:
+            return PathUtils.isAncestor(this.path, path);
+        default:
+            throw new RuntimeException("Unknown path restriction: " + pathRestriction);
+        }
+    }
+
+    public void restrictProperty(String propertyName, Operator op, String value) {
+        PropertyValueCondition x = propertyRanges.get(propertyName);
+        if (x == null) {
+            x = new PropertyValueCondition();
+            x.propertyName = propertyName;
+            propertyRanges.put(propertyName, x);
+        }
+        String oldFirst = x.first, oldLast = x.last;
+        switch (op) {
+        case EQ:
+            x.first = maxValue(oldFirst, value);
+            x.firstExcluding = false;
+            x.last = minValue(oldLast, value);
+            x.lastExcluding = false;
+            break;
+        case NE:
+            // not null
+            break;
+        case GT:
+            x.first = maxValue(oldFirst, value);
+            x.firstExcluding = true;
+            break;
+        case GE:
+            x.first = maxValue(oldFirst, value);
+            x.firstExcluding = x.first == oldFirst ? x.firstExcluding : false;
+            break;
+        case LT:
+            x.last = minValue(oldLast, value);
+            x.lastExcluding = true;
+            break;
+        case LE:
+            x.last = minValue(oldLast, value);
+            x.lastExcluding = x.last == oldLast ? x.lastExcluding : false;
+            break;
+        case LIKE:
+            throw new RuntimeException("LIKE is not supported");
+        }
+    }
+
+    static String maxValue(String a, String b) {
+        if (a == null) {
+            return b;
+        }
+        return a.compareTo(b) < 0 ? b : a;
+    }
+
+    static String minValue(String a, String b) {
+        if (a == null) {
+            return b;
+        }
+        return a.compareTo(b) < 0 ? a : b;
+    }
+
+    public void restrictPath(String addedPath, PathRestriction addedPathRestriction) {
+        // calculating the intersection of path restrictions
+        // this is ugly code, but I don't currently see a radically simpler method
+        switch (addedPathRestriction) {
+        case PARENT:
+            switch (pathRestriction) {
+            case PARENT:
+                // ignore as it's fast anyway
+                // (would need to loop to find a common ancestor)
+                break;
+            case EXACT:
+            case ALL_CHILDREN:
+            case DIRECT_CHILDREN:
+                if (!PathUtils.isAncestor(path, addedPath)) {
+                    setAlwaysFalse();
+                }
+                break;
+            }
+            pathRestriction = PathRestriction.PARENT;
+            path = addedPath;
+            break;
+        case EXACT:
+            switch (pathRestriction) {
+            case PARENT:
+                if (!PathUtils.isAncestor(addedPath, path)) {
+                    setAlwaysFalse();
+                }
+                break;
+            case EXACT:
+                if (!addedPath.equals(path)) {
+                    setAlwaysFalse();
+                }
+                break;
+            case ALL_CHILDREN:
+                if (!PathUtils.isAncestor(path, addedPath)) {
+                    setAlwaysFalse();
+                }
+                break;
+            case DIRECT_CHILDREN:
+                if (!PathUtils.getParentPath(addedPath).equals(path)) {
+                    setAlwaysFalse();
+                }
+                break;
+            }
+            path = addedPath;
+            pathRestriction = PathRestriction.EXACT;
+            break;
+        case ALL_CHILDREN:
+            switch (pathRestriction) {
+            case PARENT:
+            case EXACT:
+                if (!PathUtils.isAncestor(addedPath, path)) {
+                    setAlwaysFalse();
+                }
+                break;
+            case ALL_CHILDREN:
+                if (PathUtils.isAncestor(path, addedPath)) {
+                    path = addedPath;
+                } else if (!path.equals(addedPath) && !PathUtils.isAncestor(addedPath, path)) {
+                    setAlwaysFalse();
+                }
+                break;
+            case DIRECT_CHILDREN:
+                if (!path.equals(addedPath) && !PathUtils.isAncestor(addedPath, path)) {
+                    setAlwaysFalse();
+                }
+                break;
+            }
+            break;
+        case DIRECT_CHILDREN:
+            switch (pathRestriction) {
+            case PARENT:
+                if (!PathUtils.isAncestor(addedPath, path)) {
+                    setAlwaysFalse();
+                }
+                break;
+            case EXACT:
+                if (!PathUtils.getParentPath(path).equals(addedPath)) {
+                    setAlwaysFalse();
+                }
+                break;
+            case ALL_CHILDREN:
+                if (!path.equals(addedPath) && !PathUtils.isAncestor(path, addedPath)) {
+                    setAlwaysFalse();
+                } else {
+                    path = addedPath;
+                    pathRestriction = PathRestriction.DIRECT_CHILDREN;
+                }
+                break;
+            case DIRECT_CHILDREN:
+                if (!path.equals(addedPath)) {
+                    setAlwaysFalse();
+                }
+                break;
+            }
+            break;
+        }
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/NodeReader.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/NodeReader.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/NodeReader.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/NodeReader.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,54 @@
+/*
+ * 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.index;
+
+/**
+ * A node reader. The reader should use the data in the filter if possible to
+ * speed up reading.
+ */
+public interface NodeReader {
+
+    /**
+     * Estimate the cost to use this reader with the given filter. The returned
+     * cost is a value between 1 (very fast; lookup of a unique node) and the
+     * estimated number of nodes to traverse.
+     *
+     * @param filter the filter
+     * @return the estimated cost in number of read nodes
+     */
+    double getCost(Filter filter);
+
+    /**
+     * Start reading nodes.
+     *
+     * @param filter the filter
+     * @param revisionId the revision
+     * @return a cursor to iterate over the result
+     */
+    Cursor query(Filter filter, String revisionId);
+
+    /**
+     * Get the query plan for the given reader.
+     *
+     * @param filter the filter
+     * @return the query plan
+     */
+    String getPlan(Filter filter);
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingCursor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingCursor.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingCursor.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingCursor.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,118 @@
+/*
+ * 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.index;
+
+import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.mk.simple.NodeImpl;
+import org.apache.jackrabbit.mk.util.PathUtils;
+import java.util.ArrayList;
+
+/**
+ * A cursor that reads all nodes in a given subtree.
+ */
+public class TraversingCursor implements Cursor {
+
+    private final MicroKernel mk;
+    private final String revisionId;
+    private final int childBlockSize;
+
+    private ArrayList<NodeCursor> nodes = new ArrayList<NodeCursor>();
+    private String currentPath;
+    private NodeImpl currentNode;
+
+    public TraversingCursor(MicroKernel mk, String revisionId, int childBlockSize, String path) {
+        this.mk = mk;
+        this.revisionId = revisionId;
+        this.childBlockSize = childBlockSize;
+        currentPath = path;
+    }
+
+    private boolean loadChildren(String path, long offset) {
+        String s = mk.getNodes(path, revisionId, 0, offset, childBlockSize, null);
+        NodeCursor c = new NodeCursor();
+        c.node = NodeImpl.parse(s);
+        c.node.setPath(path);
+        c.pos = offset;
+        nodes.add(c);
+        String child = c.node.getChildNodeName(0);
+        return child != null;
+    }
+
+    @Override
+    public NodeImpl currentNode() {
+        if (currentNode == null) {
+            if (currentPath == null) {
+                return null;
+            }
+            currentNode = NodeImpl.parse(mk.getNodes(currentPath, revisionId));
+            currentNode.setPath(currentPath);
+        }
+        return currentNode;
+    }
+
+    @Override
+    public String currentPath() {
+        return currentPath;
+    }
+
+    @Override
+    public boolean next() {
+        currentNode = null;
+        if (nodes == null) {
+            currentPath = null;
+            return false;
+        }
+        if (nodes.isEmpty()) {
+            if (!mk.nodeExists(currentPath, revisionId)) {
+                nodes = null;
+                currentPath = null;
+                return false;
+            }
+            loadChildren(currentPath, 0);
+            return true;
+        }
+        while (!nodes.isEmpty()) {
+            // next child node in the deepest level
+            NodeCursor c = nodes.get(nodes.size() - 1);
+            currentPath = c.node.getPath();
+            long pos = c.pos++;
+            if (pos >= c.node.getTotalChildNodeCount()) {
+                // there are no more child nodes
+                nodes.remove(nodes.size() - 1);
+            } else {
+                if (pos > 0 && pos % childBlockSize == 0) {
+                    // need to load a new block
+                    nodes.remove(nodes.size() - 1);
+                    if (loadChildren(currentPath, pos)) {
+                        c = nodes.get(nodes.size() - 1);
+                        c.pos++;
+                    }
+                }
+                String childName = c.node.getChildNodeName(pos % childBlockSize);
+                currentPath = PathUtils.concat(currentPath, childName);
+                loadChildren(currentPath, 0);
+                return true;
+            }
+        }
+        nodes = null;
+        currentPath = null;
+        return false;
+    }
+
+    static class NodeCursor {
+        NodeImpl node;
+        long pos;
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingReader.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingReader.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingReader.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingReader.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,66 @@
+/*
+ * 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.index;
+
+import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.mk.util.PathUtils;
+
+public class TraversingReader implements NodeReader {
+
+    private final MicroKernel mk;
+    private int childBlockSize = 2000;
+
+    public TraversingReader(MicroKernel mk) {
+        this.mk = mk;
+    }
+
+    public void setChildBlockSize(int childBlockSize) {
+        this.childBlockSize = childBlockSize;
+    }
+
+    @Override
+    public Cursor query(Filter filter, String revisionId) {
+        return new TraversingCursor(mk, revisionId, childBlockSize, filter.getPath());
+    }
+
+    @Override
+    public double getCost(Filter filter) {
+        String path = filter.getPath();
+        // TODO estimate or read the node count
+        double nodeCount = 10000000;
+        if (!PathUtils.denotesRoot(path)) {
+            for (int depth = PathUtils.getDepth(path); depth > 0; depth--) {
+                // estimate 10 child nodes per node
+                nodeCount /= 10;
+            }
+        }
+        return nodeCount;
+    }
+
+    @Override
+    public String getPlan(Filter filter) {
+        String p = filter.getPath();
+        String r = filter.getPathRestriction().toString();
+        if (PathUtils.denotesRoot(p)) {
+            p = "";
+        }
+        return "traverse \"" + p + r + '"';
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryTest.java?rev=1302927&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryTest.java Tue Mar 20 15:05:04 2012
@@ -0,0 +1,158 @@
+/*
+ * 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;
+
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.text.ParseException;
+import java.util.Iterator;
+import org.apache.jackrabbit.mk.MicroKernelFactory;
+import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test the query feature.
+ */
+public class QueryTest {
+
+    MicroKernel mk;
+    String head;
+    QueryEngine qe;
+
+    @Before
+    public void setUp() {
+        mk = MicroKernelFactory.getInstance("simple:/target/temp;clear");
+        head = mk.getHeadRevision();
+        qe = QueryEngine.getInstance(mk);
+    }
+
+    @After
+    public void tearDown() {
+        mk.dispose();
+    }
+
+    @Test
+    public void script() throws Exception {
+        test("queryTest.txt");
+    }
+
+    @Test
+    public void xpath() throws Exception {
+        test("queryXpathTest.txt");
+    }
+
+    private void test(String file) throws Exception {
+        InputStream in = getClass().getResourceAsStream(file);
+        LineNumberReader r = new LineNumberReader(new InputStreamReader(in));
+        PrintWriter w = new PrintWriter(new OutputStreamWriter(new FileOutputStream("target/" + file)));
+        boolean errors = false;
+        try {
+            while (true) {
+                String line = r.readLine();
+                if (line == null) {
+                    break;
+                }
+                line = line.trim();
+                if (line.startsWith("#") || line.length() == 0) {
+                    w.println(line);
+                } else if (line.startsWith("xpath")) {
+                    line = line.substring("xpath".length()).trim();
+                    w.println("xpath " + line);
+                    XPathToSQL2Converter c = new XPathToSQL2Converter();
+                    String got;
+                    try {
+                        got = c.convert(line);
+                    } catch (ParseException e) {
+                        got = "invalid: " + e.getMessage().replace('\n', ' ');
+                    }
+                    line = r.readLine().trim();
+                    w.println(got);
+                    if (!line.equals(got)) {
+                        errors = true;
+                    }
+                } else if (line.startsWith("select") || line.startsWith("explain")) {
+                    w.println(line);
+                    Iterator<Row> result = qe.executeQuery(QueryEngine.SQL2, line, null);
+                    boolean readEnd = true;
+                    while (result.hasNext()) {
+                        Row row = result.next();
+                        String resultLine = readRow(line, row);
+                        w.println(resultLine);
+                        if (readEnd) {
+                            line = r.readLine();
+                            if (line == null) {
+                                errors = true;
+                                readEnd = false;
+                            } else {
+                                line = line.trim();
+                                if (line.length() == 0) {
+                                    errors = true;
+                                    readEnd = false;
+                                } else {
+                                    if (!line.equals(resultLine)) {
+                                        errors = true;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    w.println("");
+                    if (readEnd) {
+                        while (true) {
+                            line = r.readLine();
+                            if (line == null) {
+                                break;
+                            }
+                            line = line.trim();
+                            if (line.length() == 0) {
+                                break;
+                            }
+                            errors = true;
+                        }
+                    }
+                } else {
+                    w.println(line);
+                    mk.commit("/", line, mk.getHeadRevision(), "");
+                }
+            }
+        } finally {
+            w.close();
+            r.close();
+        }
+        if (errors) {
+            throw new Exception("Results in target/queryTest.txt don't match expected " +
+                    "results in src/test/resources/quersTest.txt; compare the files for details");
+        }
+    }
+
+    private String readRow(String query, Row row) {
+        StringBuilder buff = new StringBuilder();
+        Value[] values = row.getValues();
+        for (int i = 0; i < values.length; i++) {
+            if (i > 0) {
+                buff.append(", ");
+            }
+            Value v = values[i];
+            buff.append(v == null ? "null" : v.getString());
+        }
+        return buff.toString();
+    }
+
+}



Mime
View raw message