Author: thomasm Date: Thu May 31 14:43:30 2012 New Revision: 1344752 URL: http://svn.apache.org/viewvc?rev=1344752&view=rev Log: OAK-28 Query implementation
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/QueryEngine.java 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/QueryEngineImpl.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/AstElement.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/NodeNameImpl.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.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/queryXpathTest.txt Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/QueryEngine.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/QueryEngine.java?rev=1344752&r1=1344751&r2=1344752&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/QueryEngine.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/QueryEngine.java Thu May 31 14:43:30 2012 @@ -19,6 +19,7 @@ package org.apache.jackrabbit.oak.api; import java.text.ParseException; import java.util.List; import java.util.Map; +import org.apache.jackrabbit.oak.namepath.NamePathMapper; /** * The query engine allows to parse and execute queries. @@ -53,12 +54,14 @@ public interface QueryEngine { * @param limit the maximum result set size * @param offset the number of rows to skip * @param bindings the bind variable value bindings + * @param namePathMapper the name and path mapper to use * @return the result * @throws ParseException if the statement could not be parsed * @throws IllegalArgumentException if there was an error executing the query */ Result executeQuery(String statement, String language, ContentSession session, - long limit, long offset, Map<String, CoreValue> bindings) throws ParseException; + long limit, long offset, Map<String, CoreValue> bindings, + NamePathMapper namePathMapper) throws ParseException; // TODO pass namespace mapping // TODO pass node type information (select * from [xyz] is supposed to return at least the mandatory columns for xyz) 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=1344752&r1=1344751&r2=1344752&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 Thu May 31 14:43:30 2012 @@ -23,6 +23,7 @@ import org.apache.jackrabbit.mk.api.Micr import org.apache.jackrabbit.oak.api.ContentSession; import org.apache.jackrabbit.oak.api.CoreValue; import org.apache.jackrabbit.oak.api.CoreValueFactory; +import org.apache.jackrabbit.oak.namepath.NamePathMapper; import org.apache.jackrabbit.oak.query.ast.AstVisitorBase; import org.apache.jackrabbit.oak.query.ast.BindVariableValueImpl; import org.apache.jackrabbit.oak.query.ast.ChildNodeImpl; @@ -73,6 +74,7 @@ public class Query { private boolean prepared; private final CoreValueFactory valueFactory; private ContentSession session; + private NamePathMapper namePathMapper; Query(SourceImpl source, ConstraintImpl constraint, OrderingImpl[] orderings, ColumnImpl[] columns, CoreValueFactory valueFactory) { @@ -496,4 +498,12 @@ public class Query { this.session = session; } + public void setNamePathMapper(NamePathMapper namePathMapper) { + this.namePathMapper = namePathMapper; + } + + public NamePathMapper getNamePathMapper() { + return namePathMapper; + } + } Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java?rev=1344752&r1=1344751&r2=1344752&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java Thu May 31 14:43:30 2012 @@ -26,6 +26,7 @@ import org.apache.jackrabbit.oak.api.Con import org.apache.jackrabbit.oak.api.CoreValue; import org.apache.jackrabbit.oak.api.CoreValueFactory; import org.apache.jackrabbit.oak.api.QueryEngine; +import org.apache.jackrabbit.oak.namepath.NamePathMapper; import org.apache.jackrabbit.oak.query.index.FilterImpl; import org.apache.jackrabbit.oak.query.index.TraversingIndex; import org.apache.jackrabbit.oak.spi.QueryIndex; @@ -36,6 +37,7 @@ public class QueryEngineImpl implements static final String SQL2 = "JCR-SQL2"; private static final String XPATH = "xpath"; + private static final String JQOM = "JCR-JQOM"; private final MicroKernel mk; private final CoreValueFactory vf; @@ -51,7 +53,7 @@ public class QueryEngineImpl implements @Override public List<String> getSupportedQueryLanguages() { - return Arrays.asList(SQL2, XPATH); + return Arrays.asList(SQL2, XPATH, JQOM); } /** @@ -70,7 +72,7 @@ public class QueryEngineImpl implements private Query parseQuery(String statement, String language) throws ParseException { Query q; - if (SQL2.equals(language)) { + if (SQL2.equals(language) || JQOM.equals(language)) { q = parserSQL2.parse(statement); } else if (XPATH.equals(language)) { XPathToSQL2Converter converter = new XPathToSQL2Converter(); @@ -84,9 +86,11 @@ public class QueryEngineImpl implements @Override public ResultImpl executeQuery(String statement, String language, ContentSession session, - long limit, long offset, Map<String, CoreValue> bindings) throws ParseException { + long limit, long offset, Map<String, CoreValue> bindings, + NamePathMapper namePathMapper) throws ParseException { Query q = parseQuery(statement, language); q.setSession(session); + q.setNamePathMapper(namePathMapper); q.setLimit(limit); q.setOffset(offset); q.setMicroKernel(mk); 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=1344752&r1=1344751&r2=1344752&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java Thu May 31 14:43:30 2012 @@ -169,8 +169,8 @@ public class XPathToSQL2Converter { f.params.add(Literal.newString(path)); condition = add(condition, f); } else { - // TODO jcr:path is only a pseudo-property - Condition c = new Condition(new Property("jcr:path"), "=", Literal.newString(path)); + Function c = new Function("issamenode"); + c.params.add(Literal.newString(path)); condition = add(condition, c); } if (nodeName != null) { Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElement.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElement.java?rev=1344752&r1=1344751&r2=1344752&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElement.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElement.java Thu May 31 14:43:30 2012 @@ -24,6 +24,7 @@ import org.apache.jackrabbit.oak.api.Cor import org.apache.jackrabbit.oak.api.CoreValueFactory; import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.kernel.CoreValueMapper; +import org.apache.jackrabbit.oak.namepath.NamePathMapper; import org.apache.jackrabbit.oak.query.Query; import javax.jcr.PropertyType; @@ -125,5 +126,18 @@ abstract class AstElement { return true; } + protected String getOakPath(String jcrPath) { + NamePathMapper m = query.getNamePathMapper(); + if (m == null) { + // to simplify testing, a getNamePathMapper isn't required + return jcrPath; + } + String p = m.getOakPath(jcrPath); + if (p == null) { + throw new IllegalArgumentException("Not a valid JCR path: " + jcrPath); + } + return p; + } + } 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=1344752&r1=1344751&r2=1344752&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 Thu May 31 14:43:30 2012 @@ -64,7 +64,7 @@ public class ComparisonImpl extends Cons if (v1.getType() != v2.getType()) { // "the value of operand2 is converted to the // property type of the value of operand1" - v2 = convert(query.getValueFactory(), v2, v1.getType()); + v2 = convert(v2, v1.getType()); } switch (operator) { case EQUAL: @@ -85,7 +85,7 @@ public class ComparisonImpl extends Cons throw new IllegalArgumentException("Unknown operator: " + operator); } - private static CoreValue convert(CoreValueFactory vf, CoreValue v, int targetType) { + private CoreValue convert(CoreValue v, int targetType) { // TODO support full set of conversion features defined in the JCR spec // at 3.6.4 Property Type Conversion // re-use existing code if possible @@ -93,6 +93,7 @@ public class ComparisonImpl extends Cons if (sourceType == targetType) { return v; } + CoreValueFactory vf = query.getValueFactory(); switch (sourceType) { case PropertyType.STRING: switch(targetType) { @@ -122,7 +123,7 @@ public class ComparisonImpl extends Cons case PropertyType.DECIMAL: return vf.createValue(v.getString(), PropertyType.DECIMAL); case PropertyType.NAME: - return vf.createValue(v.getString(), PropertyType.NAME); + return vf.createValue(getOakPath(v.getString()), PropertyType.NAME); case PropertyType.PATH: return vf.createValue(v.getString(), PropertyType.PATH); case PropertyType.REFERENCE: 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=1344752&r1=1344751&r2=1344752&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 Thu May 31 14:43:30 2012 @@ -18,6 +18,7 @@ */ package org.apache.jackrabbit.oak.query.ast; +import javax.jcr.PropertyType; import org.apache.jackrabbit.oak.api.CoreValue; import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.query.index.FilterImpl; @@ -55,7 +56,11 @@ public class NodeNameImpl extends Dynami @Override public CoreValue currentValue() { String name = PathUtils.getName(selector.currentPath()); - return query.getValueFactory().createValue(name); + CoreValue v = query.getValueFactory().createValue(name); + String path = v.getString(); + // normalize paths (./name > name) + path = getOakPath(path); + return query.getValueFactory().createValue(path, PropertyType.NAME); } @Override @@ -64,10 +69,11 @@ public class NodeNameImpl extends Dynami throw new IllegalArgumentException("Invalid name value: " + v.toString()); } String path = v.getString(); + // normalize paths (./name > name) + path = getOakPath(path); if (PathUtils.isAbsolute(path)) { throw new IllegalArgumentException("NAME() comparison with absolute path are not allowed: " + path); } - // TODO normalize paths (./name > name) if (PathUtils.getDepth(path) > 1) { throw new IllegalArgumentException("NAME() comparison with relative path are not allowed: " + path); } 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=1344752&r1=1344751&r2=1344752&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 Thu May 31 14:43:30 2012 @@ -43,6 +43,7 @@ public class SameNodeImpl extends Constr @Override public boolean evaluate() { String p = getAbsolutePath(path); + // TODO normalize paths return selector.currentPath().equals(p); } @@ -69,6 +70,7 @@ public class SameNodeImpl extends Constr String p = getAbsolutePath(path); f.restrictPath(p, Filter.PathRestriction.EXACT); } + // TODO validate absolute path } } 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=1344752&r1=1344751&r2=1344752&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 Thu May 31 14:43:30 2012 @@ -191,7 +191,7 @@ public class QueryTest extends AbstractQ } private Result executeQuery(String statement, HashMap<String, CoreValue> sv) throws ParseException { - return qe.executeQuery(statement, QueryEngineImpl.SQL2, session, Long.MAX_VALUE, 0, sv); + return qe.executeQuery(statement, QueryEngineImpl.SQL2, session, Long.MAX_VALUE, 0, sv, null); } } Modified: jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/queryXpathTest.txt URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/queryXpathTest.txt?rev=1344752&r1=1344751&r2=1344752&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/queryXpathTest.txt (original) +++ jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/queryXpathTest.txt Thu May 31 14:43:30 2012 @@ -19,7 +19,7 @@ # * use ascii character only xpath /jcr:root/testdata/node[@jcr:primaryType] -select [jcr:path], [jcr:score], * from [nt:base] where ([jcr:primaryType] is not null) and ([jcr:path] = '/testdata/node') +select [jcr:path], [jcr:score], * from [nt:base] where ([jcr:primaryType] is not null) and issamenode('/testdata/node') xpath //testroot/*[@jcr:primaryType='nt:unstructured'] order by @prop2, @prop1 invalid: Query: //testroot/*(*)[@jcr:primaryType='nt:unstructured'] order by @prop2, @prop1; expected: non-path condition @@ -31,10 +31,10 @@ xpath /jcr:root/test//text() select [jcr:path], [jcr:score], * from [nt:base] where isdescendantnode('/test') and (name() = 'jcr:xmltext') xpath /jcr:root/test/jcr:xmltext -select [jcr:path], [jcr:score], * from [nt:base] where [jcr:path] = '/test/jcr:xmltext' +select [jcr:path], [jcr:score], * from [nt:base] where issamenode('/test/jcr:xmltext') xpath /jcr:root/test/text() -select [jcr:path], [jcr:score], * from [nt:base] where [jcr:path] = '/test/jcr:xmltext' +select [jcr:path], [jcr:score], * from [nt:base] where issamenode('/test/jcr:xmltext') xpath //*[@*='x'] select [jcr:path], [jcr:score], * from [nt:base] where [*] = 'x' @@ -87,6 +87,9 @@ select [jcr:path], [jcr:score], * from [ xpath /jcr:root/content/campaigns//element(*, PageContent)[(@sling:resourceType = 'teaser' or @sling:resourceType = 'newsletter' or @teaserPageType = 'newsletter' or @teaserPageType = 'tweet') and ((@onTime < xs:dateTime('2012-04-01T00:00:00.000+02:00')) or not(@onTime)) and ((@offTime >= xs:dateTime('2012-02-26T00:00:00.000+01:00')) or not(@offTime))] order by @onTime select [jcr:path], [jcr:score], * from [PageContent] where (((((([sling:resourceType] = 'teaser') or ([sling:resourceType] = 'newsletter')) or ([teaserPageType] = 'newsletter')) or ([teaserPageType] = 'tweet')) and (([onTime] < cast('2012-04-01T00:00:00.000+02:00' as date)) or ([onTime] is null))) and (([offTime] >= cast('2012-02-26T00:00:00.000+01:00' as date)) or ([offTime] is null))) and isdescendantnode('/content/campaigns') order by [onTime] +xpath /jcr:root/content/dam//element(*, asset) +select [jcr:path], [jcr:score], * from [asset] where isdescendantnode('/content/dam') + xpath /jcr:root/content/dam//element(*, asset)[jcr:content/metadata/@dam:scene] select [jcr:path], [jcr:score], * from [asset] where ([jcr:content/metadata/dam:scene] is not null) and isdescendantnode('/content/dam') @@ -129,7 +132,7 @@ xpath /jcr:root/nodes//element(*, my:typ select [jcr:path], [jcr:score], * from [my:type] where isdescendantnode('/nodes') xpath /jcr:root/some/element(nodes, my:type) -select [jcr:path], [jcr:score], * from [my:type] where [jcr:path] = '/some/nodes' +select [jcr:path], [jcr:score], * from [my:type] where issamenode('/some/nodes') xpath /jcr:root/some/nodes/element(*, my:type) select [jcr:path], [jcr:score], * from [my:type] where ischildnode('/some/nodes')