You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by th...@apache.org on 2012/09/07 15:14:41 UTC
svn commit: r1382008 [1/2] - in /jackrabbit/oak/trunk/oak-core/src:
main/java/org/apache/jackrabbit/oak/query/
main/java/org/apache/jackrabbit/oak/query/ast/
test/java/org/apache/jackrabbit/oak/query/
test/resources/org/apache/jackrabbit/oak/query/
Author: thomasm
Date: Fri Sep 7 13:14:40 2012
New Revision: 1382008
URL: http://svn.apache.org/viewvc?rev=1382008&view=rev
Log:
OAK-28 Query implementation: new feature "measure" to get the number of scanned nodes per selector instead of the nodes themselves or the query plan; (join) conditions are pushed down to the selectors if possible, this can reduce the number of nodes read; renamed method "apply" to "restrict"; the XPath to SQL-2 converter now supports joins and only uses parentheses where needed; test queries are now much faster because /jcr:system child nodes are excluded and join conditions are pushed down; if queries take more than 3 seconds now the test is considered as failed
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.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/XPathToSQL2Converter.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AndImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ConstraintImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeJoinConditionImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinConditionImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NotImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeJoinConditionImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryTest.java
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_explain.txt
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java?rev=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java Fri Sep 7 13:14:40 2012
@@ -74,7 +74,7 @@ public class Query {
private QueryEngineImpl queryEngine;
private final OrderingImpl[] orderings;
private ColumnImpl[] columns;
- private boolean explain;
+ private boolean explain, measure;
private long limit = Long.MAX_VALUE;
private long offset;
private long size = -1;
@@ -244,8 +244,8 @@ public class Query {
}
}.visit(this);
- source.init(this);
source.setQueryConstraint(constraint);
+ source.init(this);
for (ColumnImpl column : columns) {
column.bindSelector(source);
}
@@ -291,6 +291,10 @@ public class Query {
this.explain = explain;
}
+ public void setMeasure(boolean measure) {
+ this.measure = measure;
+ }
+
public ResultImpl executeQuery(String revisionId, NodeState root) {
return new ResultImpl(this, revisionId, root);
}
@@ -301,10 +305,14 @@ public class Query {
if (explain) {
String plan = source.getPlan();
columns = new ColumnImpl[] { new ColumnImpl("explain", "plan", "plan")};
- ResultRowImpl r = new ResultRowImpl(this, new String[0], new CoreValue[] { getValueFactory().createValue(plan) }, null);
+ ResultRowImpl r = new ResultRowImpl(this,
+ new String[0],
+ new CoreValue[] { getValueFactory().createValue(plan) },
+ null);
it = Arrays.asList(r).iterator();
} else {
it = new RowIterator(revisionId, root, limit, offset);
+ long resultCount = 0;
if (orderings != null) {
// TODO "order by" is not necessary if the used index returns
// rows in the same order
@@ -313,9 +321,40 @@ public class Query {
ResultRowImpl r = it.next();
list.add(r);
}
- size = list.size();
+ resultCount = size = list.size();
Collections.sort(list);
it = list.iterator();
+ } else if (measure) {
+ while (it.hasNext()) {
+ resultCount++;
+ it.next();
+ }
+ }
+ if (measure) {
+ columns = new ColumnImpl[] {
+ new ColumnImpl("measure", "selector", "selector"),
+ new ColumnImpl("measure", "scanCount", "scanCount")
+ };
+ ArrayList<ResultRowImpl> list = new ArrayList<ResultRowImpl>();
+ ResultRowImpl r = new ResultRowImpl(this,
+ new String[0],
+ new CoreValue[] {
+ getValueFactory().createValue("query"),
+ getValueFactory().createValue(resultCount),
+ },
+ null);
+ list.add(r);
+ for (SelectorImpl selector : selectors) {
+ r = new ResultRowImpl(this,
+ new String[0],
+ new CoreValue[] {
+ getValueFactory().createValue(selector.getSelectorName()),
+ getValueFactory().createValue(selector.getScanCount()),
+ },
+ null);
+ list.add(r);
+ }
+ it = list.iterator();
}
}
return it;
@@ -458,6 +497,7 @@ public class Query {
if (prepared) {
return;
}
+ prepared = true;
source.prepare(mk);
}
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=1382008&r1=1382007&r2=1382008&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 Fri Sep 7 13:14:40 2012
@@ -109,7 +109,12 @@ public class SQL2Parser {
expected = new ArrayList<String>();
bindVariables = new HashMap<String, BindVariableValueImpl>();
read();
- boolean explain = readIf("EXPLAIN");
+ boolean explain = false, measure = false;
+ if (readIf("EXPLAIN")) {
+ explain = true;
+ } else if (readIf("MEASURE")) {
+ measure = true;
+ }
read("SELECT");
ArrayList<ColumnOrWildcard> list = parseColumns();
if (supportSQL1) {
@@ -133,6 +138,7 @@ public class SQL2Parser {
}
Query q = new Query(source, constraint, orderings, columnArray, valueFactory);
q.setExplain(explain);
+ q.setMeasure(measure);
try {
q.init();
} catch (Exception e) {
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java?rev=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java Fri Sep 7 13:14:40 2012
@@ -62,6 +62,10 @@ public class XPathToSQL2Converter {
if (explain) {
query = query.substring("explain ".length());
}
+ boolean measure = query.startsWith("measure");
+ if (measure) {
+ query = query.substring("measure ".length());
+ }
// TODO verify this is correct
if (!query.startsWith("/")) {
query = "/jcr:root/" + query;
@@ -70,150 +74,141 @@ public class XPathToSQL2Converter {
expected = new ArrayList<String>();
read();
- String path = "";
- String nodeName = null;
- Expression condition = null;
-
currentSelector.name = "a";
currentSelector.nodeType = "nt:base";
- selectors.add(currentSelector);
ArrayList<Expression> columnList = new ArrayList<Expression>();
- // queries of the type
- // jcr:root/*
- // jcr:root/test/*
- // jcr:root/element()
- // jcr:root/element(*)
- boolean children = false;
-
- // queries of the type
- // /jcr:root//...
- // /jcr:root/test//...
- // /jcr:root[...]
- // /jcr:root (just by itself)
- boolean descendants = false;
-
// TODO support one node matcher ('*' wildcard), example:
// /jcr:root/content/acme/*/jcr:content[@template='/apps/acme']
// TODO verify '//' is behaving correctly, as specified, example:
// /jcr:root/content/acme//jcr:content[@template='/apps/acme']
+
+ String pathPattern = "";
while (true) {
- if (readIf("/")) {
- if (nodeName != null) {
- throw getSyntaxError("non-path condition");
+ boolean shortcut = false;
+ boolean slash = readIf("/");
+ if (!slash) {
+ break;
+ } else if (readIf("jcr:root")) {
+ // "/jcr:root" may only appear at the beginning
+ if (!pathPattern.isEmpty()) {
+ throw getSyntaxError("jcr:root needs to be at the beginning");
}
if (readIf("/")) {
- descendants = true;
- } else if (readIf("jcr:root")) {
- path = "/";
+ // "/jcr:root/"
+ currentSelector.path = "/";
+ pathPattern = "/";
if (readIf("/")) {
- if (readIf("/")) {
- descendants = true;
- }
- } else {
- descendants = true;
- if (readIf("[")) {
- Expression c = parseConstraint();
- condition = add(condition, c);
- read("]");
- }
- // expected end of statement
- break;
+ // "/jcr:root//"
+ pathPattern = "//";
+ currentSelector.isDescendant = true;
}
} else {
- descendants = false;
+ // for example "/jcr:root[condition]"
+ pathPattern = "/%";
+ currentSelector.path = "/";
+ currentSelector.isDescendant = true;
+ shortcut = true;
}
- if (readIf("*")) {
- currentSelector.nodeType = "nt:base";
- children = true;
- } else if (readIf("text")) {
- read("(");
- read(")");
- if (descendants) {
- nodeName = "jcr:xmltext";
- } else {
- path = PathUtils.concat(path, "jcr:xmltext");
- }
- } else if (readIf("element")) {
- read("(");
- if (readIf(")")) {
+ } else if (readIf("/")) {
+ // "//" was read
+ pathPattern += "%";
+ currentSelector.isDescendant = true;
+ } else {
+ // the token "/" was read
+ pathPattern += "/";
+ }
+ if (shortcut) {
+ // query of the style: "/jcr:root[condition]"
+ } else if (readIf("*")) {
+ // "...*"
+ pathPattern += "%";
+ currentSelector.nodeType = "nt:base";
+ currentSelector.isChild = true;
+ } else if (readIf("text")) {
+ // "...text()"
+ pathPattern += "jcr:xmltext";
+ read("(");
+ read(")");
+ if (currentSelector.isDescendant) {
+ currentSelector.nodeName = "jcr:xmltext";
+ } else {
+ currentSelector.path = PathUtils.concat(currentSelector.path, "jcr:xmltext");
+ }
+ } else if (readIf("element")) {
+ // "...element(..."
+ read("(");
+ if (readIf(")")) {
+ // any
+ currentSelector.isChild = true;
+ } else {
+ if (readIf("*")) {
// any
- children = true;
+ currentSelector.isChild = true;
+ pathPattern += "%";
} else {
- if (readIf("*")) {
- // any
- children = true;
- } else {
- children = false;
- String name = readIdentifier();
- path = PathUtils.concat(path, name);
- }
- if (readIf(",")) {
- currentSelector.nodeType = readIdentifier();
- } else {
- currentSelector.nodeType = "nt:base";
- }
- read(")");
+ currentSelector.isChild = false;
+ String name = readIdentifier();
+ pathPattern += name;
+ currentSelector.path = PathUtils.concat(currentSelector.path, name);
}
- } else if (readIf("@")) {
- Property p = readProperty();
- columnList.add(p);
- } else if (readIf("(")) {
- do {
- read("@");
- Property p = readProperty();
- columnList.add(p);
- } while (readIf("|"));
- read(")");
- } else {
- if (descendants) {
- nodeName = readIdentifier();
+ if (readIf(",")) {
+ currentSelector.nodeType = readIdentifier();
} else {
- String name = readIdentifier();
- path = PathUtils.concat(path, name);
+ currentSelector.nodeType = "nt:base";
}
+ read(")");
}
- if (readIf("[")) {
- Expression c = parseConstraint();
- condition = add(condition, c);
- read("]");
+ } else if (readIf("@")) {
+ Property p = readProperty();
+ columnList.add(p);
+ } else if (readIf("(")) {
+ // special case: ".../(@prop)" is actually not a child node,
+ // but the same node (selector) as before
+ if (selectors.size() > 0) {
+ currentSelector = selectors.remove(selectors.size() - 1);
+ // prevent (join) conditions are added again
+ currentSelector.isChild = false;
+ currentSelector.isDescendant = false;
+ currentSelector.path = "";
+ currentSelector.nodeName = null;
}
+ if (currentSelector.nodeType == null) {
+ currentSelector.nodeType = "nt:base";
+ }
+ do {
+ read("@");
+ Property p = readProperty();
+ columnList.add(p);
+ } while (readIf("|"));
+ read(")");
} else {
- break;
- }
- }
- if (path.isEmpty()) {
- // no condition
- } else {
- if (!PathUtils.isAbsolute(path)) {
- path = PathUtils.concat("/", path);
- }
- if (descendants) {
- Function c = new Function("isdescendantnode");
- c.params.add(new SelectorExpr(currentSelector));
- c.params.add(Literal.newString(path));
- condition = add(condition, c);
- } else if (children) {
- Function c = new Function("ischildnode");
- c.params.add(new SelectorExpr(currentSelector));
- c.params.add(Literal.newString(path));
- condition = add(condition, c);
- } else {
- Function c = new Function("issamenode");
- c.params.add(new SelectorExpr(currentSelector));
- c.params.add(Literal.newString(path));
- condition = add(condition, c);
- }
- if (nodeName != null) {
- Function f = new Function("name");
- f.params.add(new SelectorExpr(currentSelector));
- Condition c = new Condition(f, "=", Literal.newString(nodeName));
- condition = add(condition, c);
+ // path restriction
+ String name = readIdentifier();
+ if (currentSelector.isDescendant) {
+ pathPattern += name;
+ currentSelector.nodeName = name;
+ } else {
+ pathPattern += name;
+ currentSelector.path = PathUtils.concat(currentSelector.path, name);
+ }
}
- }
+ if (readIf("[")) {
+ Expression c = parseConstraint();
+ currentSelector.condition = add(currentSelector.condition, c);
+ read("]");
+ }
+ nextSelector(false);
+ }
+ if (selectors.size() == 0) {
+ nextSelector(true);
+ }
+ // the current selector wasn't used so far
+ // go back to the last one
+ currentSelector = selectors.get(selectors.size() - 1);
if (selectors.size() == 1) {
currentSelector.onlySelector = true;
}
@@ -235,9 +230,15 @@ public class XPathToSQL2Converter {
throw getSyntaxError("<end>");
}
StringBuilder buff = new StringBuilder();
+
+ // explain | measure ...
if (explain) {
buff.append("explain ");
+ } else if (measure) {
+ buff.append("measure ");
}
+
+ // select ...
buff.append("select ");
buff.append(new Property(currentSelector, "jcr:path").toString());
buff.append(", ");
@@ -251,6 +252,8 @@ public class XPathToSQL2Converter {
buff.append(columnList.get(i).toString());
}
}
+
+ // from ...
buff.append(" from ");
for (int i = 0; i < selectors.size(); i++) {
Selector s = selectors.get(i);
@@ -262,9 +265,23 @@ public class XPathToSQL2Converter {
buff.append(" on ").append(s.joinCondition);
}
}
- if (condition != null) {
- buff.append(" where ").append(removeParens(condition.toString()));
+
+ // where ...
+ StringBuilder condition = new StringBuilder();
+ for (int i = 0; i < selectors.size(); i++) {
+ Selector s = selectors.get(i);
+ if (s.condition != null) {
+ if (condition.length() > 0) {
+ condition.append(" and ");
+ }
+ condition.append(s.condition);
+ }
+ }
+ if (condition.length() > 0) {
+ buff.append(" where ").append(condition.toString());
}
+
+ // order by ...
if (!orderList.isEmpty()) {
buff.append(" order by ");
for (int i = 0; i < orderList.size(); i++) {
@@ -276,18 +293,90 @@ public class XPathToSQL2Converter {
}
return buff.toString();
}
+
+ private void nextSelector(boolean force) throws ParseException {
+ boolean isFirstSelector = selectors.size() == 0;
+ String path = currentSelector.path;
+ Expression condition = currentSelector.condition;
+ Expression joinCondition = currentSelector.joinCondition;
+ if (currentSelector.nodeName != null) {
+ Function f = new Function("name");
+ f.params.add(new SelectorExpr(currentSelector));
+ Condition c = new Condition(f, "=",
+ Literal.newString(currentSelector.nodeName),
+ Expression.PRECEDENCE_CONDITION);
+ condition = add(condition, c);
+ }
+ if (currentSelector.isDescendant) {
+ if (isFirstSelector) {
+ if (!path.isEmpty()) {
+ if (!PathUtils.isAbsolute(path)) {
+ path = PathUtils.concat("/", path);
+ }
+ Function c = new Function("isdescendantnode");
+ c.params.add(new SelectorExpr(currentSelector));
+ c.params.add(Literal.newString(path));
+ condition = add(condition, c);
+ }
+ } else {
+ Function c = new Function("isdescendantnode");
+ c.params.add(new SelectorExpr(currentSelector));
+ c.params.add(new SelectorExpr(selectors.get(selectors.size() - 1)));
+ joinCondition = c;
+ }
+ } else if (currentSelector.isChild) {
+ if (isFirstSelector) {
+ if (!path.isEmpty()) {
+ if (!PathUtils.isAbsolute(path)) {
+ path = PathUtils.concat("/", path);
+ }
+ Function c = new Function("ischildnode");
+ c.params.add(new SelectorExpr(currentSelector));
+ c.params.add(Literal.newString(path));
+ condition = add(condition, c);
+ }
+ } else {
+ Function c = new Function("ischildnode");
+ c.params.add(new SelectorExpr(currentSelector));
+ c.params.add(new SelectorExpr(selectors.get(selectors.size() - 1)));
+ joinCondition = c;
+ }
+ } else {
+ if (!force && condition == null && joinCondition == null) {
+ // a child node of a given path, such as "/test"
+ // use the same selector for now, and extend the path
+ } else if (PathUtils.isAbsolute(path)) {
+ Function c = new Function("issamenode");
+ c.params.add(new SelectorExpr(currentSelector));
+ c.params.add(Literal.newString(path));
+ condition = add(condition, c);
+ }
+ }
+ if (force || condition != null || joinCondition != null) {
+ String nextSelectorName = "" + (char) (currentSelector.name.charAt(0) + 1);
+ if (nextSelectorName.compareTo("x") > 0) {
+ throw getSyntaxError("too many joins");
+ }
+ Selector nextSelector = new Selector();
+ nextSelector.name = nextSelectorName;
+ currentSelector.condition = condition;
+ currentSelector.joinCondition = joinCondition;
+ selectors.add(currentSelector);
+ currentSelector = nextSelector;
+ }
+ }
private static Expression add(Expression old, Expression add) {
if (old == null) {
return add;
}
- return new Condition(old, "and", add);
+ return new Condition(old, "and", add, Expression.PRECEDENCE_AND);
}
private Expression parseConstraint() throws ParseException {
Expression a = parseAnd();
while (readIf("or")) {
- a = new Condition(a, "or", parseAnd());
+ a = new Condition(a, "or", parseAnd(), Expression.PRECEDENCE_OR);
}
return a;
}
@@ -295,7 +384,7 @@ public class XPathToSQL2Converter {
private Expression parseAnd() throws ParseException {
Expression a = parseCondition();
while (readIf("and")) {
- a = new Condition(a, "and", parseCondition());
+ a = new Condition(a, "and", parseCondition(), Expression.PRECEDENCE_AND);
}
return a;
}
@@ -308,7 +397,7 @@ public class XPathToSQL2Converter {
if (a instanceof Condition && ((Condition) a).operator.equals("is not null")) {
// not(@property) -> @property is null
Condition c = (Condition) a;
- c = new Condition(c.left, "is null", null);
+ c = new Condition(c.left, "is null", null, Expression.PRECEDENCE_CONDITION);
a = c;
} else {
Function f = new Function("not");
@@ -332,21 +421,21 @@ public class XPathToSQL2Converter {
private Condition parseCondition(Expression left) throws ParseException {
Condition c;
if (readIf("=")) {
- c = new Condition(left, "=", parseExpression());
+ c = new Condition(left, "=", parseExpression(), Expression.PRECEDENCE_CONDITION);
} else if (readIf("<>")) {
- c = new Condition(left, "<>", parseExpression());
+ c = new Condition(left, "<>", parseExpression(), Expression.PRECEDENCE_CONDITION);
} else if (readIf("!=")) {
- c = new Condition(left, "<>", parseExpression());
+ c = new Condition(left, "<>", parseExpression(), Expression.PRECEDENCE_CONDITION);
} else if (readIf("<")) {
- c = new Condition(left, "<", parseExpression());
+ c = new Condition(left, "<", parseExpression(), Expression.PRECEDENCE_CONDITION);
} else if (readIf(">")) {
- c = new Condition(left, ">", parseExpression());
+ c = new Condition(left, ">", parseExpression(), Expression.PRECEDENCE_CONDITION);
} else if (readIf("<=")) {
- c = new Condition(left, "<=", parseExpression());
+ c = new Condition(left, "<=", parseExpression(), Expression.PRECEDENCE_CONDITION);
} else if (readIf(">=")) {
- c = new Condition(left, ">=", parseExpression());
+ c = new Condition(left, ">=", parseExpression(), Expression.PRECEDENCE_CONDITION);
} else {
- c = new Condition(left, "is not null", null);
+ c = new Condition(left, "is not null", null, Expression.PRECEDENCE_CONDITION);
}
return c;
}
@@ -435,7 +524,7 @@ public class XPathToSQL2Converter {
private Expression parseFunction(String functionName) throws ParseException {
if ("jcr:like".equals(functionName)) {
- Condition c = new Condition(parseExpression(), "like", null);
+ Condition c = new Condition(parseExpression(), "like", null, Expression.PRECEDENCE_CONDITION);
read(",");
c.right = parseExpression();
read(")");
@@ -817,33 +906,95 @@ public class XPathToSQL2Converter {
return new ParseException("Query:\n" + query, index);
}
- static String removeParens(String s) {
- if (s.startsWith("(") && s.endsWith(")")) {
- return s.substring(1, s.length() - 1);
- }
- return s;
- }
-
/**
* A selector.
*/
static class Selector {
+ /**
+ * The selector name.
+ */
String name;
+
+ /**
+ * Whether this is the only selector in the query.
+ */
boolean onlySelector;
+
+ /**
+ * The node type, if set, or null.
+ */
String nodeType;
- Expression joinCondition;
+
+ /**
+ * Whether this is a child node of the previous selector or a given path.
+ */
+ // queries of the type
+ // jcr:root/*
+ // jcr:root/test/*
+ // jcr:root/element()
+ // jcr:root/element(*)
+ boolean isChild;
+ /**
+ * Whether this is a descendant of the previous selector or a given path.
+ */
+ // queries of the type
+ // /jcr:root//...
+ // /jcr:root/test//...
+ // /jcr:root[...]
+ // /jcr:root (just by itself)
+ boolean isDescendant;
+
+ /**
+ * The path (only used for the first selector).
+ */
+ String path = "";
+
+ /**
+ * The node name, if set.
+ */
+ String nodeName;
+
+ /**
+ * The condition for this selector.
+ */
+ Expression condition;
+
+ /**
+ * The join condition from the previous selector.
+ */
+ Expression joinCondition;
+
}
/**
* An expression.
*/
abstract static class Expression {
-
+
+ static final int PRECEDENCE_OR = 1, PRECEDENCE_AND = 2,
+ PRECEDENCE_CONDITION = 3, PRECEDENCE_OPERAND = 4;
+
+ /**
+ * Whether this is a condition.
+ *
+ * @return true if it is
+ */
boolean isCondition() {
return false;
}
+
+ /**
+ * Get the operator / operation precedence. The JCR specification uses:
+ * 1=OR, 2=AND, 3=condition, 4=operand
+ *
+ * @return the precedence (as an example, multiplication needs to return
+ * a higher number than addition)
+ */
+ int getPrecedence() {
+ return PRECEDENCE_OPERAND;
+ }
}
@@ -932,21 +1083,49 @@ public class XPathToSQL2Converter {
final Expression left;
final String operator;
Expression right;
+ final int precedence;
- Condition(Expression left, String operator, Expression right) {
+ /**
+ * Create a new condition.
+ *
+ * @param left the left hand side operator, or null
+ * @param operator the operator
+ * @param right the right hand side operator, or null
+ * @param precedence the operator precedence (Expression.PRECEDENCE_...)
+ */
+ Condition(Expression left, String operator, Expression right, int precedence) {
this.left = left;
this.operator = operator;
this.right = right;
+ this.precedence = precedence;
+ }
+
+ @Override
+ int getPrecedence() {
+ return precedence;
}
@Override
public String toString() {
- return
- "(" +
- (left == null ? "" : left + " ") +
- operator +
- (right == null ? "" : " " + right) +
- ")";
+ StringBuilder buff = new StringBuilder();
+ if (left != null) {
+ if (left.getPrecedence() < precedence) {
+ buff.append('(').append(left.toString()).append(')');
+ } else {
+ buff.append(left.toString());
+ }
+ buff.append(' ');
+ }
+ buff.append(operator);
+ if (right != null) {
+ buff.append(' ');
+ if (right.getPrecedence() < precedence) {
+ buff.append('(').append(right.toString()).append(')');
+ } else {
+ buff.append(right.toString());
+ }
+ }
+ return buff.toString();
}
@Override
@@ -976,7 +1155,7 @@ public class XPathToSQL2Converter {
if (i > 0) {
buff.append(", ");
}
- buff.append(removeParens(params.get(i).toString()));
+ buff.append(params.get(i).toString());
}
buff.append(')');
return buff.toString();
@@ -1005,7 +1184,7 @@ public class XPathToSQL2Converter {
@Override
public String toString() {
StringBuilder buff = new StringBuilder("cast(");
- buff.append(removeParens(expr.toString()));
+ buff.append(expr.toString());
buff.append(" as ").append(type).append(')');
return buff.toString();
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AndImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AndImpl.java?rev=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AndImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AndImpl.java Fri Sep 7 13:14:40 2012
@@ -56,9 +56,15 @@ public class AndImpl extends ConstraintI
}
@Override
- public void apply(FilterImpl f) {
- constraint1.apply(f);
- constraint2.apply(f);
+ public void restrict(FilterImpl f) {
+ constraint1.restrict(f);
+ constraint2.restrict(f);
+ }
+
+ @Override
+ public void restrictPushDown(SelectorImpl s) {
+ constraint1.restrictPushDown(s);
+ constraint2.restrictPushDown(s);
}
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java?rev=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java Fri Sep 7 13:14:40 2012
@@ -72,11 +72,18 @@ public class ChildNodeImpl extends Const
}
@Override
- public void apply(FilterImpl f) {
+ public void restrict(FilterImpl f) {
if (selector == f.getSelector()) {
String path = getAbsolutePath(parentPath);
f.restrictPath(path, Filter.PathRestriction.DIRECT_CHILDREN);
}
}
+ @Override
+ public void restrictPushDown(SelectorImpl s) {
+ if (s == selector) {
+ s.restrictSelector(this);
+ }
+ }
+
}
\ No newline at end of file
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java?rev=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java Fri Sep 7 13:14:40 2012
@@ -72,7 +72,7 @@ public class ChildNodeJoinConditionImpl
}
@Override
- public void apply(FilterImpl f) {
+ public void restrict(FilterImpl f) {
String p = parentSelector.currentPath();
String c = childSelector.currentPath();
if (f.getSelector() == parentSelector && c != null) {
@@ -83,4 +83,9 @@ public class ChildNodeJoinConditionImpl
}
}
+ @Override
+ public void restrictPushDown(SelectorImpl s) {
+ // nothing to do
+ }
+
}
\ No newline at end of file
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java?rev=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java Fri Sep 7 13:14:40 2012
@@ -121,7 +121,7 @@ public class ComparisonImpl extends Cons
}
@Override
- public void apply(FilterImpl f) {
+ public void restrict(FilterImpl f) {
CoreValue v = operand2.currentValue();
if (v != null) {
if (operator == Operator.LIKE) {
@@ -147,6 +147,16 @@ public class ComparisonImpl extends Cons
}
}
+ @Override
+ public void restrictPushDown(SelectorImpl s) {
+ CoreValue v = operand2.currentValue();
+ if (v != null) {
+ if (operand1.canRestrictSelector(s)) {
+ s.restrictSelector(this);
+ }
+ }
+ }
+
/**
* A pattern matcher.
*/
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ConstraintImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ConstraintImpl.java?rev=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ConstraintImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ConstraintImpl.java Fri Sep 7 13:14:40 2012
@@ -37,6 +37,14 @@ public abstract class ConstraintImpl ext
*
* @param f the filter
*/
- public abstract void apply(FilterImpl f);
+ public abstract void restrict(FilterImpl f);
+
+ /**
+ * Push as much of the condition down to this selector, further restricting
+ * the selector condition if possible.
+ *
+ * @param s the selector
+ */
+ public abstract void restrictPushDown(SelectorImpl s);
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java?rev=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java Fri Sep 7 13:14:40 2012
@@ -48,6 +48,9 @@ public class DescendantNodeImpl extends
public boolean evaluate() {
String p = selector.currentPath();
String path = getAbsolutePath(ancestorPath);
+ if (p == null || path == null) {
+ return false;
+ }
return PathUtils.isAncestor(path, p);
}
@@ -66,11 +69,18 @@ public class DescendantNodeImpl extends
}
@Override
- public void apply(FilterImpl f) {
+ public void restrict(FilterImpl f) {
if (f.getSelector() == selector) {
String path = getAbsolutePath(ancestorPath);
f.restrictPath(path, Filter.PathRestriction.ALL_CHILDREN);
}
}
+ @Override
+ public void restrictPushDown(SelectorImpl s) {
+ if (s == selector) {
+ s.restrictSelector(this);
+ }
+ }
+
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeJoinConditionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeJoinConditionImpl.java?rev=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeJoinConditionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeJoinConditionImpl.java Fri Sep 7 13:14:40 2012
@@ -71,7 +71,7 @@ public class DescendantNodeJoinCondition
}
@Override
- public void apply(FilterImpl f) {
+ public void restrict(FilterImpl f) {
String d = descendantSelector.currentPath();
String a = ancestorSelector.currentPath();
if (d != null && f.getSelector() == ancestorSelector) {
@@ -82,4 +82,9 @@ public class DescendantNodeJoinCondition
}
}
+ @Override
+ public void restrictPushDown(SelectorImpl s) {
+ // nothing to do
+ }
+
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java?rev=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java Fri Sep 7 13:14:40 2012
@@ -29,6 +29,17 @@ public abstract class DynamicOperandImpl
public abstract void apply(FilterImpl f, Operator operator, CoreValue v);
+ /**
+ * Check whether the condition can be applied to a selector (to restrict the
+ * selector). The method may return true if the operand can be evaluated
+ * when the given selector and all previous selectors in the join can be
+ * evaluated.
+ *
+ * @param s the selector
+ * @return true if the condition can be applied
+ */
+ public abstract boolean canRestrictSelector(SelectorImpl s);
+
public boolean supportsRangeConditions() {
return true;
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java?rev=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java Fri Sep 7 13:14:40 2012
@@ -120,7 +120,7 @@ public class EquiJoinConditionImpl exten
}
@Override
- public void apply(FilterImpl f) {
+ public void restrict(FilterImpl f) {
PropertyState p1 = selector1.currentProperty(property1Name);
PropertyState p2 = selector2.currentProperty(property2Name);
if (f.getSelector() == selector1 && p2 != null) {
@@ -137,4 +137,18 @@ public class EquiJoinConditionImpl exten
}
}
+ @Override
+ public void restrictPushDown(SelectorImpl s) {
+ // both properties may not be null
+ if (s == selector1) {
+ PropertyExistenceImpl ex = new PropertyExistenceImpl(s.getSelectorName(), property1Name);
+ ex.bindSelector(s);
+ s.restrictSelector(ex);
+ } else if (s == selector2) {
+ PropertyExistenceImpl ex = new PropertyExistenceImpl(s.getSelectorName(), property2Name);
+ ex.bindSelector(s);
+ s.restrictSelector(ex);
+ }
+ }
+
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java?rev=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java Fri Sep 7 13:14:40 2012
@@ -123,7 +123,7 @@ public class FullTextSearchImpl extends
}
@Override
- public void apply(FilterImpl f) {
+ public void restrict(FilterImpl f) {
if (propertyName != null) {
if (f.getSelector() == selector) {
f.restrictProperty(propertyName, Operator.NOT_EQUAL, (CoreValue) null);
@@ -132,6 +132,13 @@ public class FullTextSearchImpl extends
f.restrictFulltextCondition(fullTextSearchExpression.currentValue().getString());
}
+ @Override
+ public void restrictPushDown(SelectorImpl s) {
+ if (s == selector) {
+ selector.restrictSelector(this);
+ }
+ }
+
/**
* A parser for fulltext condition literals. The grammar is defined in the
* <a href="http://www.day.com/specs/jcr/2.0/6_Query.html#6.7.19">
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java?rev=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java Fri Sep 7 13:14:40 2012
@@ -63,4 +63,9 @@ public class FullTextSearchScoreImpl ext
// TODO support fulltext index conditions (score)
}
+ @Override
+ public boolean canRestrictSelector(SelectorImpl s) {
+ return s == selector;
+ }
+
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinConditionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinConditionImpl.java?rev=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinConditionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinConditionImpl.java Fri Sep 7 13:14:40 2012
@@ -22,6 +22,8 @@ public abstract class JoinConditionImpl
public abstract boolean evaluate();
- public abstract void apply(FilterImpl f);
+ public abstract void restrict(FilterImpl f);
+
+ public abstract void restrictPushDown(SelectorImpl selectorImpl);
}
Modified: 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=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/JoinImpl.java Fri Sep 7 13:14:40 2012
@@ -79,36 +79,31 @@ public class JoinImpl extends SourceImpl
public void init(Query query) {
switch (joinType) {
case INNER:
- left.setQueryConstraint(queryConstraint);
- right.setQueryConstraint(queryConstraint);
- right.setJoinCondition(joinCondition);
- left.init(query);
- right.init(query);
+ left.addJoinCondition(joinCondition, false);
+ right.addJoinCondition(joinCondition, true);
break;
case LEFT_OUTER:
- left.setQueryConstraint(queryConstraint);
right.setOuterJoin(true);
- right.setQueryConstraint(queryConstraint);
- right.setJoinCondition(joinCondition);
- left.init(query);
- right.init(query);
+ left.addJoinCondition(joinCondition, false);
+ right.addJoinCondition(joinCondition, true);
break;
case RIGHT_OUTER:
- right.setQueryConstraint(queryConstraint);
- left.setOuterJoin(true);
- left.setQueryConstraint(queryConstraint);
- left.setJoinCondition(joinCondition);
- right.init(query);
- left.init(query);
+ // swap left and right
// 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;
+ right.setOuterJoin(true);
+ left.addJoinCondition(joinCondition, false);
+ right.addJoinCondition(joinCondition, true);
break;
}
+ left.setQueryConstraint(queryConstraint);
+ right.setQueryConstraint(queryConstraint);
+ right.init(query);
+ left.init(query);
}
@Override
Modified: 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=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java Fri Sep 7 13:14:40 2012
@@ -86,4 +86,9 @@ public class LengthImpl extends DynamicO
// TODO LENGTH(x) conditions: can use IS NOT NULL as a condition?
}
+ @Override
+ public boolean canRestrictSelector(SelectorImpl s) {
+ return propertyValue.canRestrictSelector(s);
+ }
+
}
Modified: 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=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java Fri Sep 7 13:14:40 2012
@@ -67,4 +67,9 @@ public class LowerCaseImpl extends Dynam
// TODO UPPER(x) conditions: can use IS NOT NULL?
}
+ @Override
+ public boolean canRestrictSelector(SelectorImpl s) {
+ return operand.canRestrictSelector(s);
+ }
+
}
Modified: 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=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java Fri Sep 7 13:14:40 2012
@@ -72,4 +72,9 @@ public class NodeLocalNameImpl extends D
// TODO support LOCALNAME index conditions
}
+ @Override
+ public boolean canRestrictSelector(SelectorImpl s) {
+ return s == selector;
+ }
+
}
Modified: 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=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java Fri Sep 7 13:14:40 2012
@@ -87,6 +87,11 @@ public class NodeNameImpl extends Dynami
// TODO support NAME(..) index conditions
}
+ @Override
+ public boolean canRestrictSelector(SelectorImpl s) {
+ return s == selector;
+ }
+
private String decodeName(String path) {
// Name escaping (convert _x0020_ to space)
path = ISO9075.decode(path);
Modified: 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=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NotImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NotImpl.java Fri Sep 7 13:14:40 2012
@@ -51,7 +51,13 @@ public class NotImpl extends ConstraintI
}
@Override
- public void apply(FilterImpl f) {
+ public void restrict(FilterImpl f) {
+ // ignore
+ // TODO convert NOT conditions
+ }
+
+ @Override
+ public void restrictPushDown(SelectorImpl s) {
// ignore
// TODO convert NOT conditions
}
Modified: 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=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java Fri Sep 7 13:14:40 2012
@@ -57,9 +57,18 @@ public class OrImpl extends ConstraintIm
}
@Override
- public void apply(FilterImpl f) {
+ public void restrict(FilterImpl f) {
// ignore
// TODO convert OR conditions to UNION
}
+ @Override
+ public void restrictPushDown(SelectorImpl s) {
+ // ignore
+ // TODO some OR conditions can be applied to a selector,
+ // for example WHERE X.ID = 1 OR X.ID = 2
+ // can be applied to X as a whole,
+ // but X.ID = 1 OR Y.ID = 2 can't be applied to either
+ }
+
}
Modified: 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=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java Fri Sep 7 13:14:40 2012
@@ -66,10 +66,17 @@ public class PropertyExistenceImpl exten
}
@Override
- public void apply(FilterImpl f) {
+ public void restrict(FilterImpl f) {
if (f.getSelector() == selector) {
f.restrictProperty(propertyName, Operator.NOT_EQUAL, (CoreValue) null);
}
}
+ @Override
+ public void restrictPushDown(SelectorImpl s) {
+ if (s == selector) {
+ s.restrictSelector(this);
+ }
+ }
+
}
Modified: 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=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java Fri Sep 7 13:14:40 2012
@@ -165,4 +165,9 @@ public class PropertyValueImpl extends D
}
}
+ @Override
+ public boolean canRestrictSelector(SelectorImpl s) {
+ return s == selector;
+ }
+
}
Modified: 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=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java Fri Sep 7 13:14:40 2012
@@ -65,7 +65,7 @@ public class SameNodeImpl extends Constr
}
@Override
- public void apply(FilterImpl f) {
+ public void restrict(FilterImpl f) {
if (f.getSelector() == selector) {
String p = getAbsolutePath(path);
f.restrictPath(p, Filter.PathRestriction.EXACT);
@@ -73,4 +73,11 @@ public class SameNodeImpl extends Constr
// TODO validate absolute path
}
+ @Override
+ public void restrictPushDown(SelectorImpl s) {
+ if (s == selector) {
+ s.restrictSelector(this);
+ }
+ }
+
}
Modified: 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=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeJoinConditionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeJoinConditionImpl.java Fri Sep 7 13:14:40 2012
@@ -84,7 +84,7 @@ public class SameNodeJoinConditionImpl e
}
@Override
- public void apply(FilterImpl f) {
+ public void restrict(FilterImpl f) {
String p1 = selector1.currentPath();
String p2 = selector2.currentPath();
if (f.getSelector() == selector1) {
@@ -95,4 +95,9 @@ public class SameNodeJoinConditionImpl e
}
}
+ @Override
+ public void restrictPushDown(SelectorImpl s) {
+ // nothing to do
+ }
+
}
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=1382008&r1=1382007&r2=1382008&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 Fri Sep 7 13:14:40 2012
@@ -48,6 +48,16 @@ public class SelectorImpl extends Source
private final String nodeTypeName, selectorName;
private Cursor cursor;
+ private int scanCount;
+
+ /**
+ * The selector condition can be evaluated when the given selector is
+ * evaluated. For example, for the query
+ * "select * from nt:base a inner join nt:base b where a.x = 1 and b.y = 2",
+ * the condition "a.x = 1" can be evaluated when evaluating selector a. The
+ * other part of the condition can't be evaluated until b is available.
+ */
+ private ConstraintImpl selectorCondition;
public SelectorImpl(String nodeTypeName, String selectorName) {
this.nodeTypeName = nodeTypeName;
@@ -69,13 +79,18 @@ public class SelectorImpl extends Source
@Override
public String toString() {
- // TODO quote nodeTypeName?
- return nodeTypeName + " as " + getSelectorName();
+ return "[" + nodeTypeName + "] as " + selectorName;
}
@Override
public void prepare(MicroKernel mk) {
+ if (queryConstraint != null) {
+ queryConstraint.restrictPushDown(this);
+ }
+ for (JoinConditionImpl c : allJoinConditions) {
+ c.restrictPushDown(this);
+ }
index = query.getBestIndex(createFilter());
}
@@ -86,20 +101,27 @@ public class SelectorImpl extends Source
@Override
public String getPlan() {
- return nodeTypeName + " as " + getSelectorName() + " /* " + index.getPlan(createFilter()) + " */";
+ StringBuilder buff = new StringBuilder();
+ buff.append(toString());
+ buff.append(" /* ").append(index.getPlan(createFilter()));
+ if (selectorCondition != null) {
+ buff.append(" where ").append(selectorCondition);
+ }
+ buff.append(" */");
+ return buff.toString();
}
private FilterImpl createFilter() {
FilterImpl f = new FilterImpl(this);
f.setNodeType(nodeTypeName);
if (joinCondition != null) {
- joinCondition.apply(f);
+ joinCondition.restrict(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);
+ queryConstraint.restrict(f);
}
}
return f;
@@ -107,9 +129,25 @@ public class SelectorImpl extends Source
@Override
public boolean next() {
+ while (true) {
+ if (!nextNode()) {
+ return false;
+ }
+ if (selectorCondition != null && !selectorCondition.evaluate()) {
+ continue;
+ }
+ if (joinCondition != null && !joinCondition.evaluate()) {
+ continue;
+ }
+ return true;
+ }
+ }
+
+ private boolean nextNode() {
if (cursor == null) {
return false;
}
+ scanCount++;
while (true) {
boolean result = cursor.next();
if (!result) {
@@ -191,4 +229,16 @@ public class SelectorImpl extends Source
return null;
}
+ public long getScanCount() {
+ return scanCount;
+ }
+
+ public void restrictSelector(ConstraintImpl constraint) {
+ if (selectorCondition == null) {
+ selectorCondition = constraint;
+ } else {
+ selectorCondition = new AndImpl(selectorCondition, constraint);
+ }
+ }
+
}
Modified: 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=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java Fri Sep 7 13:14:40 2012
@@ -18,6 +18,7 @@
*/
package org.apache.jackrabbit.oak.query.ast;
+import java.util.ArrayList;
import org.apache.jackrabbit.mk.api.MicroKernel;
import org.apache.jackrabbit.oak.query.Query;
import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -27,9 +28,38 @@ import org.apache.jackrabbit.oak.spi.sta
*/
public abstract class SourceImpl extends AstElement {
+ /**
+ * The WHERE clause of the query.
+ */
protected ConstraintImpl queryConstraint;
+
+ /**
+ * The join condition of this selector that can be evaluated at execution
+ * time. For the query "select * from nt:base as a inner join nt:base as b
+ * on a.x = b.x", the join condition "a.x = b.x" is only set for the
+ * selector b, as selector a can't evaluate it if it is executed first
+ * (until b is executed).
+ */
protected JoinConditionImpl joinCondition;
+
+ /**
+ * The list of all join conditions this selector is involved. For the query
+ * "select * from nt:base as a inner join nt:base as b on a.x =
+ * b.x", the join condition "a.x = b.x" is set for both selectors a and b,
+ * so both can check if the property x is set.
+ */
+ protected ArrayList<JoinConditionImpl> allJoinConditions =
+ new ArrayList<JoinConditionImpl>();
+
+ /**
+ * Whether this selector is the right hand side of a join.
+ */
protected boolean join;
+
+ /**
+ * Whether this selector is the right hand side of a left outer join.
+ * Right outer joins are converted to left outer join.
+ */
protected boolean outerJoin;
/**
@@ -42,12 +72,17 @@ public abstract class SourceImpl extends
}
/**
- * Set the join condition (the ON ... condition).
+ * Add the join condition (the ON ... condition).
*
* @param joinCondition the join condition
+ * @param forThisSelector if set, the join condition can only be evaluated
+ * when all previous selectors are executed.
*/
- public void setJoinCondition(JoinConditionImpl joinCondition) {
- this.joinCondition = joinCondition;
+ public void addJoinCondition(JoinConditionImpl joinCondition, boolean forThisSelector) {
+ if (forThisSelector) {
+ this.joinCondition = joinCondition;
+ }
+ allJoinConditions.add(joinCondition);
}
/**
@@ -74,7 +109,7 @@ public abstract class SourceImpl extends
* @return the selector, or null
*/
public abstract SelectorImpl getSelector(String selectorName);
-
+
/**
* Get the selector with the given name, or fail if not found.
*
Modified: 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=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java Fri Sep 7 13:14:40 2012
@@ -67,4 +67,9 @@ public class UpperCaseImpl extends Dynam
// TODO UPPER(x) conditions: can use IS NOT NULL?
}
+ @Override
+ public boolean canRestrictSelector(SelectorImpl s) {
+ return operand.canRestrictSelector(s);
+ }
+
}
Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java?rev=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java Fri Sep 7 13:14:40 2012
@@ -52,6 +52,7 @@ public abstract class AbstractQueryTest
return new ContentRepositoryImpl(mk, qip, im);
}
+ @Override
@Before
public void before() throws Exception {
super.before();
Modified: 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=1382008&r1=1382007&r2=1382008&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryTest.java Fri Sep 7 13:14:40 2012
@@ -15,6 +15,7 @@ package org.apache.jackrabbit.oak.query;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -78,7 +79,8 @@ public class QueryTest extends AbstractQ
result = executeQuery("explain select * from [nt:base] where id = 1 order by id",
QueryEngineImpl.SQL2, null).getRows().iterator();
assertTrue(result.hasNext());
- assertEquals("nt:base as nt:base /* traverse \"//*\" */",
+ assertEquals("[nt:base] as nt:base " +
+ "/* traverse \"//*\" where nt:base.id = cast('1' as long) */",
result.next().getValue("plan").getString());
}
@@ -103,23 +105,24 @@ public class QueryTest extends AbstractQ
w.println("xpath2sql " + line);
XPathToSQL2Converter c = new XPathToSQL2Converter();
String got;
- boolean failed;
try {
got = c.convert(line);
- failed = false;
+ executeQuery(got, QueryEngineImpl.SQL2, null);
} catch (ParseException e) {
got = "invalid: " + e.getMessage().replace('\n', ' ');
- failed = true;
+ } catch (Exception e) {
+ // e.printStackTrace();
+ got = "error: " + e.toString().replace('\n', ' ');
}
line = r.readLine().trim();
w.println(got);
if (!line.equals(got)) {
errors = true;
}
- if (!failed) {
- executeQuery(got, QueryEngineImpl.SQL2, null);
- }
- } else if (line.startsWith("select") || line.startsWith("explain") || line.startsWith("sql1")) {
+ } else if (line.startsWith("select") ||
+ line.startsWith("explain") ||
+ line.startsWith("measure") ||
+ line.startsWith("sql1")) {
w.println(line);
String language = QueryEngineImpl.SQL2;
if (line.startsWith("sql1")) {
@@ -169,6 +172,7 @@ public class QueryTest extends AbstractQ
String diff = line.substring(spaceIndex).trim();
mk.commit(path, diff, mk.getHeadRevision(), "");
}
+ w.flush();
}
} finally {
w.close();
@@ -181,6 +185,7 @@ public class QueryTest extends AbstractQ
}
private List<String> executeQuery(String query, String language) {
+ long time = System.currentTimeMillis();
List<String> lines = new ArrayList<String>();
try {
Result result = executeQuery(query, language, null);
@@ -195,6 +200,10 @@ public class QueryTest extends AbstractQ
} catch (IllegalArgumentException e) {
lines.add(e.toString());
}
+ time = System.currentTimeMillis() - time;
+ if (time > 3000) {
+ fail("Query took too long: " + query + " took " + time + " ms");
+ }
return lines;
}