### Eclipse Workspace Patch 1.0 #P oak-core Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java (working copy) @@ -216,12 +216,14 @@ * Keep the returned path, to avoid returning duplicate entries. */ private final Set knownPaths = Sets.newHashSet(); + private final long maxMemoryEntries; PathIterator(Filter filter, String indexName) { this.filter = filter; this.indexName = indexName; parentPath = ""; currentPath = "/"; + this.maxMemoryEntries = filter.getQueryEngineSettings().getLimitInMemory(); } void enqueue(Iterator it) { @@ -272,7 +274,7 @@ readCount++; if (readCount % 1000 == 0) { - FilterIterators.checkReadLimit(readCount); + FilterIterators.checkReadLimit(readCount, maxMemoryEntries); LOG.warn("Traversed " + readCount + " nodes using index " + indexName + " with filter " + filter); } Index: src/main/java/org/apache/jackrabbit/oak/core/SystemRoot.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/core/SystemRoot.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/core/SystemRoot.java (working copy) @@ -20,6 +20,7 @@ import org.apache.jackrabbit.oak.Oak; import org.apache.jackrabbit.oak.api.Root; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.spi.commit.CommitHook; import org.apache.jackrabbit.oak.spi.commit.EmptyHook; import org.apache.jackrabbit.oak.spi.query.CompositeQueryIndexProvider; @@ -51,22 +52,26 @@ private SystemRoot( NodeStore store, CommitHook hook, String workspaceName, - SecurityProvider securityProvider, QueryIndexProvider indexProvider, + SecurityProvider securityProvider, + QueryEngineSettings queryEngineSettings, + QueryIndexProvider indexProvider, ContentSessionImpl session) { super(store, hook, workspaceName, SystemSubject.INSTANCE, - securityProvider, indexProvider, session); + securityProvider, queryEngineSettings, indexProvider, session); } public SystemRoot(final NodeStore store, final CommitHook hook, final String workspaceName, - final SecurityProvider securityProvider, final QueryIndexProvider indexProvider) { - this(store, hook, workspaceName, securityProvider, indexProvider, + final SecurityProvider securityProvider, final QueryEngineSettings queryEngineSettings, + final QueryIndexProvider indexProvider) { + this(store, hook, workspaceName, securityProvider, queryEngineSettings, indexProvider, new ContentSessionImpl( LOGIN_CONTEXT, securityProvider, workspaceName, - store, hook, indexProvider) { + store, hook, queryEngineSettings, indexProvider) { @Override public Root getLatestRoot() { return new SystemRoot( store, hook, workspaceName, securityProvider, + queryEngineSettings, indexProvider, this); } }); @@ -79,6 +84,7 @@ public SystemRoot(NodeStore store, CommitHook hook) { // FIXME: define proper default or pass workspace name with the constructor this(store, hook, Oak.DEFAULT_WORKSPACE_NAME, new OpenSecurityProvider(), + new QueryEngineSettings(), new CompositeQueryIndexProvider()); } Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java (revision 1580862) +++ src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java (working copy) @@ -215,9 +215,9 @@ "OrderedPropertyIndex index is used even when no index is available for filter " + filter); } - cursor = Cursors.newPathCursor(paths); + cursor = Cursors.newPathCursor(paths, filter.getQueryEngineSettings()); if (depth > 1) { - cursor = Cursors.newAncestorCursor(cursor, depth - 1); + cursor = Cursors.newAncestorCursor(cursor, depth - 1, filter.getQueryEngineSettings()); } } else { // if for some reasons it's not an Ordered Lookup we delegate up the chain Index: src/main/java/org/apache/jackrabbit/oak/core/ImmutableRoot.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/core/ImmutableRoot.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/core/ImmutableRoot.java (working copy) @@ -35,6 +35,7 @@ import org.apache.jackrabbit.oak.plugins.tree.ImmutableTree; import org.apache.jackrabbit.oak.query.ExecutionContext; import org.apache.jackrabbit.oak.query.QueryEngineImpl; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.spi.state.NodeState; /** @@ -117,6 +118,7 @@ protected ExecutionContext getExecutionContext() { return new ExecutionContext( rootTree.getNodeState(), ImmutableRoot.this, + new QueryEngineSettings(), new PropertyIndexProvider()); } }; Index: src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/core/MutableRoot.java (working copy) @@ -43,6 +43,7 @@ import org.apache.jackrabbit.oak.plugins.index.diffindex.UUIDDiffIndexProviderWrapper; import org.apache.jackrabbit.oak.query.ExecutionContext; import org.apache.jackrabbit.oak.query.QueryEngineImpl; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.spi.commit.CommitHook; import org.apache.jackrabbit.oak.spi.commit.CommitInfo; import org.apache.jackrabbit.oak.spi.commit.CompositeEditorProvider; @@ -76,6 +77,8 @@ private final Subject subject; private final SecurityProvider securityProvider; + + private final QueryEngineSettings queryEngineSettings; private final QueryIndexProvider indexProvider; @@ -142,6 +145,7 @@ String workspaceName, Subject subject, SecurityProvider securityProvider, + QueryEngineSettings queryEngineSettings, QueryIndexProvider indexProvider, ContentSessionImpl session) { this.store = checkNotNull(store); @@ -149,6 +153,7 @@ this.workspaceName = checkNotNull(workspaceName); this.subject = checkNotNull(subject); this.securityProvider = checkNotNull(securityProvider); + this.queryEngineSettings = queryEngineSettings; this.indexProvider = indexProvider; this.session = checkNotNull(session); @@ -303,7 +308,7 @@ provider, getBaseState(), getRootState()); } return new ExecutionContext( - getBaseState(), MutableRoot.this, provider); + getBaseState(), MutableRoot.this, queryEngineSettings, provider); } }; } Index: src/main/java/org/apache/jackrabbit/oak/spi/query/Cursors.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/spi/query/Cursors.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/spi/query/Cursors.java (working copy) @@ -25,6 +25,7 @@ import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry; import org.apache.jackrabbit.oak.query.FilterIterators; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.query.index.IndexRowImpl; import org.apache.jackrabbit.oak.spi.query.Filter.PathRestriction; import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry; @@ -57,8 +58,8 @@ * @param paths the paths to iterate over (must return distinct paths) * @return the Cursor. */ - public static Cursor newPathCursor(Iterable paths) { - return new PathCursor(paths.iterator(), true); + public static Cursor newPathCursor(Iterable paths, QueryEngineSettings settings) { + return new PathCursor(paths.iterator(), true, settings); } /** @@ -68,8 +69,8 @@ * @param paths the paths to iterate over (might contain duplicate entries) * @return the Cursor. */ - public static Cursor newPathCursorDistinct(Iterable paths) { - return new PathCursor(paths.iterator(), true); + public static Cursor newPathCursorDistinct(Iterable paths, QueryEngineSettings settings) { + return new PathCursor(paths.iterator(), true, settings); } /** @@ -96,10 +97,10 @@ * @param level the ancestor level. Must be >= 1. * @return cursor over the ancestors of c at level. */ - public static Cursor newAncestorCursor(Cursor c, int level) { + public static Cursor newAncestorCursor(Cursor c, int level, QueryEngineSettings settings) { checkNotNull(c); checkArgument(level >= 1); - return new AncestorCursor(c, level); + return new AncestorCursor(c, level, settings); } /** @@ -120,8 +121,8 @@ */ private static class AncestorCursor extends PathCursor { - public AncestorCursor(Cursor cursor, int level) { - super(transform(cursor, level), true); + public AncestorCursor(Cursor cursor, int level, QueryEngineSettings settings) { + super(transform(cursor, level), true, settings); } private static Iterator transform(Cursor cursor, final int level) { @@ -156,16 +157,17 @@ private final Iterator iterator; - public PathCursor(Iterator paths, boolean distinct) { + public PathCursor(Iterator paths, boolean distinct, final QueryEngineSettings settings) { Iterator it = paths; if (distinct) { it = Iterators.filter(it, new Predicate() { private final HashSet known = new HashSet(); + private final long maxMemoryEntries = settings.getLimitInMemory(); @Override public boolean apply(@Nullable String input) { - FilterIterators.checkMemoryLimit(known.size()); + FilterIterators.checkMemoryLimit(known.size(), maxMemoryEntries); // Set.add returns true for new entries return known.add(input); } @@ -210,9 +212,12 @@ private boolean init; private boolean closed; + + private final long maxReadEntries; public TraversingCursor(Filter filter, NodeState rootState) { this.filter = filter; + this.maxReadEntries = filter.getQueryEngineSettings().getLimitReads(); String path = filter.getPath(); parentPath = null; @@ -294,7 +299,7 @@ readCount++; if (readCount % 1000 == 0) { - FilterIterators.checkReadLimit(readCount); + FilterIterators.checkReadLimit(readCount, maxReadEntries); LOG.warn("Traversed " + readCount + " nodes with filter " + filter + "; consider creating an index or changing the query"); } Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java (working copy) @@ -66,7 +66,7 @@ throw new IllegalStateException( "NodeType index is used even when no index is available for filter " + filter); } - return Cursors.newPathCursorDistinct(lookup.query(filter)); + return Cursors.newPathCursorDistinct(lookup.query(filter), filter.getQueryEngineSettings()); } @Override Index: src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettings.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettings.java (revision 0) +++ src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettings.java (working copy) @@ -0,0 +1,48 @@ +/* + * 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; + +/** + * Settings of the query engine. + */ +public class QueryEngineSettings { + + public static final int DEFAULT_QUERY_LIMIT_IN_MEMORY = 10000; + public static final int DEFAULT_QUERY_LIMIT_READS = 100000; + + private long limitInMemory = DEFAULT_QUERY_LIMIT_IN_MEMORY; + private long limitReads = DEFAULT_QUERY_LIMIT_READS; + + public long getLimitInMemory() { + return limitInMemory; + } + + public void setLimitInMemory(long limitInMemory) { + this.limitInMemory = limitInMemory; + } + + public long getLimitReads() { + return limitReads; + } + + public void setLimitReads(long limitReads) { + this.limitReads = limitReads; + } + +} Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/diffindex/DiffIndex.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/index/diffindex/DiffIndex.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/plugins/index/diffindex/DiffIndex.java (working copy) @@ -41,7 +41,7 @@ @Override public Cursor query(Filter filter, NodeState rootState) { - return Cursors.newPathCursor(collector.getResults(filter)); + return Cursors.newPathCursor(collector.getResults(filter), filter.getQueryEngineSettings()); } @Override Index: src/test/java/org/apache/jackrabbit/oak/query/IteratorsTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/query/IteratorsTest.java (revision 1580856) +++ src/test/java/org/apache/jackrabbit/oak/query/IteratorsTest.java (working copy) @@ -35,6 +35,8 @@ */ public class IteratorsTest { + private QueryEngineSettings settings = new QueryEngineSettings(); + private static final Comparator INT_COMP = new Comparator() { @Override @@ -46,10 +48,10 @@ @Test public void distinct() { - assertEquals("", toString(FilterIterators.newDistinct(it()))); - assertEquals("1", toString(FilterIterators.newDistinct(it(1)))); - assertEquals("1, 2", toString(FilterIterators.newDistinct(it(1, 2)))); - assertEquals("1, 2, 3", toString(FilterIterators.newDistinct(it(1, 2, 1, 3, 3, 1)))); + assertEquals("", toString(FilterIterators.newDistinct(it(), settings))); + assertEquals("1", toString(FilterIterators.newDistinct(it(1), settings))); + assertEquals("1, 2", toString(FilterIterators.newDistinct(it(1, 2), settings))); + assertEquals("1, 2, 3", toString(FilterIterators.newDistinct(it(1, 2, 1, 3, 3, 1), settings))); } @Test @@ -84,15 +86,15 @@ @Test public void sort() { - assertEquals("", toString(FilterIterators.newSort(it(new Integer[]{}), INT_COMP, 0))); - assertEquals("", toString(FilterIterators.newSort(it(1), INT_COMP, 0))); - assertEquals("1", toString(FilterIterators.newSort(it(1), INT_COMP, 1))); - assertEquals("1", toString(FilterIterators.newSort(it(1), INT_COMP, 2))); - assertEquals("1", toString(FilterIterators.newSort(it(1, 2), INT_COMP, 1))); - assertEquals("1", toString(FilterIterators.newSort(it(2, 1), INT_COMP, 1))); - assertEquals("1, 2", toString(FilterIterators.newSort(it(1, 2, 3), INT_COMP, 2))); - assertEquals("1, 2", toString(FilterIterators.newSort(it(3, 2, 1), INT_COMP, 2))); - assertEquals("1, 1, 2", toString(FilterIterators.newSort(it(3, 3, 2, 1, 1), INT_COMP, 3))); + assertEquals("", toString(FilterIterators.newSort(it(new Integer[]{}), INT_COMP, 0, settings))); + assertEquals("", toString(FilterIterators.newSort(it(1), INT_COMP, 0, settings))); + assertEquals("1", toString(FilterIterators.newSort(it(1), INT_COMP, 1, settings))); + assertEquals("1", toString(FilterIterators.newSort(it(1), INT_COMP, 2, settings))); + assertEquals("1", toString(FilterIterators.newSort(it(1, 2), INT_COMP, 1, settings))); + assertEquals("1", toString(FilterIterators.newSort(it(2, 1), INT_COMP, 1, settings))); + assertEquals("1, 2", toString(FilterIterators.newSort(it(1, 2, 3), INT_COMP, 2, settings))); + assertEquals("1, 2", toString(FilterIterators.newSort(it(3, 2, 1), INT_COMP, 2, settings))); + assertEquals("1, 1, 2", toString(FilterIterators.newSort(it(3, 3, 2, 1, 1), INT_COMP, 3, settings))); } @Test @@ -101,47 +103,47 @@ // no filtering assertEquals("3, 3, 2, 1", toString(FilterIterators.newCombinedFilter( - it(3, 3, 2, 1), false, Long.MAX_VALUE, 0, null))); + it(3, 3, 2, 1), false, Long.MAX_VALUE, 0, null, settings))); // distinct assertEquals("3, 2, 1", toString(FilterIterators.newCombinedFilter( - it(3, 3, 2, 1), true, Long.MAX_VALUE, 0, null))); + it(3, 3, 2, 1), true, Long.MAX_VALUE, 0, null, settings))); // order by assertEquals("1, 2, 3, 3", toString(FilterIterators.newCombinedFilter( - it(3, 3, 2, 1), false, Long.MAX_VALUE, 0, INT_COMP))); + it(3, 3, 2, 1), false, Long.MAX_VALUE, 0, INT_COMP, settings))); // distinct & order by assertEquals("1, 2, 3", toString(FilterIterators.newCombinedFilter( - it(3, 3, 2, 1), true, Long.MAX_VALUE, 0, INT_COMP))); + it(3, 3, 2, 1), true, Long.MAX_VALUE, 0, INT_COMP, settings))); // limit assertEquals("3, 3", toString(FilterIterators.newCombinedFilter( - it(3, 3, 2, 1), false, 2, 0, null))); + it(3, 3, 2, 1), false, 2, 0, null, settings))); // offset assertEquals("3, 2, 1", toString(FilterIterators.newCombinedFilter( - it(3, 3, 2, 1), false, Long.MAX_VALUE, 1, null))); + it(3, 3, 2, 1), false, Long.MAX_VALUE, 1, null, settings))); // limit & offset assertEquals("3, 2", toString(FilterIterators.newCombinedFilter( - it(3, 3, 2, 1), false, 2, 1, null))); + it(3, 3, 2, 1), false, 2, 1, null, settings))); // distinct & limit & offset assertEquals("2, 1", toString(FilterIterators.newCombinedFilter( - it(3, 3, 2, 1), true, 2, 1, null))); + it(3, 3, 2, 1), true, 2, 1, null, settings))); // distinct & limit & offset & order by assertEquals("2, 3", toString(FilterIterators.newCombinedFilter( - it(3, 3, 2, 1), true, 2, 1, INT_COMP))); + it(3, 3, 2, 1), true, 2, 1, INT_COMP, settings))); } Index: src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdateTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdateTest.java (revision 1580856) +++ src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexUpdateTest.java (working copy) @@ -35,6 +35,7 @@ import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider; import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexLookup; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.query.ast.SelectorImpl; import org.apache.jackrabbit.oak.query.index.FilterImpl; import org.apache.jackrabbit.oak.spi.commit.CommitInfo; @@ -201,7 +202,7 @@ NodeState types = system.getChildNode(JCR_NODE_TYPES); NodeState type = types.getChildNode(NT_BASE); SelectorImpl selector = new SelectorImpl(type, NT_BASE); - Filter filter = new FilterImpl(selector, "SELECT * FROM [nt:base]"); + Filter filter = new FilterImpl(selector, "SELECT * FROM [nt:base]", new QueryEngineSettings()); return Sets.newHashSet(lookup.query(filter, name, PropertyValues.newString(value))); } Index: src/main/java/org/apache/jackrabbit/oak/spi/lifecycle/CompositeWorkspaceInitializer.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/spi/lifecycle/CompositeWorkspaceInitializer.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/spi/lifecycle/CompositeWorkspaceInitializer.java (working copy) @@ -22,6 +22,7 @@ import java.util.Collection; import javax.annotation.Nonnull; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.spi.commit.CommitHook; import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; @@ -46,9 +47,9 @@ } @Override - public void initialize(NodeBuilder builder, String workspaceName, QueryIndexProvider indexProvider, CommitHook commitHook) { + public void initialize(NodeBuilder builder, String workspaceName, QueryEngineSettings queryEngineSettings, QueryIndexProvider indexProvider, CommitHook commitHook) { for (WorkspaceInitializer tracker : initializers) { - tracker.initialize(builder, workspaceName, indexProvider, commitHook); + tracker.initialize(builder, workspaceName, queryEngineSettings, indexProvider, commitHook); } } Index: src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java (revision 1580856) +++ src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java (working copy) @@ -38,7 +38,7 @@ private final NodeState types = INITIAL_CONTENT.getChildNode(JCR_SYSTEM) .getChildNode(JCR_NODE_TYPES); - private final SQL2Parser p = new SQL2Parser(NamePathMapper.DEFAULT, types); + private final SQL2Parser p = new SQL2Parser(NamePathMapper.DEFAULT, types, new QueryEngineSettings()); private Filter createFilter(String xpath) throws ParseException { String sql = new XPathToSQL2Converter().convert(xpath); Index: src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java (working copy) @@ -101,8 +101,9 @@ NodeState types = context.getBaseState() .getChildNode(JCR_SYSTEM) .getChildNode(JCR_NODE_TYPES); + QueryEngineSettings settings = context.getSettings(); - SQL2Parser parser = new SQL2Parser(mapper, types); + SQL2Parser parser = new SQL2Parser(mapper, types, settings); if (language.endsWith(NO_LITERALS)) { language = language.substring(0, language.length() - NO_LITERALS.length()); parser.setAllowNumberLiterals(false); Index: src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java (working copy) @@ -308,7 +308,7 @@ public void execute(NodeState rootState) { QueryIndex index = plan.getIndex(); if (index == null) { - cursor = Cursors.newPathCursor(new ArrayList()); + cursor = Cursors.newPathCursor(new ArrayList(), query.getSettings()); return; } IndexPlan p = plan.getIndexPlan(); @@ -346,7 +346,7 @@ */ @Override public FilterImpl createFilter(boolean preparing) { - FilterImpl f = new FilterImpl(this, query.getStatement()); + FilterImpl f = new FilterImpl(this, query.getStatement(), query.getSettings()); f.setPreparing(preparing); if (joinCondition != null) { joinCondition.restrict(f); Index: src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettingsService.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettingsService.java (revision 0) +++ src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettingsService.java (working copy) @@ -0,0 +1,90 @@ +/* + * 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.util.Map; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.ConfigurationPolicy; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Property; +import org.apache.jackrabbit.oak.commons.PropertiesUtil; +import org.osgi.service.component.ComponentContext; + +/** + * The OSGi service to configure the query engine. + */ +@Component +public class QueryEngineSettingsService { + + private static final String QUERY_LIMIT_IN_MEMORY_NAME = "oak.queryLimitInMemory"; + private static final String QUERY_LIMIT_READS_NAME = "oak.queryLimitReads"; + + public static final int DEFAULT_QUERY_LIMIT_IN_MEMORY = 10000; + public static final int DEFAULT_QUERY_LIMIT_READS = 100000; + + @Property(intValue = DEFAULT_QUERY_LIMIT_IN_MEMORY) + private static final String QUERY_LIMIT_IN_MEMORY = "queryLimitInMemory"; + + @Property(intValue = DEFAULT_QUERY_LIMIT_READS) + private static final String QUERY_LIMIT_READS = "queryLimitReads"; + + private ComponentContext context; + + @Activate + protected void activate(ComponentContext context, Map config) throws Exception { + this.context = context; + } + + @Deactivate + protected void deactivate() { + } + + int getQueryLimitInMemory() { + return PropertiesUtil.toInteger(prop(QUERY_LIMIT_IN_MEMORY, QUERY_LIMIT_IN_MEMORY_NAME), DEFAULT_QUERY_LIMIT_IN_MEMORY); + } + + private Object prop(String propName, String fwkPropName) { + //Prefer framework property first + Object value = context.getBundleContext().getProperty(fwkPropName); + if (value != null) { + return value; + } + + //Fallback to one from config + return context.getProperties().get(propName); + } + +// /** +// * How many nodes a query may read at most into memory, for "order by" and +// * "distinct" queries. If this limit is exceeded, the query throws an +// * exception. +// */ +// public static final int QUERY_LIMIT_IN_MEMORY = +// Integer.getInteger("oak.queryLimitInMemory", 10000); +// +// /** +// * How many nodes a query may read at most (raw read operations, including +// * skipped nodes). If this limit is exceeded, the query throws an exception. +// */ +// public static final int QUERY_LIMIT_READS = +// Integer.getInteger("oak.queryLimitReads", 100000); + +} Index: src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java (revision 1580862) +++ src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java (working copy) @@ -43,6 +43,7 @@ import org.apache.jackrabbit.oak.plugins.index.IndexUtils; import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.OrderDirection; import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.query.ast.Operator; import org.apache.jackrabbit.oak.query.ast.SelectorImpl; import org.apache.jackrabbit.oak.query.index.FilterImpl; @@ -416,7 +417,7 @@ NodeState types = system.getChildNode(JCR_NODE_TYPES); NodeState type = types.getChildNode(nodeTypeName); SelectorImpl selector = new SelectorImpl(type, nodeTypeName); - return new FilterImpl(selector, "SELECT * FROM [" + nodeTypeName + "]"); + return new FilterImpl(selector, "SELECT * FROM [" + nodeTypeName + "]", new QueryEngineSettings()); } private static QueryIndex.OrderEntry createOrderEntry(String property, Index: src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationInitializer.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationInitializer.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/security/authorization/AuthorizationInitializer.java (working copy) @@ -20,6 +20,7 @@ import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.plugins.index.IndexUtils; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionConstants; import org.apache.jackrabbit.oak.spi.commit.CommitHook; import org.apache.jackrabbit.oak.spi.lifecycle.WorkspaceInitializer; @@ -45,6 +46,7 @@ @Override public void initialize( NodeBuilder builder, String workspaceName, + QueryEngineSettings queryEngineSettings, QueryIndexProvider indexProvider, CommitHook commitHook) { // property index for rep:principalName stored in ACEs NodeBuilder index = IndexUtils.getOrCreateOakIndex(builder); Index: src/test/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexTest.java (revision 1580856) +++ src/test/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexTest.java (working copy) @@ -37,6 +37,7 @@ import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider; import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore; import org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.query.ast.SelectorImpl; import org.apache.jackrabbit.oak.query.index.FilterImpl; import org.apache.jackrabbit.oak.spi.commit.CommitInfo; @@ -101,7 +102,7 @@ NodeState types = system.getChildNode(JCR_NODE_TYPES); NodeState type = types.getChildNode(nodeTypeName); SelectorImpl selector = new SelectorImpl(type, nodeTypeName); - return new FilterImpl(selector, "SELECT * FROM [" + nodeTypeName + "]"); + return new FilterImpl(selector, "SELECT * FROM [" + nodeTypeName + "]", new QueryEngineSettings()); } private static void checkCursor(Cursor cursor, String... matches) { Index: src/main/java/org/apache/jackrabbit/oak/spi/lifecycle/WorkspaceInitializer.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/spi/lifecycle/WorkspaceInitializer.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/spi/lifecycle/WorkspaceInitializer.java (working copy) @@ -16,6 +16,7 @@ */ package org.apache.jackrabbit.oak.spi.lifecycle; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.spi.commit.CommitHook; import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; @@ -33,6 +34,7 @@ @Override public void initialize( NodeBuilder builder, String workspaceName, + QueryEngineSettings queryEngineSettings, QueryIndexProvider indexProvider, CommitHook commitHook) { } }; @@ -49,6 +51,7 @@ */ void initialize(NodeBuilder builder, String workspaceName, + QueryEngineSettings queryEngineSettings, QueryIndexProvider indexProvider, CommitHook commitHook); } Index: src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java (working copy) @@ -27,6 +27,7 @@ import javax.jcr.PropertyType; import org.apache.jackrabbit.oak.api.PropertyValue; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression; /** @@ -64,6 +65,8 @@ */ FullTextExpression getFullTextConstraint(); + QueryEngineSettings getQueryEngineSettings(); + /** * Whether the filter contains a native condition. * Index: src/main/java/org/apache/jackrabbit/oak/spi/lifecycle/OakInitializer.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/spi/lifecycle/OakInitializer.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/spi/lifecycle/OakInitializer.java (working copy) @@ -23,6 +23,7 @@ import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider; import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.spi.commit.CommitHook; import org.apache.jackrabbit.oak.spi.commit.CommitInfo; import org.apache.jackrabbit.oak.spi.commit.EditorHook; @@ -53,11 +54,12 @@ @Nonnull NodeStore store, @Nonnull String workspaceName, @Nonnull IndexEditorProvider indexEditor, + QueryEngineSettings queryEngineSettings, @Nonnull QueryIndexProvider indexProvider, @Nonnull CommitHook commitHook) { NodeBuilder builder = store.getRoot().builder(); for (WorkspaceInitializer wspInit : initializer) { - wspInit.initialize(builder, workspaceName, indexProvider, commitHook); + wspInit.initialize(builder, workspaceName, queryEngineSettings, indexProvider, commitHook); } try { CommitHook hook = new EditorHook(new IndexUpdateProvider(indexEditor)); Index: src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java (revision 1580856) +++ src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java (working copy) @@ -37,6 +37,7 @@ import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider; import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.query.ast.SelectorImpl; import org.apache.jackrabbit.oak.query.index.FilterImpl; import org.apache.jackrabbit.oak.query.index.TraversingIndex; @@ -291,7 +292,7 @@ NodeState types = system.getChildNode(JCR_NODE_TYPES); NodeState type = types.getChildNode(nodeTypeName); SelectorImpl selector = new SelectorImpl(type, nodeTypeName); - return new FilterImpl(selector, "SELECT * FROM [" + nodeTypeName + "]"); + return new FilterImpl(selector, "SELECT * FROM [" + nodeTypeName + "]", new QueryEngineSettings()); } /** Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java (working copy) @@ -85,23 +85,23 @@ if (pr.propertyType == REFERENCE) { String uuid = pr.first.getValue(STRING); String name = pr.propertyName; - return lookup(root, uuid, name, REF_NAME); + return lookup(root, uuid, name, REF_NAME, filter); } if (pr.propertyType == WEAKREFERENCE) { String uuid = pr.first.getValue(STRING); String name = pr.propertyName; - return lookup(root, uuid, name, WEAK_REF_NAME); + return lookup(root, uuid, name, WEAK_REF_NAME, filter); } } - return newPathCursor(new ArrayList()); + return newPathCursor(new ArrayList(), filter.getQueryEngineSettings()); } private static Cursor lookup(NodeState root, String uuid, - final String name, String index) { + final String name, String index, Filter filter) { NodeState indexRoot = root.getChildNode(INDEX_DEFINITIONS_NAME) .getChildNode(NAME); if (!indexRoot.exists()) { - return newPathCursor(new ArrayList()); + return newPathCursor(new ArrayList(), filter.getQueryEngineSettings()); } Iterable paths = STORE.query(new FilterImpl(), index + "(" + uuid + ")", indexRoot, index, ImmutableSet.of(uuid)); @@ -120,7 +120,7 @@ return getParentPath(path); } }); - return newPathCursor(paths); + return newPathCursor(paths, filter.getQueryEngineSettings()); } @Override Index: src/main/java/org/apache/jackrabbit/oak/query/UnionQueryImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/query/UnionQueryImpl.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/query/UnionQueryImpl.java (working copy) @@ -47,11 +47,13 @@ private long limit = Long.MAX_VALUE; private long offset; private long size = -1; + private final QueryEngineSettings settings; - UnionQueryImpl(boolean unionAll, Query left, Query right) { + UnionQueryImpl(boolean unionAll, Query left, Query right, QueryEngineSettings settings) { this.unionAll = unionAll; this.left = left; this.right = right; + this.settings = settings; } @Override @@ -235,7 +237,7 @@ } boolean distinct = !unionAll; Comparator orderBy = ResultRowImpl.getComparator(orderings); - it = FilterIterators.newCombinedFilter(it, distinct, limit, offset, orderBy); + it = FilterIterators.newCombinedFilter(it, distinct, limit, offset, orderBy, settings); return it; } Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java (working copy) @@ -205,9 +205,9 @@ if (paths == null) { throw new IllegalStateException("Property index is used even when no index is available for filter " + filter); } - Cursor c = Cursors.newPathCursor(paths); + Cursor c = Cursors.newPathCursor(paths, filter.getQueryEngineSettings()); if (depth > 1) { - c = Cursors.newAncestorCursor(c, depth - 1); + c = Cursors.newAncestorCursor(c, depth - 1, filter.getQueryEngineSettings()); } return c; } Index: src/main/java/org/apache/jackrabbit/oak/Oak.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/Oak.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/Oak.java (working copy) @@ -61,6 +61,7 @@ import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider; import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider; import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.spi.commit.CommitHook; import org.apache.jackrabbit.oak.spi.commit.CommitInfo; import org.apache.jackrabbit.oak.spi.commit.CompositeEditorProvider; @@ -86,6 +87,7 @@ import org.apache.jackrabbit.oak.spi.whiteboard.Registration; import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard; import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardAware; +import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -106,9 +108,11 @@ public static final String DEFAULT_WORKSPACE_NAME = "default"; private final NodeStore store; - + private final List initializers = newArrayList(); + private QueryEngineSettings queryEngineSettings = new QueryEngineSettings(); + private final List queryIndexProviders = newArrayList(); private final List indexEditorProviders = newArrayList(); @@ -306,6 +310,12 @@ return this; } + @Nonnull + public Oak with(@Nonnull QueryEngineSettings queryEngineSettings) { + this.queryEngineSettings = queryEngineSettings; + return this; + } + /** * Associates the given query index provider with the repository to * be created. @@ -446,7 +456,10 @@ if (securityProvider instanceof WhiteboardAware) { ((WhiteboardAware) securityProvider).setWhiteboard(whiteboard); } - + QueryEngineSettings queryEngineSettings = WhiteboardUtils.getService(whiteboard, QueryEngineSettings.class); + if (queryEngineSettings != null) { + this.queryEngineSettings = queryEngineSettings; + } return this; } @@ -506,7 +519,7 @@ } }); OakInitializer.initialize(workspaceInitializers, store, - defaultWorkspaceName, indexEditors, indexProvider, + defaultWorkspaceName, indexEditors, queryEngineSettings, indexProvider, CompositeHook.compose(initHooks)); // add index hooks later to prevent the OakInitializer to do excessive indexing @@ -526,6 +539,7 @@ store, CompositeHook.compose(commitHooks), defaultWorkspaceName, + queryEngineSettings, indexProvider, securityProvider); } Index: src/main/java/org/apache/jackrabbit/oak/query/FilterIterators.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/query/FilterIterators.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/query/FilterIterators.java (working copy) @@ -26,32 +26,33 @@ */ public class FilterIterators { - /** - * How many nodes a query may read at most into memory, for "order by" and - * "distinct" queries. If this limit is exceeded, the query throws an - * exception. - */ - public static final int QUERY_LIMIT_IN_MEMORY = - Integer.getInteger("oak.queryLimitInMemory", 10000); - - /** - * How many nodes a query may read at most (raw read operations, including - * skipped nodes). If this limit is exceeded, the query throws an exception. - */ - public static final int QUERY_LIMIT_READS = - Integer.getInteger("oak.queryLimitReads", 100000); +// /** +// * How many nodes a query may read at most into memory, for "order by" and +// * "distinct" queries. If this limit is exceeded, the query throws an +// * exception. +// */ +// public static final int QUERY_LIMIT_IN_MEMORY = +// Integer.getInteger("oak.queryLimitInMemory", 10000); +// +// /** +// * How many nodes a query may read at most (raw read operations, including +// * skipped nodes). If this limit is exceeded, the query throws an exception. +// */ +// public static final int QUERY_LIMIT_READS = +// Integer.getInteger("oak.queryLimitReads", 100000); /** * Verify the number of in-memory nodes is below the limit. * * @param count the number of nodes + * @param maxMemoryEntries the maximum number of nodes * @throws UnsupportedOperationException if the limit was exceeded */ - public static void checkMemoryLimit(long count) { - if (count > QUERY_LIMIT_IN_MEMORY) { + public static void checkMemoryLimit(long count, long maxMemoryEntries) { + if (count > maxMemoryEntries) { throw new UnsupportedOperationException( "The query read more than " + - QUERY_LIMIT_IN_MEMORY + " nodes in memory. " + + maxMemoryEntries + " nodes in memory. " + "To avoid running out of memory, processing was stopped."); } } @@ -62,27 +63,27 @@ * @param count the number of read operations * @throws UnsupportedOperationException if the limit was exceeded */ - public static void checkReadLimit(long count) { - if (count > QUERY_LIMIT_READS) { + public static void checkReadLimit(long count, long maxReadEntries) { + if (count > maxReadEntries) { throw new UnsupportedOperationException( "The query read or traversed more than " + - QUERY_LIMIT_READS + " nodes. " + + maxReadEntries + " nodes. " + "To avoid affecting other tasks, processing was stopped."); } } public static Iterator newCombinedFilter( Iterator it, boolean distinct, long limit, long offset, - Comparator orderBy) { + Comparator orderBy, QueryEngineSettings settings) { if (distinct) { - it = FilterIterators.newDistinct(it); + it = FilterIterators.newDistinct(it, settings); } if (orderBy != null) { // avoid overflow (both offset and limit could be Long.MAX_VALUE) int max = (int) Math.min(Integer.MAX_VALUE, Math.min(Integer.MAX_VALUE, offset) + Math.min(Integer.MAX_VALUE, limit)); - it = FilterIterators.newSort(it, orderBy, max); + it = FilterIterators.newSort(it, orderBy, max, settings); } if (offset != 0) { it = FilterIterators.newOffset(it, offset); @@ -93,8 +94,8 @@ return it; } - public static DistinctIterator newDistinct(Iterator it) { - return new DistinctIterator(it); + public static DistinctIterator newDistinct(Iterator it, QueryEngineSettings settings) { + return new DistinctIterator(it, settings); } public static Iterator newLimit(Iterator it, long limit) { @@ -105,8 +106,8 @@ return new OffsetIterator(it, offset); } - public static Iterator newSort(Iterator it, Comparator orderBy, int max) { - return new SortIterator(it, orderBy, max); + public static Iterator newSort(Iterator it, Comparator orderBy, int max, QueryEngineSettings settings) { + return new SortIterator(it, orderBy, max, settings); } /** @@ -119,12 +120,14 @@ static class DistinctIterator implements Iterator { private final Iterator source; + private final long maxMemoryEntries; private final HashSet distinctSet; private K current; private boolean end; - DistinctIterator(Iterator source) { + DistinctIterator(Iterator source, QueryEngineSettings settings) { this.source = source; + this.maxMemoryEntries = settings.getLimitInMemory(); distinctSet = new HashSet(); } @@ -135,7 +138,7 @@ while (source.hasNext()) { current = source.next(); if (distinctSet.add(current)) { - checkMemoryLimit(distinctSet.size()); + checkMemoryLimit(distinctSet.size(), maxMemoryEntries); return; } } @@ -181,14 +184,16 @@ static class SortIterator implements Iterator { private final Iterator source; + private final long maxMemoryEntries; private final Comparator orderBy; private Iterator result; private final int max; - SortIterator(Iterator source, Comparator orderBy, int max) { + SortIterator(Iterator source, Comparator orderBy, int max, QueryEngineSettings settings) { this.source = source; this.orderBy = orderBy; this.max = max; + this.maxMemoryEntries = settings.getLimitInMemory(); } private void init() { @@ -199,7 +204,7 @@ while (source.hasNext()) { K x = source.next(); list.add(x); - checkMemoryLimit(list.size()); + checkMemoryLimit(list.size(), maxMemoryEntries); // from time to time, sort and truncate // this should results in O(n*log(2*keep)) operations, // which is close to the optimum O(n*log(keep)) Index: src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java (working copy) @@ -102,6 +102,8 @@ private boolean supportSQL1; private NamePathMapper namePathMapper; + + private final QueryEngineSettings settings; /** * Create a new parser. A parser can be re-used, but it is not thread safe. @@ -109,9 +111,10 @@ * @param namePathMapper the name-path mapper to use * @param types the node with the node type information */ - public SQL2Parser(NamePathMapper namePathMapper, NodeState types) { + public SQL2Parser(NamePathMapper namePathMapper, NodeState types, QueryEngineSettings settings) { this.namePathMapper = namePathMapper; this.types = checkNotNull(types); + this.settings = checkNotNull(settings); } /** @@ -143,7 +146,7 @@ } boolean unionAll = readIf("ALL"); QueryImpl q2 = parseSelect(); - q = new UnionQueryImpl(unionAll, q, q2); + q = new UnionQueryImpl(unionAll, q, q2, settings); } OrderingImpl[] orderings = null; if (readIf("ORDER")) { @@ -183,7 +186,7 @@ constraint = parseConstraint(); } QueryImpl q = new QueryImpl( - statement, source, constraint, columnArray, namePathMapper); + statement, source, constraint, columnArray, namePathMapper, settings); q.setDistinct(distinct); return q; } Index: src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java (working copy) @@ -129,13 +129,16 @@ private double estimatedCost; + private final QueryEngineSettings settings; + QueryImpl(String statement, SourceImpl source, ConstraintImpl constraint, - ColumnImpl[] columns, NamePathMapper mapper) { + ColumnImpl[] columns, NamePathMapper mapper, QueryEngineSettings settings) { this.statement = statement; this.source = source; this.constraint = constraint; this.columns = columns; this.namePathMapper = mapper; + this.settings = settings; } @Override @@ -446,7 +449,7 @@ orderBy = ResultRowImpl.getComparator(orderings); } Iterator it = - FilterIterators.newCombinedFilter(rowIt, distinct, limit, offset, orderBy); + FilterIterators.newCombinedFilter(rowIt, distinct, limit, offset, orderBy, settings); if (measure) { // run the query while (it.hasNext()) { @@ -887,4 +890,8 @@ return statement; } + public QueryEngineSettings getSettings() { + return settings; + } + } Index: src/test/java/org/apache/jackrabbit/oak/query/SQL2ParserTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/query/SQL2ParserTest.java (revision 1580856) +++ src/test/java/org/apache/jackrabbit/oak/query/SQL2ParserTest.java (working copy) @@ -35,7 +35,7 @@ private final NodeState types = INITIAL_CONTENT.getChildNode(JCR_SYSTEM).getChildNode(JCR_NODE_TYPES); - private final SQL2Parser p = new SQL2Parser(null, types); + private final SQL2Parser p = new SQL2Parser(null, types, new QueryEngineSettings()); @Test public void testIgnoreSqlComment() throws ParseException { Index: src/main/java/org/apache/jackrabbit/oak/query/ExecutionContext.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/query/ExecutionContext.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/query/ExecutionContext.java (working copy) @@ -39,13 +39,17 @@ private final Root root; + private final QueryEngineSettings settings; + private final QueryIndexProvider indexProvider; public ExecutionContext( NodeState baseState, Root root, + QueryEngineSettings settings, QueryIndexProvider indexProvider) { this.baseState = baseState; this.root = root; + this.settings = settings; this.indexProvider = indexProvider; } @@ -79,4 +83,9 @@ public QueryIndexProvider getIndexProvider() { return indexProvider; } + + public QueryEngineSettings getSettings() { + return settings; + } + } Index: src/main/java/org/apache/jackrabbit/oak/core/ContentRepositoryImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/core/ContentRepositoryImpl.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/core/ContentRepositoryImpl.java (working copy) @@ -35,6 +35,7 @@ import org.apache.jackrabbit.oak.api.ContentSession; import org.apache.jackrabbit.oak.api.Descriptors; import org.apache.jackrabbit.oak.kernel.KernelNodeStore; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.spi.commit.CommitHook; import org.apache.jackrabbit.oak.spi.query.CompositeQueryIndexProvider; import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider; @@ -113,6 +114,7 @@ private final String defaultWorkspaceName; private final SecurityProvider securityProvider; private final QueryIndexProvider indexProvider; + private final QueryEngineSettings queryEngineSettings; private GenericDescriptors descriptors; @@ -129,12 +131,14 @@ public ContentRepositoryImpl(@Nonnull NodeStore nodeStore, @Nonnull CommitHook commitHook, @Nonnull String defaultWorkspaceName, + QueryEngineSettings queryEngineSettings, @Nullable QueryIndexProvider indexProvider, @Nonnull SecurityProvider securityProvider) { this.nodeStore = checkNotNull(nodeStore); this.commitHook = checkNotNull(commitHook); this.defaultWorkspaceName = checkNotNull(defaultWorkspaceName); this.securityProvider = checkNotNull(securityProvider); + this.queryEngineSettings = queryEngineSettings != null ? queryEngineSettings : new QueryEngineSettings(); this.indexProvider = indexProvider != null ? indexProvider : new CompositeQueryIndexProvider(); } @@ -156,7 +160,7 @@ loginContext.login(); return new ContentSessionImpl(loginContext, securityProvider, workspaceName, nodeStore, - commitHook, indexProvider); + commitHook, queryEngineSettings, indexProvider); } public NodeStore getNodeStore() { Index: src/main/java/org/apache/jackrabbit/oak/core/ContentSessionImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/core/ContentSessionImpl.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/core/ContentSessionImpl.java (working copy) @@ -28,6 +28,7 @@ import org.apache.jackrabbit.oak.api.AuthInfo; import org.apache.jackrabbit.oak.api.ContentSession; import org.apache.jackrabbit.oak.api.Root; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.spi.commit.CommitHook; import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider; import org.apache.jackrabbit.oak.spi.security.SecurityProvider; @@ -53,6 +54,7 @@ private final String workspaceName; private final NodeStore store; private final CommitHook hook; + private final QueryEngineSettings queryEngineSettings; private final QueryIndexProvider indexProvider; private final String sessionName; @@ -67,12 +69,14 @@ @Nonnull String workspaceName, @Nonnull NodeStore store, @Nonnull CommitHook hook, + QueryEngineSettings queryEngineSettings, @Nonnull QueryIndexProvider indexProvider) { this.loginContext = loginContext; this.securityProvider = securityProvider; this.workspaceName = workspaceName; this.store = store; this.hook = hook; + this.queryEngineSettings = queryEngineSettings; this.indexProvider = indexProvider; this.sessionName = "session-" + SESSION_COUNTER.incrementAndGet(); } @@ -104,7 +108,7 @@ public Root getLatestRoot() { checkLive(); return new MutableRoot(store, hook, workspaceName, loginContext.getSubject(), - securityProvider, indexProvider, this); + securityProvider, queryEngineSettings, indexProvider, this); } //-----------------------------------------------------------< Closable >--- Index: src/main/java/org/apache/jackrabbit/oak/security/user/UserInitializer.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/security/user/UserInitializer.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/security/user/UserInitializer.java (working copy) @@ -29,6 +29,7 @@ import org.apache.jackrabbit.oak.plugins.index.IndexConstants; import org.apache.jackrabbit.oak.plugins.index.IndexUtils; import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.spi.commit.CommitHook; import org.apache.jackrabbit.oak.spi.lifecycle.WorkspaceInitializer; import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider; @@ -87,11 +88,12 @@ @Override public void initialize( NodeBuilder builder, String workspaceName, + QueryEngineSettings queryEngineSettings, QueryIndexProvider indexProvider, CommitHook commitHook) { NodeState base = builder.getNodeState(); MemoryNodeStore store = new MemoryNodeStore(base); - Root root = new SystemRoot(store, commitHook, workspaceName, securityProvider, indexProvider); + Root root = new SystemRoot(store, commitHook, workspaceName, securityProvider, queryEngineSettings, indexProvider); UserConfiguration userConfiguration = securityProvider.getConfiguration(UserConfiguration.class); UserManager userManager = userConfiguration.getUserManager(root, NamePathMapper.DEFAULT); Index: src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java (revision 1580856) +++ src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java (working copy) @@ -32,6 +32,7 @@ import org.apache.jackrabbit.oak.api.PropertyValue; import org.apache.jackrabbit.oak.commons.PathUtils; +import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.query.ast.JoinConditionImpl; import org.apache.jackrabbit.oak.query.ast.NativeFunctionImpl; import org.apache.jackrabbit.oak.query.ast.Operator; @@ -50,6 +51,8 @@ private final SelectorImpl selector; private final String queryStatement; + + private final QueryEngineSettings settings; /** * Whether the filter is always false. @@ -98,7 +101,7 @@ // TODO support "order by" public FilterImpl() { - this(null, null); + this(null, null, new QueryEngineSettings()); } /** @@ -107,11 +110,12 @@ * @param selector the selector for the given filter * @param queryStatement the query statement */ - public FilterImpl(SelectorImpl selector, String queryStatement) { + public FilterImpl(SelectorImpl selector, String queryStatement, QueryEngineSettings settings) { this.selector = selector; this.queryStatement = queryStatement; this.matchesAllTypes = selector != null ? selector.matchesAllTypes() : false; + this.settings = settings; } public FilterImpl(Filter filter) { @@ -127,6 +131,7 @@ this.selector = impl.selector; this.matchesAllTypes = selector != null ? selector.matchesAllTypes() : false; + this.settings = filter.getQueryEngineSettings(); } public void setPreparing(boolean preparing) { @@ -580,4 +585,9 @@ this.matchesAllTypes = matchesAllTypes; } + @Override + public QueryEngineSettings getQueryEngineSettings() { + return settings; + } + }