From d5e7a0f66f639d42934074a751aa9af9f76a3ceb Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Sun, 17 Sep 2006 21:40:47 +0000 Subject: Finished replacing old query engine. --- .../carbonado/qe/FullScanIndexedQueryExecutor.java | 96 -- .../amazon/carbonado/qe/FullScanQueryExecutor.java | 4 +- .../amazon/carbonado/qe/IndexedQueryAnalyzer.java | 26 +- .../amazon/carbonado/qe/IndexedQueryExecutor.java | 34 +- .../com/amazon/carbonado/qe/KeyQueryExecutor.java | 6 +- .../java/com/amazon/carbonado/qe/OrderingList.java | 13 + .../com/amazon/carbonado/qe/RepositoryAccess.java | 4 +- .../amazon/carbonado/qe/SortedQueryExecutor.java | 8 +- .../com/amazon/carbonado/qe/StorageAccess.java | 1 - .../amazon/carbonado/qe/UnionQueryAnalyzer.java | 123 +- .../amazon/carbonado/qe/UnionQueryExecutor.java | 27 +- .../carbonado/repo/indexed/IndexedRepository.java | 21 +- .../repo/indexed/IndexedRepositoryBuilder.java | 2 +- .../carbonado/repo/indexed/IndexedStorage.java | 157 ++- .../java/com/amazon/carbonado/spi/BaseQuery.java | 395 ------ .../amazon/carbonado/spi/BaseQueryCompiler.java | 249 ---- .../com/amazon/carbonado/spi/BaseQueryEngine.java | 1413 -------------------- .../com/amazon/carbonado/spi/IndexSelector.java | 1205 ----------------- .../carbonado/qe/TestIndexedQueryAnalyzer.java | 24 +- .../carbonado/qe/TestIndexedQueryExecutor.java | 18 +- .../carbonado/qe/TestJoinedQueryExecutor.java | 2 +- .../carbonado/qe/TestUnionQueryAnalyzer.java | 9 + 22 files changed, 309 insertions(+), 3528 deletions(-) delete mode 100644 src/main/java/com/amazon/carbonado/qe/FullScanIndexedQueryExecutor.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/BaseQuery.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/BaseQueryCompiler.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/BaseQueryEngine.java delete mode 100644 src/main/java/com/amazon/carbonado/spi/IndexSelector.java (limited to 'src') diff --git a/src/main/java/com/amazon/carbonado/qe/FullScanIndexedQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/FullScanIndexedQueryExecutor.java deleted file mode 100644 index cfd012b..0000000 --- a/src/main/java/com/amazon/carbonado/qe/FullScanIndexedQueryExecutor.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. All rights reserved. - * - * Licensed 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 com.amazon.carbonado.qe; - -import java.io.IOException; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import com.amazon.carbonado.Cursor; -import com.amazon.carbonado.FetchException; -import com.amazon.carbonado.Storable; - -import com.amazon.carbonado.filter.Filter; -import com.amazon.carbonado.filter.FilterValues; - -import com.amazon.carbonado.info.OrderedProperty; -import com.amazon.carbonado.info.StorableIndex; - -/** - * QueryExecutor which fully scans all Storables of a given type, referencing - * an index. - * - * @author Brian S O'Neill - */ -public class FullScanIndexedQueryExecutor extends AbstractQueryExecutor { - private final Support mSupport; - private final StorableIndex mIndex; - - /** - * @param support support for full scan - * @param index index to use, which may be a primary key index - * @throws IllegalArgumentException if support or index is null - */ - public FullScanIndexedQueryExecutor(Support support, StorableIndex index) { - if (support == null || index == null) { - throw new IllegalArgumentException(); - } - mSupport = support; - mIndex = index; - } - - /** - * Returns an open filter. - */ - public Filter getFilter() { - return Filter.getOpenFilter(mIndex.getStorableType()); - } - - public Cursor fetch(FilterValues values) throws FetchException { - return mSupport.fetch(mIndex); - } - - /** - * Returns the natural order of the index. - */ - public OrderingList getOrdering() { - return OrderingList.get(mIndex.getOrderedProperties()); - } - - public boolean printPlan(Appendable app, int indentLevel, FilterValues values) - throws IOException - { - indent(app, indentLevel); - app.append("full index scan: "); - app.append(mIndex.getStorableType().getName()); - newline(app); - return true; - } - - public static interface Support { - /** - * Perform a full scan of all Storables referenced by an index. - * - * @param index index to scan, which may be a primary key index - */ - Cursor fetch(StorableIndex index) throws FetchException; - } -} diff --git a/src/main/java/com/amazon/carbonado/qe/FullScanQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/FullScanQueryExecutor.java index 2875542..dbf5690 100644 --- a/src/main/java/com/amazon/carbonado/qe/FullScanQueryExecutor.java +++ b/src/main/java/com/amazon/carbonado/qe/FullScanQueryExecutor.java @@ -59,7 +59,7 @@ public class FullScanQueryExecutor extends AbstractQueryExec } public Cursor fetch(FilterValues values) throws FetchException { - return mSupport.fetch(); + return mSupport.fetchAll(); } /** @@ -85,6 +85,6 @@ public class FullScanQueryExecutor extends AbstractQueryExec /** * Perform a full scan of all Storables. */ - Cursor fetch() throws FetchException; + Cursor fetchAll() throws FetchException; } } diff --git a/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java b/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java index 9b46227..76f0b52 100644 --- a/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java +++ b/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java @@ -83,8 +83,10 @@ public class IndexedQueryAnalyzer { * @param ordering optional properties which define desired ordering * @throws IllegalArgumentException if filter is not supported */ - public Result analyze(Filter filter, OrderingList ordering) { - if (!filter.isBound()) { + public Result analyze(Filter filter, OrderingList ordering) + throws SupportException, RepositoryException + { + if (filter != null && !filter.isBound()) { throw new IllegalArgumentException("Filter must be bound"); } @@ -151,7 +153,9 @@ public class IndexedQueryAnalyzer { /** * @return null if no foreign indexes for property */ - private synchronized ForeignIndexes getForeignIndexes(ChainedProperty chainedProp) { + private synchronized ForeignIndexes getForeignIndexes(ChainedProperty chainedProp) + throws SupportException, RepositoryException + { // Remove the last property as it is expected to be a simple storable // property instead of a joined Storable. chainedProp = chainedProp.trim(); @@ -198,7 +202,9 @@ public class IndexedQueryAnalyzer { * Checks if the property is a join and its internal properties are fully * indexed. */ - private boolean isProperJoin(StorableProperty property) { + private boolean isProperJoin(StorableProperty property) + throws SupportException, RepositoryException + { if (!property.isJoin() || property.isQuery()) { return false; } @@ -227,7 +233,9 @@ public class IndexedQueryAnalyzer { return false; } - private boolean simpleAnalyze(Filter filter) { + private boolean simpleAnalyze(Filter filter) + throws SupportException, RepositoryException + { Collection> indexes = indexesFor(filter.getStorableType()); if (indexes != null) { @@ -242,7 +250,9 @@ public class IndexedQueryAnalyzer { return false; } - private Collection> indexesFor(Class type) { + private Collection> indexesFor(Class type) + throws SupportException, RepositoryException + { return mRepoAccess.storageAccessFor(type).getAllIndexes(); } @@ -509,10 +519,6 @@ public class IndexedQueryAnalyzer { { CompositeScore score = getCompositeScore(); FilteringScore fScore = score.getFilteringScore(); - - if (!fScore.hasAnyMatches()) { - return new FullScanIndexedQueryExecutor(access, index); - } if (fScore.isKeyMatch()) { return new KeyQueryExecutor(access, index, fScore); } diff --git a/src/main/java/com/amazon/carbonado/qe/IndexedQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/IndexedQueryExecutor.java index f8d2b23..d87740b 100644 --- a/src/main/java/com/amazon/carbonado/qe/IndexedQueryExecutor.java +++ b/src/main/java/com/amazon/carbonado/qe/IndexedQueryExecutor.java @@ -52,6 +52,7 @@ public class IndexedQueryExecutor extends AbstractQueryExecu private final Support mSupport; private final StorableIndex mIndex; + private final int mIdentityCount; private final Filter mIdentityFilter; private final List> mExclusiveRangeStartFilters; private final List> mInclusiveRangeStartFilters; @@ -78,6 +79,7 @@ public class IndexedQueryExecutor extends AbstractQueryExecu FilteringScore fScore = score.getFilteringScore(); + mIdentityCount = fScore.getIdentityCount(); mIdentityFilter = fScore.getIdentityFilter(); mExclusiveRangeStartFilters = fScore.getExclusiveRangeStartFilters(); mInclusiveRangeStartFilters = fScore.getInclusiveRangeStartFilters(); @@ -152,11 +154,11 @@ public class IndexedQueryExecutor extends AbstractQueryExecu } } - return mSupport.fetch(mIndex, identityValues, - rangeStartBoundary, rangeStartValue, - rangeEndBoundary, rangeEndValue, - mReverseRange, - mReverseOrder); + return mSupport.fetchSubset(mIndex, identityValues, + rangeStartBoundary, rangeStartValue, + rangeEndBoundary, rangeEndValue, + mReverseRange, + mReverseOrder); } public Filter getFilter() { @@ -179,7 +181,11 @@ public class IndexedQueryExecutor extends AbstractQueryExecu } public OrderingList getOrdering() { - return OrderingList.get(mIndex.getOrderedProperties()); + OrderingList list = OrderingList.get(mIndex.getOrderedProperties()); + if (mIdentityCount > 0) { + list = OrderingList.get(list.subList(mIdentityCount, list.size())); + } + return list; } public boolean printPlan(Appendable app, int indentLevel, FilterValues values) @@ -261,14 +267,14 @@ public class IndexedQueryExecutor extends AbstractQueryExecu * @param reverseOrder when true, iteration should be reversed from its * natural order */ - Cursor fetch(StorableIndex index, - Object[] identityValues, - BoundaryType rangeStartBoundary, - Object rangeStartValue, - BoundaryType rangeEndBoundary, - Object rangeEndValue, - boolean reverseRange, - boolean reverseOrder) + Cursor fetchSubset(StorableIndex index, + Object[] identityValues, + BoundaryType rangeStartBoundary, + Object rangeStartValue, + BoundaryType rangeEndBoundary, + Object rangeEndValue, + boolean reverseRange, + boolean reverseOrder) throws FetchException; } } diff --git a/src/main/java/com/amazon/carbonado/qe/KeyQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/KeyQueryExecutor.java index acde1a8..d8436c0 100644 --- a/src/main/java/com/amazon/carbonado/qe/KeyQueryExecutor.java +++ b/src/main/java/com/amazon/carbonado/qe/KeyQueryExecutor.java @@ -69,7 +69,7 @@ public class KeyQueryExecutor extends AbstractQueryExecutor< } public Cursor fetch(FilterValues values) throws FetchException { - return mSupport.fetch(mIndex, values.getValuesFor(mKeyFilter)); + return mSupport.fetchOne(mIndex, values.getValuesFor(mKeyFilter)); } public Filter getFilter() { @@ -87,7 +87,7 @@ public class KeyQueryExecutor extends AbstractQueryExecutor< throws IOException { indent(app, indentLevel); - app.append("index key: "); + app.append("index key match: "); app.append(mIndex.getStorableType().getName()); newline(app); indent(app, indentLevel); @@ -110,7 +110,7 @@ public class KeyQueryExecutor extends AbstractQueryExecutor< * @param index index to open, which may be a primary key index * @param identityValues of exactly matching values to apply to index */ - Cursor fetch(StorableIndex index, Object[] identityValues) + Cursor fetchOne(StorableIndex index, Object[] identityValues) throws FetchException; } } diff --git a/src/main/java/com/amazon/carbonado/qe/OrderingList.java b/src/main/java/com/amazon/carbonado/qe/OrderingList.java index 08a39f5..f6366e5 100644 --- a/src/main/java/com/amazon/carbonado/qe/OrderingList.java +++ b/src/main/java/com/amazon/carbonado/qe/OrderingList.java @@ -97,6 +97,19 @@ public class OrderingList extends AbstractList OrderingList get(List> orderings) { + OrderingList list = emptyList(); + if (orderings != null && orderings.size() > 0) { + for (OrderedProperty property : orderings) { + list = list.concat(property); + } + } + return list; + } + private static OrderingList getListHead(Class type) { OrderingList node; synchronized (cCache) { diff --git a/src/main/java/com/amazon/carbonado/qe/RepositoryAccess.java b/src/main/java/com/amazon/carbonado/qe/RepositoryAccess.java index 6e3c3de..cb25afd 100644 --- a/src/main/java/com/amazon/carbonado/qe/RepositoryAccess.java +++ b/src/main/java/com/amazon/carbonado/qe/RepositoryAccess.java @@ -20,6 +20,7 @@ package com.amazon.carbonado.qe; import com.amazon.carbonado.MalformedTypeException; import com.amazon.carbonado.Repository; +import com.amazon.carbonado.RepositoryException; import com.amazon.carbonado.Storable; import com.amazon.carbonado.SupportException; @@ -40,5 +41,6 @@ public interface RepositoryAccess { * @throws IllegalArgumentException if specified type is null * @throws MalformedTypeException if specified type is not suitable */ - StorageAccess storageAccessFor(Class type); + StorageAccess storageAccessFor(Class type) + throws SupportException, RepositoryException; } diff --git a/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java index 7b12e43..cae03d8 100644 --- a/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java +++ b/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java @@ -117,10 +117,10 @@ public class SortedQueryExecutor extends AbstractQueryExecut throws IOException { indent(app, indentLevel); - if (mHandledOrdering.size() == 0) { - app.append("full sort: "); - } else { - app.append("finish sort: "); + app.append("sort: "); + if (mHandledOrdering.size() > 0) { + app.append(mHandledOrdering.toString()); + app.append(", "); } app.append(mRemainderOrdering.toString()); newline(app); diff --git a/src/main/java/com/amazon/carbonado/qe/StorageAccess.java b/src/main/java/com/amazon/carbonado/qe/StorageAccess.java index 5f93952..2ab2313 100644 --- a/src/main/java/com/amazon/carbonado/qe/StorageAccess.java +++ b/src/main/java/com/amazon/carbonado/qe/StorageAccess.java @@ -37,7 +37,6 @@ import com.amazon.carbonado.info.StorableIndex; */ public interface StorageAccess extends FullScanQueryExecutor.Support, - FullScanIndexedQueryExecutor.Support, KeyQueryExecutor.Support, IndexedQueryExecutor.Support, SortedQueryExecutor.Support diff --git a/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java b/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java index e0d49c2..5933f6c 100644 --- a/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java +++ b/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java @@ -80,8 +80,10 @@ public class UnionQueryAnalyzer implements QueryExecutorFact * @param filter optional filter which must be {@link Filter#isBound bound} * @param ordering optional properties which define desired ordering */ - public Result analyze(Filter filter, OrderingList ordering) { - if (!filter.isBound()) { + public Result analyze(Filter filter, OrderingList ordering) + throws SupportException, RepositoryException + { + if (filter != null && !filter.isBound()) { throw new IllegalArgumentException("Filter must be bound"); } @@ -89,7 +91,7 @@ public class UnionQueryAnalyzer implements QueryExecutorFact ordering = OrderingList.emptyList(); } - return new Result(buildSubResults(filter, ordering)); + return buildResult(filter, ordering); } /** @@ -108,14 +110,19 @@ public class UnionQueryAnalyzer implements QueryExecutorFact * Splits the filter into sub-results, merges sub-results, and possibly * imposes a total ordering. */ - private List.Result> - buildSubResults(Filter filter, OrderingList ordering) + private Result buildResult(Filter filter, OrderingList ordering) + throws SupportException, RepositoryException { - List.Result> subResults = splitIntoSubResults(filter, ordering); + List.Result> subResults; + if (filter == null) { + subResults = Collections.singletonList(mIndexAnalyzer.analyze(filter, ordering)); + } else { + subResults = splitIntoSubResults(filter, ordering); + } if (subResults.size() <= 1) { // Total ordering not required. - return subResults; + return new Result(subResults); } // If any orderings have an unspecified direction, switch to ASCENDING @@ -141,7 +148,7 @@ public class UnionQueryAnalyzer implements QueryExecutorFact if (subResults.size() <= 1) { // Total ordering no longer required. - return subResults; + return new Result(subResults); } } @@ -155,7 +162,7 @@ public class UnionQueryAnalyzer implements QueryExecutorFact ChainedProperty property = op.getChainedProperty(); if (pruneKeys(keys, property)) { // Found a key which is fully covered, indicating total ordering. - return subResults; + return new Result(subResults, ordering); } } @@ -224,7 +231,7 @@ public class UnionQueryAnalyzer implements QueryExecutorFact } } - return subResults; + return new Result(subResults, ordering); } /** @@ -272,7 +279,7 @@ public class UnionQueryAnalyzer implements QueryExecutorFact private Tally bestTally(Iterable tallies) { Tally best = null; for (Tally tally : tallies) { - if (best == null || tally.compareTo(best) < 0) { + if (best == null || tally.compareTo(best) > 0) { best = tally; } } @@ -298,12 +305,16 @@ public class UnionQueryAnalyzer implements QueryExecutorFact */ private List.Result> splitIntoSubResults(Filter filter, OrderingList ordering) + throws SupportException, RepositoryException { // Required for split to work. Filter dnfFilter = filter.disjunctiveNormalForm(); Splitter splitter = new Splitter(ordering); - dnfFilter.accept(splitter, null); + RepositoryException e = dnfFilter.accept(splitter, null); + if (e != null) { + throw e; + } List.Result> subResults = splitter.mSubResults; @@ -373,7 +384,9 @@ public class UnionQueryAnalyzer implements QueryExecutorFact return mergedResults; } - Storage storageDelegate(IndexedQueryAnalyzer.Result result) { + Storage storageDelegate(IndexedQueryAnalyzer.Result result) + throws SupportException, RepositoryException + { StorableIndex localIndex = result.getLocalIndex(); StorageAccess localAccess = mRepoAccess.storageAccessFor(getStorableType()); @@ -389,12 +402,18 @@ public class UnionQueryAnalyzer implements QueryExecutorFact public class Result { private final List.Result> mSubResults; + private final OrderingList mTotalOrdering; Result(List.Result> subResults) { + this(subResults, null); + } + + Result(List.Result> subResults, OrderingList totalOrdering) { if (subResults.size() < 1) { throw new IllegalArgumentException(); } mSubResults = Collections.unmodifiableList(subResults); + mTotalOrdering = totalOrdering; } /** @@ -405,6 +424,13 @@ public class UnionQueryAnalyzer implements QueryExecutorFact return mSubResults; } + /** + * Returns a total ordering, if one was imposed. Otherwise, null is returned. + */ + public OrderingList getTotalOrdering() { + return mTotalOrdering; + } + /** * Creates a QueryExecutor based on this result. */ @@ -423,7 +449,7 @@ public class UnionQueryAnalyzer implements QueryExecutorFact executors.add(subResults.get(i).createExecutor()); } - return new UnionQueryExecutor(executors); + return new UnionQueryExecutor(executors, mTotalOrdering); } } @@ -484,7 +510,7 @@ public class UnionQueryAnalyzer implements QueryExecutorFact } /** - * Returns -1 if this tally is better. + * Returns -1 if this tally is worse. */ public int compareTo(Tally other) { int thisBest = getBestCount(); @@ -497,13 +523,20 @@ public class UnionQueryAnalyzer implements QueryExecutorFact } return 0; } + + public String toString() { + return "Tally: {property=" + mProperty + + ", asc=" + mAscendingCount + + ", desc=" + mDescendingCount + + '}'; + } } /** * Analyzes a disjunctive normal filter into sub-results over filters that * only contain 'and' operations. */ - private class Splitter extends Visitor { + private class Splitter extends Visitor { private final OrderingList mOrdering; final List.Result> mSubResults; @@ -514,37 +547,55 @@ public class UnionQueryAnalyzer implements QueryExecutorFact } @Override - public Object visit(OrFilter filter, Object param) { - Filter left = filter.getLeftFilter(); - if (!(left instanceof OrFilter)) { - subAnalyze(left); - } else { - left.accept(this, param); - } - Filter right = filter.getRightFilter(); - if (!(right instanceof OrFilter)) { - subAnalyze(right); - } else { - right.accept(this, param); + public RepositoryException visit(OrFilter filter, Object param) { + try { + Filter left = filter.getLeftFilter(); + if (!(left instanceof OrFilter)) { + subAnalyze(left); + } else { + RepositoryException e = left.accept(this, param); + if (e != null) { + return e; + } + } + Filter right = filter.getRightFilter(); + if (!(right instanceof OrFilter)) { + subAnalyze(right); + } else { + RepositoryException e = right.accept(this, param); + if (e != null) { + return e; + } + } + return null; + } catch (RepositoryException e) { + return e; } - return null; } // This method should only be called if root filter has no 'or' operators. @Override - public Object visit(AndFilter filter, Object param) { - subAnalyze(filter); - return null; + public RepositoryException visit(AndFilter filter, Object param) { + try { + subAnalyze(filter); + return null; + } catch (RepositoryException e) { + return e; + } } // This method should only be called if root filter has no logical operators. @Override - public Object visit(PropertyFilter filter, Object param) { - subAnalyze(filter); - return null; + public RepositoryException visit(PropertyFilter filter, Object param) { + try { + subAnalyze(filter); + return null; + } catch (RepositoryException e) { + return e; + } } - private void subAnalyze(Filter subFilter) { + private void subAnalyze(Filter subFilter) throws SupportException, RepositoryException { IndexedQueryAnalyzer.Result subResult = mIndexAnalyzer.analyze(subFilter, mOrdering); diff --git a/src/main/java/com/amazon/carbonado/qe/UnionQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/UnionQueryExecutor.java index 5d2a5c6..47672d1 100644 --- a/src/main/java/com/amazon/carbonado/qe/UnionQueryExecutor.java +++ b/src/main/java/com/amazon/carbonado/qe/UnionQueryExecutor.java @@ -64,19 +64,34 @@ public class UnionQueryExecutor extends AbstractQueryExecuto /** * @param executors executors to wrap, each must have the exact same total ordering - * @throws IllegalArgumentException if any parameter is null or if ordering doesn't match + * @throws IllegalArgumentException if any executors is null or if ordering doesn't match */ public UnionQueryExecutor(List> executors) { + this(executors, null); + } + + /** + * @param executors executors to wrap, each must have the exact same total ordering + * @param totalOrdering effective total ordering of executors + * @throws IllegalArgumentException if executors is null + */ + public UnionQueryExecutor(List> executors, OrderingList totalOrdering) { if (executors == null || executors.size() == 0) { throw new IllegalArgumentException(); } - OrderingList totalOrdering = executors.get(0).getOrdering(); - // Compare for consistency. - for (int i=1; i, IndexedStorage> mStorages; - IndexedRepository(String name, Repository repository) { + IndexedRepository(RepositoryReference rootRef, String name, Repository repository) { + mRootRef = rootRef; mRepository = repository; mName = name; mStorages = new IdentityHashMap, IndexedStorage>(); @@ -87,7 +94,7 @@ class IndexedRepository implements Repository, (type, "Storable cannot have any indexes: " + type + ", " + indexCount); } - return mRepository.storageFor(type); + return masterStorage; } storage = new IndexedStorage(this, masterStorage); @@ -172,6 +179,16 @@ class IndexedRepository implements Repository, mRepository.close(); } + public Repository getRootRepository() { + return mRootRef.get(); + } + + public StorageAccess storageAccessFor(Class type) + throws SupportException, RepositoryException + { + return (StorageAccess) storageFor(type); + } + Storage getIndexEntryStorageFor(Class indexEntryClass) throws RepositoryException { diff --git a/src/main/java/com/amazon/carbonado/repo/indexed/IndexedRepositoryBuilder.java b/src/main/java/com/amazon/carbonado/repo/indexed/IndexedRepositoryBuilder.java index cfbe128..f3ec04b 100644 --- a/src/main/java/com/amazon/carbonado/repo/indexed/IndexedRepositoryBuilder.java +++ b/src/main/java/com/amazon/carbonado/repo/indexed/IndexedRepositoryBuilder.java @@ -65,7 +65,7 @@ public class IndexedRepositoryBuilder extends AbstractRepositoryBuilder { return wrapped; } - Repository repo = new IndexedRepository(getName(), wrapped); + Repository repo = new IndexedRepository(rootRef, getName(), wrapped); rootRef.set(repo); return repo; } diff --git a/src/main/java/com/amazon/carbonado/repo/indexed/IndexedStorage.java b/src/main/java/com/amazon/carbonado/repo/indexed/IndexedStorage.java index 60a64d9..78b5f11 100644 --- a/src/main/java/com/amazon/carbonado/repo/indexed/IndexedStorage.java +++ b/src/main/java/com/amazon/carbonado/repo/indexed/IndexedStorage.java @@ -19,6 +19,7 @@ package com.amazon.carbonado.repo.indexed; import java.util.ArrayList; +import java.util.Collection; import java.util.Comparator; import java.util.IdentityHashMap; import java.util.List; @@ -44,6 +45,9 @@ import com.amazon.carbonado.UniqueConstraintException; import com.amazon.carbonado.capability.IndexInfo; import com.amazon.carbonado.capability.IndexInfoCapability; +import com.amazon.carbonado.cursor.ArraySortBuffer; +import com.amazon.carbonado.cursor.MergeSortBuffer; + import com.amazon.carbonado.filter.Filter; import com.amazon.carbonado.info.Direction; @@ -51,11 +55,12 @@ import com.amazon.carbonado.info.StorableInfo; import com.amazon.carbonado.info.StorableIntrospector; import com.amazon.carbonado.info.StorableIndex; -import com.amazon.carbonado.cursor.MergeSortBuffer; +import com.amazon.carbonado.cursor.SortBuffer; import com.amazon.carbonado.qe.BoundaryType; +import com.amazon.carbonado.qe.QueryEngine; +import com.amazon.carbonado.qe.StorageAccess; -import com.amazon.carbonado.spi.BaseQueryEngine; import com.amazon.carbonado.spi.RepairExecutor; import com.amazon.carbonado.spi.StorableIndexSet; @@ -64,7 +69,7 @@ import com.amazon.carbonado.spi.StorableIndexSet; * * @author Brian S O'Neill */ -class IndexedStorage implements Storage { +class IndexedStorage implements Storage, StorageAccess { static StorableIndexSet gatherRequiredIndexes(StorableInfo info) { StorableIndexSet indexSet = new StorableIndexSet(); indexSet.addIndexes(info); @@ -76,9 +81,12 @@ class IndexedStorage implements Storage { final Storage mMasterStorage; private final Map, IndexInfo> mIndexInfoMap; + private final StorableIndexSet mIndexSet; private final QueryEngine mQueryEngine; + private Storage mRootStorage; + @SuppressWarnings("unchecked") IndexedStorage(IndexedRepository repository, Storage masterStorage) throws RepositoryException @@ -208,7 +216,9 @@ class IndexedStorage implements Storage { currentIndexSet.add(freeIndexes[i]); } - mQueryEngine = new QueryEngine(info, repository, this, currentIndexSet); + mIndexSet = currentIndexSet; + + mQueryEngine = new QueryEngine(masterStorage.getStorableType(), repository); } public Class getStorableType() { @@ -220,15 +230,15 @@ class IndexedStorage implements Storage { } public Query query() throws FetchException { - return mQueryEngine.getCompiledQuery(); + return mQueryEngine.query(); } public Query query(String filter) throws FetchException { - return mQueryEngine.getCompiledQuery(filter); + return mQueryEngine.query(filter); } public Query query(Filter filter) throws FetchException { - return mQueryEngine.getCompiledQuery(filter); + return mQueryEngine.query(filter); } public boolean addTrigger(Trigger trigger) { @@ -256,17 +266,87 @@ class IndexedStorage implements Storage { return accessors.toArray(new IndexEntryAccessor[accessors.size()]); } - Storage getStorageFor(StorableIndex index) { + public Collection> getAllIndexes() { + return mIndexSet; + } + + public Storage storageDelegate(StorableIndex index) { if (mIndexInfoMap.get(index) instanceof ManagedIndex) { // Index is managed by this storage, which is typical. - return this; + return null; } // Index is managed by master storage, most likely a primary key index. return mMasterStorage; } - ManagedIndex getManagedIndex(StorableIndex index) { - return (ManagedIndex) mIndexInfoMap.get(index); + public SortBuffer createSortBuffer() { + // FIXME: This is messy. If Storables had built-in serialization + // support, then MergeSortBuffer would not need a root storage. + if (mRootStorage == null) { + try { + mRootStorage = mRepository.getRootRepository().storageFor(getStorableType()); + } catch (RepositoryException e) { + LogFactory.getLog(IndexedStorage.class).warn(null, e); + return new ArraySortBuffer(); + } + } + + // FIXME: sort buffer should be on repository access. Also, create abstract + // repository access that creates the correct merge sort buffer. And more: + // create capability for managing merge sort buffers. + return new MergeSortBuffer(mRootStorage); + } + + public Cursor fetchAll() throws FetchException { + return mMasterStorage.query().fetch(); + } + + public Cursor fetchOne(StorableIndex index, + Object[] identityValues) + throws FetchException + { + // TODO: optimize fetching one by loading storable by primary key + return fetchSubset(index, identityValues, + BoundaryType.OPEN, null, + BoundaryType.OPEN, null, + false, false); + } + + public Cursor fetchSubset(StorableIndex index, + Object[] identityValues, + BoundaryType rangeStartBoundary, + Object rangeStartValue, + BoundaryType rangeEndBoundary, + Object rangeEndValue, + boolean reverseRange, + boolean reverseOrder) + throws FetchException + { + // Note: this code ignores the reverseRange parameter to avoid double + // reversal. Only the lowest storage layer should examine this + // parameter. + + ManagedIndex indexInfo = (ManagedIndex) mIndexInfoMap.get(index); + + Query query = indexInfo.getIndexEntryQueryFor + (identityValues == null ? 0 : identityValues.length, + rangeStartBoundary, rangeEndBoundary, reverseOrder); + + if (identityValues != null) { + query = query.withValues(identityValues); + } + + if (rangeStartBoundary != BoundaryType.OPEN) { + query = query.with(rangeStartValue); + } + if (rangeEndBoundary != BoundaryType.OPEN) { + query = query.with(rangeEndValue); + } + + Cursor indexEntryCursor = query.fetch(); + + return new IndexedCursor + (indexEntryCursor, IndexedStorage.this, indexInfo.getIndexEntryClassBuilder()); } private void registerIndex(ManagedIndex managedIndex) @@ -351,59 +431,4 @@ class IndexedStorage implements Storage { indexEntryStorage.query().deleteAll(); unregisterIndex(index); } - - private static class QueryEngine extends BaseQueryEngine { - - QueryEngine(StorableInfo info, - Repository repo, - IndexedStorage storage, - StorableIndexSet indexSet) { - super(info, repo, storage, null, indexSet); - } - - @Override - protected Storage getStorageFor(StorableIndex index) { - return storage().getStorageFor(index); - } - - protected Cursor openCursor(StorableIndex index, - Object[] exactValues, - BoundaryType rangeStartBoundary, - Object rangeStartValue, - BoundaryType rangeEndBoundary, - Object rangeEndValue, - boolean reverseRange, - boolean reverseOrder) - throws FetchException - { - // Note: this code ignores the reverseRange parameter to avoid - // double reversal. Only the lowest storage layer should examine - // this parameter. - - ManagedIndex indexInfo = storage().getManagedIndex(index); - Query query = indexInfo.getIndexEntryQueryFor - (exactValues == null ? 0 : exactValues.length, - rangeStartBoundary, rangeEndBoundary, reverseOrder); - - if (exactValues != null) { - query = query.withValues(exactValues); - } - - if (rangeStartBoundary != BoundaryType.OPEN) { - query = query.with(rangeStartValue); - } - if (rangeEndBoundary != BoundaryType.OPEN) { - query = query.with(rangeEndValue); - } - - Cursor indexEntryCursor = query.fetch(); - - return new IndexedCursor - (indexEntryCursor, storage(), indexInfo.getIndexEntryClassBuilder()); - } - - private IndexedStorage storage() { - return (IndexedStorage) super.getStorage(); - } - } } diff --git a/src/main/java/com/amazon/carbonado/spi/BaseQuery.java b/src/main/java/com/amazon/carbonado/spi/BaseQuery.java deleted file mode 100644 index e5fc3b0..0000000 --- a/src/main/java/com/amazon/carbonado/spi/BaseQuery.java +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. All rights reserved. - * - * Licensed 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 com.amazon.carbonado.spi; - -import java.io.IOException; - -import org.cojen.util.BeanPropertyAccessor; - -import com.amazon.carbonado.Cursor; -import com.amazon.carbonado.FetchException; -import com.amazon.carbonado.IsolationLevel; -import com.amazon.carbonado.PersistException; -import com.amazon.carbonado.PersistMultipleException; -import com.amazon.carbonado.Repository; -import com.amazon.carbonado.Storage; -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.Transaction; -import com.amazon.carbonado.Query; -import com.amazon.carbonado.util.Appender; - -import com.amazon.carbonado.filter.ClosedFilter; -import com.amazon.carbonado.filter.Filter; -import com.amazon.carbonado.filter.FilterValues; -import com.amazon.carbonado.filter.OpenFilter; -import com.amazon.carbonado.filter.RelOp; - -import com.amazon.carbonado.info.OrderedProperty; - -import com.amazon.carbonado.qe.AbstractQuery; -import com.amazon.carbonado.qe.EmptyQuery; - -/** - * BaseQuery supports binding filters to values. - * - * @author Brian S O'Neill - * @deprecated Use {@link com.amazon.carbonado.qe.StandardQuery} - */ -public abstract class BaseQuery extends AbstractQuery implements Appender { - /** - * Appends spaces to the given appendable. Useful for implementing - * printNative and printPlan. - */ - public static void indent(Appendable app, int indentLevel) throws IOException { - for (int i=0; i[] orderings) { - String[] orderingStrings; - if (orderings == null || orderings.length == 0) { - return EMPTY_ORDERINGS; - } - orderingStrings = new String[orderings.length]; - for (int i=0; i mStorage; - // Values for this query. - private final FilterValues mValues; - // Properties that this query is ordered by. - private final String[] mOrderings; - - // Note: Since constructor has parameters, this class is called Base - // instead of Abstract. - /** - * @param storage required storage object - * @param values optional values object, defaults to open filter if null - * @param orderings optional order-by properties - */ - protected BaseQuery(Repository repo, - Storage storage, - FilterValues values, - OrderedProperty[] orderings) - { - if (repo == null || storage == null) { - throw new IllegalArgumentException(); - } - mRepository = repo; - mStorage = storage; - mValues = values; - mOrderings = extractOrderingNames(orderings); - } - - /** - * @param storage required storage object - * @param values optional values object, defaults to open filter if null - * @param orderings optional order-by properties, not cloned - */ - protected BaseQuery(Repository repo, - Storage storage, - FilterValues values, - String[] orderings) - { - if (repo == null || storage == null) { - throw new IllegalArgumentException(); - } - mRepository = repo; - mStorage = storage; - mValues = values; - mOrderings = orderings == null ? EMPTY_ORDERINGS : orderings; - } - - public Class getStorableType() { - return mStorage.getStorableType(); - } - - public Filter getFilter() { - FilterValues values = mValues; - if (values != null) { - return values.getFilter(); - } - return Filter.getOpenFilter(mStorage.getStorableType()); - } - - public FilterValues getFilterValues() { - return mValues; - } - - public int getBlankParameterCount() { - return mValues == null ? 0 : mValues.getBlankParameterCount(); - } - - public Query with(int value) { - return newInstance(requireValues().with(value)); - } - - public Query with(long value) { - return newInstance(requireValues().with(value)); - } - - public Query with(float value) { - return newInstance(requireValues().with(value)); - } - - public Query with(double value) { - return newInstance(requireValues().with(value)); - } - - public Query with(boolean value) { - return newInstance(requireValues().with(value)); - } - - public Query with(char value) { - return newInstance(requireValues().with(value)); - } - - public Query with(byte value) { - return newInstance(requireValues().with(value)); - } - - public Query with(short value) { - return newInstance(requireValues().with(value)); - } - - public Query with(Object value) { - return newInstance(requireValues().with(value)); - } - - public Query withValues(Object... values) { - if (values == null || values.length == 0) { - return this; - } - return newInstance(requireValues().withValues(values)); - } - - public Query and(Filter filter) throws FetchException { - FilterValues values = getFilterValues(); - Query newQuery; - if (values == null) { - newQuery = mStorage.query(filter); - } else { - newQuery = mStorage.query(values.getFilter().and(filter)); - newQuery = newQuery.withValues(values.getValues()); - } - return mOrderings.length == 0 ? newQuery : newQuery.orderBy(mOrderings); - } - - public Query or(Filter filter) throws FetchException { - FilterValues values = getFilterValues(); - if (values == null) { - throw new IllegalStateException("Query is already guaranteed to fetch everything"); - } - Query newQuery = mStorage.query(values.getFilter().or(filter)); - newQuery = newQuery.withValues(values.getValues()); - return mOrderings.length == 0 ? newQuery : newQuery.orderBy(mOrderings); - } - - public Query not() throws FetchException { - FilterValues values = getFilterValues(); - if (values == null) { - // FIXME: fix this or remove BaseQuery class. - throw new UnsupportedOperationException(); - //return new EmptyQuery(mStorage, mOrderings); - } - Query newQuery = mStorage.query(values.getFilter().not()); - newQuery = newQuery.withValues(values.getSuppliedValues()); - return mOrderings.length == 0 ? newQuery : newQuery.orderBy(mOrderings); - } - - public Cursor fetchAfter(S start) throws FetchException { - String[] orderings; - if (start == null || (orderings = mOrderings).length == 0) { - return fetch(); - } - - Class storableType = mStorage.getStorableType(); - Filter orderFilter = Filter.getClosedFilter(storableType); - Filter lastSubFilter = Filter.getOpenFilter(storableType); - BeanPropertyAccessor accessor = BeanPropertyAccessor.forClass(storableType); - - Object[] values = new Object[orderings.length]; - - for (int i=0;;) { - String propertyName = orderings[i]; - RelOp operator = RelOp.GT; - char c = propertyName.charAt(0); - if (c == '-') { - propertyName = propertyName.substring(1); - operator = RelOp.LT; - } else if (c == '+') { - propertyName = propertyName.substring(1); - } - - values[i] = accessor.getPropertyValue(start, propertyName); - - orderFilter = orderFilter.or(lastSubFilter.and(propertyName, operator)); - - if (++i >= orderings.length) { - break; - } - - lastSubFilter = lastSubFilter.and(propertyName, RelOp.EQ); - } - - Query newQuery = this.and(orderFilter); - - for (int i=0; i cursor = fetch(); - boolean result; - try { - if (cursor.hasNext()) { - S obj = cursor.next(); - if (cursor.hasNext()) { - throw new PersistMultipleException(toString()); - } - result = obj.tryDelete(); - } else { - return false; - } - } finally { - cursor.close(); - } - txn.commit(); - return result; - } catch (FetchException e) { - throw e.toPersistException(); - } finally { - txn.exit(); - } - } - - public void deleteAll() throws PersistException { - Transaction txn = mRepository.enterTransaction(IsolationLevel.READ_COMMITTED); - try { - Cursor cursor = fetch(); - try { - while (cursor.hasNext()) { - cursor.next().tryDelete(); - } - } finally { - cursor.close(); - } - txn.commit(); - } catch (FetchException e) { - throw e.toPersistException(); - } finally { - txn.exit(); - } - } - - /** - * Returns the query ordering properties, never null. The returned array is - * not cloned, only for performance reasons. Subclasses should not alter it. - */ - protected String[] getOrderings() { - return mOrderings; - } - - protected final Repository getRepository() { - return mRepository; - } - - protected final Storage getStorage() { - return mStorage; - } - - @Override - public int hashCode() { - return mStorage.hashCode() * 31 + getFilterValues().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof BaseQuery) { - BaseQuery other = (BaseQuery) obj; - return mStorage.equals(other.mStorage) && - getFilterValues().equals(other.getFilterValues()); - } - return false; - } - - public void appendTo(Appendable app) throws IOException { - app.append("Query {type="); - app.append(getStorableType().getName()); - app.append(", filter="); - Filter filter = getFilter(); - if (filter instanceof OpenFilter || filter instanceof ClosedFilter) { - filter.appendTo(app); - } else { - app.append('"'); - filter.appendTo(app, getFilterValues()); - app.append('"'); - } - - if (mOrderings != null && mOrderings.length > 0) { - app.append(", orderBy=["); - for (int i=0; i 0) { - app.append(", "); - } - app.append(mOrderings[i]); - } - app.append(']'); - } - - app.append('}'); - } - - private FilterValues requireValues() { - FilterValues values = getFilterValues(); - if (values == null) { - throw new IllegalStateException("Query doesn't have any parameters"); - } - return values; - } - - /** - * Return a new instance of BaseQuery implementation, using new filter - * values. The Filter in the FilterValues is the same as was passed in the - * constructor. - * - *

Any orderings in this query must also be applied in the new - * query. Call getOrderings to get them. - * - * @param values never null - */ - protected abstract BaseQuery newInstance(FilterValues values); -} diff --git a/src/main/java/com/amazon/carbonado/spi/BaseQueryCompiler.java b/src/main/java/com/amazon/carbonado/spi/BaseQueryCompiler.java deleted file mode 100644 index 4160b0e..0000000 --- a/src/main/java/com/amazon/carbonado/spi/BaseQueryCompiler.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. All rights reserved. - * - * Licensed 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 com.amazon.carbonado.spi; - -import java.util.Map; - -import org.cojen.util.KeyFactory; -import org.cojen.util.SoftValuedHashMap; -import org.cojen.util.WeakIdentityMap; - -import com.amazon.carbonado.FetchException; -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.MalformedFilterException; -import com.amazon.carbonado.Query; - -import com.amazon.carbonado.filter.ClosedFilter; -import com.amazon.carbonado.filter.Filter; -import com.amazon.carbonado.filter.FilterValues; - -import com.amazon.carbonado.info.OrderedProperty; -import com.amazon.carbonado.info.StorableInfo; - -/** - * BaseQueryCompiler caches compiled queries, and calls an abstract method - * to compile queries it doesn't have cached. - * - * @author Brian S O'Neill - * @deprecated Use {@link com.amazon.carbonado.qe.StandardQueryFactory} - */ -public abstract class BaseQueryCompiler { - private final StorableInfo mInfo; - private final Map> mStringToQuery; - private final Map, Queries> mFilterToQueries; - - /** - * @throws IllegalArgumentException if type is null - */ - // Note: Since constructor has parameters, this class is called Base - // instead of Abstract. - @SuppressWarnings("unchecked") - protected BaseQueryCompiler(StorableInfo info) { - if (info == null) { - throw new IllegalArgumentException(); - } - mInfo = info; - mStringToQuery = new SoftValuedHashMap(7); - mFilterToQueries = new WeakIdentityMap(7); - } - - /** - * Looks up compiled query in the cache, and returns it. If not found, then - * one is created and cached for later retrieval. - * - * @return cached compiled query which returns everything from storage - */ - public synchronized Query getCompiledQuery() throws FetchException { - return getCompiledQuery(Filter.getOpenFilter(mInfo.getStorableType())); - } - - /** - * Looks up compiled query in the cache, and returns it. If not found, then - * the filter expression is parsed, and compileQuery is invoked on the - * result. The compiled query is cached for later retrieval. - * - * @param filter query filter expression to parse - * @return cached compiled query - * @throws IllegalArgumentException if query filter expression is null - * @throws MalformedFilterException if query filter expression is malformed - */ - public synchronized Query getCompiledQuery(String filter) throws FetchException { - if (filter == null) { - throw new IllegalArgumentException("Query filter must not be null"); - } - Query query = mStringToQuery.get(filter); - if (query == null) { - query = getCompiledQuery(Filter.filterFor(mInfo.getStorableType(), filter)); - mStringToQuery.put(filter, query); - } - return query; - } - - /** - * Looks up compiled query in the cache, and returns it. If not found, then - * compileQuery is invoked on the result. The compiled query is cached for - * later retrieval. - * - * @param filter root filter tree - * @return cached compiled query - * @throws IllegalArgumentException if root filter is null - */ - public synchronized Query getCompiledQuery(Filter filter) throws FetchException { - if (filter == null) { - throw new IllegalArgumentException("Filter is null"); - } - Queries queries = mFilterToQueries.get(filter); - if (queries == null) { - Query query; - FilterValues values = filter.initialFilterValues(); - if (values != null) { - // FilterValues applies to bound filter. Use that instead. - Filter altFilter = values.getFilter(); - if (altFilter != filter) { - return getCompiledQuery(altFilter); - } - query = compileQuery(values, null); - } else { - query = compileQuery(null, null); - if (filter instanceof ClosedFilter) { - query = query.not(); - } - } - queries = new Queries(query); - mFilterToQueries.put(filter, queries); - } - return queries.mPlainQuery; - } - - /** - * Used by implementations to retrieve cached queries that have order-by - * properties. - * - * @param values filter values produced earlier by this compiler, or null, - * or a derived instance - * @param propertyNames optional property names to order by, which may be - * prefixed with '+' or '-' - * @throws IllegalArgumentException if properties are not supported or if - * filter did not originate from this compiler - */ - @SuppressWarnings("unchecked") - public Query getOrderedQuery(FilterValues values, String... propertyNames) - throws FetchException, IllegalArgumentException, UnsupportedOperationException - { - final Filter filter = - values == null ? Filter.getOpenFilter(mInfo.getStorableType()) : values.getFilter(); - - final Queries queries = mFilterToQueries.get(filter); - - if (queries == null) { - throw new IllegalArgumentException("Unknown filter provided"); - } - - if (propertyNames == null || propertyNames.length == 0) { - return queries.mPlainQuery; - } - - final Object key = KeyFactory.createKey(propertyNames); - Query query = queries.mOrderingsToQuery.get(key); - - if (query != null) { - // Now transfer property values. - if (values != null) { - query = query.withValues(values.getSuppliedValues()); - } - - return query; - } - - // Try again with property names that have an explicit direction, - // hoping for a cache hit. - - boolean propertyNamesChanged = false; - final int length = propertyNames.length; - for (int i=0; i[] orderings = new OrderedProperty[length]; - - for (int i=0; i initialValues = filter.initialFilterValues(); - - query = compileQuery(initialValues, orderings); - queries.mOrderingsToQuery.put(key, query); - - // Now transfer property values. - if (values != null) { - query = query.withValues(values.getSuppliedValues()); - } - - return query; - } - - /** - * Returns the StorableInfo object in this object. - */ - protected StorableInfo getStorableInfo() { - return mInfo; - } - - /** - * Compile the query represented by the type checked root node. If any - * order-by properties are supplied, they have been checked as well. - * - * @param values values and filter for query, which may be null if - * unfiltered - * @param orderings optional list of properties to order by - */ - protected abstract Query compileQuery(FilterValues values, - OrderedProperty[] orderings) - throws FetchException, UnsupportedOperationException; - - private static class Queries { - final Query mPlainQuery; - - final Map> mOrderingsToQuery; - - @SuppressWarnings("unchecked") - Queries(Query query) { - mPlainQuery = query; - mOrderingsToQuery = new SoftValuedHashMap(7); - } - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/BaseQueryEngine.java b/src/main/java/com/amazon/carbonado/spi/BaseQueryEngine.java deleted file mode 100644 index f1ede60..0000000 --- a/src/main/java/com/amazon/carbonado/spi/BaseQueryEngine.java +++ /dev/null @@ -1,1413 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. All rights reserved. - * - * Licensed 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 com.amazon.carbonado.spi; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.cojen.util.BeanComparator; - -import com.amazon.carbonado.Cursor; -import com.amazon.carbonado.FetchException; -import com.amazon.carbonado.Query; -import com.amazon.carbonado.Repository; -import com.amazon.carbonado.Storable; -import com.amazon.carbonado.Storage; - -import com.amazon.carbonado.filter.AndFilter; -import com.amazon.carbonado.filter.Filter; -import com.amazon.carbonado.filter.FilterValues; -import com.amazon.carbonado.filter.OrFilter; -import com.amazon.carbonado.filter.PropertyFilter; -import com.amazon.carbonado.filter.Visitor; - -import com.amazon.carbonado.info.Direction; -import com.amazon.carbonado.info.OrderedProperty; -import com.amazon.carbonado.info.StorableIndex; -import com.amazon.carbonado.info.StorableInfo; - -import com.amazon.carbonado.cursor.FilteredCursor; -import com.amazon.carbonado.cursor.MergeSortBuffer; -import com.amazon.carbonado.cursor.SortBuffer; -import com.amazon.carbonado.cursor.SortedCursor; -import com.amazon.carbonado.cursor.UnionCursor; - -import com.amazon.carbonado.qe.BoundaryType; - -/** - * Basis for a rule-based query engine. It takes care of index selection, - * filtering, sorting, and unions. - * - * @author Brian S O'Neill - * @deprecated Use {@link com.amazon.carbonado.qe.QueryEngine} - */ -public abstract class BaseQueryEngine extends BaseQueryCompiler { - private static PropertyFilter[] NO_FILTERS = new PropertyFilter[0]; - - /** - * Compares two objects which are assumed to be Comparable. If one value is - * null, it is treated as being higher. This consistent with all other - * property value comparisons in carbonado. - */ - static int compareWithNullHigh(Object a, Object b) { - return a == null ? (b == null ? 0 : -1) : (b == null ? 1 : ((Comparable) a).compareTo(b)); - } - - private final Repository mRepository; - private final Storage mStorage; - private final StorableIndex mPrimaryKeyIndex; - private final StorableIndexSet mIndexSet; - - String mMergeSortTempDir; - - /** - * @param info info for Storable - * @param repo repository for entering transactions - * @param storage source for queried objects - * @param primaryKeyIndex optional parameter representing primary key index - * @param indexSet optional parameter representing all available indexes. - * Constructor makes a local copy of the set. - * @throws IllegalArgumentException if primaryKeyIndex is null and indexSet - * is empty - */ - protected BaseQueryEngine(StorableInfo info, - Repository repo, - Storage storage, - StorableIndex primaryKeyIndex, - StorableIndexSet indexSet) { - super(info); - if (primaryKeyIndex == null && (indexSet == null || indexSet.size() == 0)) { - throw new IllegalArgumentException(); - } - mRepository = repo; - mStorage = storage; - mPrimaryKeyIndex = primaryKeyIndex; - mIndexSet = (indexSet == null || indexSet.size() == 0) ? null - : new StorableIndexSet(indexSet); - } - - /** - * @param tempDir directory to store temp files for merge sorting, or null - * for default - */ - protected void setMergeSortTempDirectory(String tempDir) { - mMergeSortTempDir = tempDir; - } - - @SuppressWarnings("unchecked") - protected Query compileQuery(final FilterValues values, - final OrderedProperty[] orderings) - throws FetchException, UnsupportedOperationException - { - if (values == null) { - // Perform requested full scan. - return fullScan(values, orderings); - } - - final Filter originalFilter = values.getFilter(); - final Filter dnfFilter = originalFilter.disjunctiveNormalForm(); - - // Analyze the disjunctive normal form, breaking down the query into - // separate queries that can be unioned together. - - IndexAnalysis analysis = new IndexAnalysis(mPrimaryKeyIndex, mIndexSet, orderings); - dnfFilter.accept(analysis, null); - - if (analysis.noBestIndex()) { - // Fallback to full scan for everything if no best index found for - // just one query component. - return fullScan(values, orderings); - } - - OrderedProperty[] totalOrderings = null; - ensureTotalOrdering: - if (analysis.getResults().size() > 1) { - // Union will be performed, and so a total ordering is required. - - // TODO: The logic in this section needs to be totally reworked. It - // does a terrible job of finding the best total ordering, often - // performing full sorts when not needed. Essentially, inefficient - // query plans can get generated. - - // If all selected indexes are unique and have the same effective ordering, then - // nothing special needs to be done to ensure total ordering. - OrderedProperty[] effectiveOrderings = null; - totalOrderCheck: - if (orderings == null || orderings.length == 0) { - for (IndexSelector.IndexFitness result : analysis.getResults()) { - StorableIndex index = result.getIndex(); - if (!index.isUnique()) { - break totalOrderCheck; - } - if (effectiveOrderings == null) { - effectiveOrderings = result.getEffectiveOrderings(); - continue; - } - if (!Arrays.equals(effectiveOrderings, result.getEffectiveOrderings())) { - break totalOrderCheck; - } - } - // All indexes already define a total ordering. - totalOrderings = effectiveOrderings; - break ensureTotalOrdering; - } - - // Augment the ordering with elements of a unique index. - - // Count how often an index has been used. - Map, Integer> counts = new LinkedHashMap, Integer>(); - - for (IndexSelector.IndexFitness result : analysis.getResults()) { - StorableIndex index = result.getIndex(); - counts.put(index, (counts.containsKey(index)) ? (counts.get(index) + 1) : 1); - } - - // Find the unique index that has been selected most often. - StorableIndex unique = mPrimaryKeyIndex; - int uniqueCount = 0; - for (Map.Entry, Integer> entry : counts.entrySet()) { - if (entry.getKey().isUnique() && entry.getValue() > uniqueCount) { - unique = entry.getKey(); - uniqueCount = entry.getValue(); - } - } - - if (unique == null) { - // Select first found unique index. - for (StorableIndex index : mIndexSet) { - if (index.isUnique()) { - unique = index; - break; - } - } - if (unique == null) { - throw new UnsupportedOperationException - ("Cannot perform union; sort requires at least one unique index"); - } - } - - // To avoid full sorts, choose an index which is already being used - // for its ordering. It may have a range filter or handled - // orderings. - StorableIndex best = null; - int bestCount = 0; - for (IndexSelector.IndexFitness result : analysis.getResults()) { - if ((result.getInclusiveRangeStartFilters().length > 0 || - result.getExclusiveRangeStartFilters().length > 0 || - result.getInclusiveRangeEndFilters().length > 0 || - result.getExclusiveRangeEndFilters().length > 0) && - (result.getHandledOrderings() != null || - result.getRemainderOrderings() == null)) { - - StorableIndex index = result.getIndex(); - int count = counts.get(index); - - if (count > bestCount) { - best = index; - bestCount = count; - } - } - } - - { - int newLength = (orderings == null ? 0 : orderings.length) - + (best == null ? 0 : best.getPropertyCount()) - + unique.getPropertyCount(); - totalOrderings = new OrderedProperty[newLength]; - - int j = 0; - if (orderings != null) { - for (int i=0; i(mPrimaryKeyIndex, mIndexSet, totalOrderings); - dnfFilter.accept(analysis, null); - - if (analysis.noBestIndex()) { - // Fallback to full scan for everything if no best index found for - // just one query component. - return fullScan(values, orderings); - } - } - - // Attempt to reduce the number of separate cursors need to be opened for union. - analysis.reduceResults(); - - List> subFactories = new ArrayList>(); - - for (IndexSelector.IndexFitness result : analysis.getResults()) { - CursorFactory subFactory; - - // Determine if KeyCursorFactory should be used instead. - boolean isKeyFilter = result.isKeyFilter(); - if (isKeyFilter) { - subFactory = new KeyCursorFactory - (this, result.getIndex(), result.getExactFilter()); - } else { - subFactory = new IndexCursorFactory - (this, result.getIndex(), - result.shouldReverseOrder(), result.shouldReverseRange(), - result.getExactFilter(), - result.getInclusiveRangeStartFilters(), - result.getExclusiveRangeStartFilters(), - result.getInclusiveRangeEndFilters(), - result.getExclusiveRangeEndFilters()); - } - - Filter remainderFilter = result.getRemainderFilter(); - if (remainderFilter != null) { - subFactory = new FilteredCursorFactory(this, subFactory, remainderFilter); - } - - if (!isKeyFilter) { - OrderedProperty[] remainderOrderings = result.getRemainderOrderings(); - if (remainderOrderings != null && remainderOrderings.length > 0) { - subFactory = new SortedCursorFactory - (this, subFactory, result.getHandledOrderings(), remainderOrderings); - } - } - - subFactories.add(subFactory); - } - - CursorFactory factory = UnionedCursorFactory - .createUnion(this, subFactories, totalOrderings); - - return CompiledQuery.create(mRepository, mStorage, values, orderings, this, factory); - } - - private Query fullScan(FilterValues values, OrderedProperty[] orderings) - throws FetchException - { - // Try to select index that has best ordering. - IndexSelector selector = new IndexSelector(null, orderings); - StorableIndex best = mPrimaryKeyIndex; - - if (mIndexSet != null) { - for (StorableIndex candidate : mIndexSet) { - int cmpResult = selector.compare(best, candidate); - if (cmpResult > 0) { - best = candidate; - } - } - } - - IndexSelector.IndexFitness result = selector.examine(best); - - CursorFactory factory; - if (result == null || result.isUseless()) { - factory = new FullScanCursorFactory(this, mPrimaryKeyIndex); - if (values != null) { - factory = new FilteredCursorFactory(this, factory, values.getFilter()); - } - if (orderings != null && orderings.length > 0) { - factory = new SortedCursorFactory(this, factory, null, orderings); - } - } else { - factory = new IndexCursorFactory - (this, result.getIndex(), - result.shouldReverseOrder(), result.shouldReverseRange(), - result.getExactFilter(), - result.getInclusiveRangeStartFilters(), - result.getExclusiveRangeStartFilters(), - result.getInclusiveRangeEndFilters(), - result.getExclusiveRangeEndFilters()); - - Filter remainderFilter = result.getRemainderFilter(); - if (remainderFilter != null) { - factory = new FilteredCursorFactory(this, factory, remainderFilter); - } - - OrderedProperty[] remainderOrderings = result.getRemainderOrderings(); - if (remainderOrderings != null && remainderOrderings.length > 0) { - factory = new SortedCursorFactory - (this, factory, result.getHandledOrderings(), remainderOrderings); - } - } - - return CompiledQuery.create(mRepository, mStorage, values, orderings, this, factory); - } - - /** - * Returns the primary Storage object in this object. - */ - protected final Storage getStorage() { - return mStorage; - } - - /** - * Returns the storage object that the given index applies to. By default, - * this method returns the primary storage. Override if indexes may be - * defined in multiple storages. - */ - protected Storage getStorageFor(StorableIndex index) { - return mStorage; - } - - /** - * Return a new Cursor instance constrained by the given parameters. The - * index values are aligned with the index properties at property index - * 0. An optional start or end boundary matches up with the index property - * following the last of the index values. - * - * @param index index to open, which may be the primary key index - * @param exactValues optional list of exactly matching values to apply to index - * @param rangeStartBoundary start boundary type - * @param rangeStartValue value to start at if boundary is not open - * @param rangeEndBoundary end boundary type - * @param rangeEndValue value to end at if boundary is not open - * @param reverseRange indicates that range operates on a property that is - * ordered in reverse (this parameter might also be true simply because - * reverseOrder is true) - * @param reverseOrder when true, iteration is reversed - */ - protected abstract Cursor openCursor(StorableIndex index, - Object[] exactValues, - BoundaryType rangeStartBoundary, - Object rangeStartValue, - BoundaryType rangeEndBoundary, - Object rangeEndValue, - boolean reverseRange, - boolean reverseOrder) - throws FetchException; - - /** - * Return a new Cursor instance which is expected to fetch at most one - * object. The chosen index is unique, and a primary or alternate key is - * contained within it. - *

- * Subclasses are encouraged to override this method and provide a more - * efficient implementation. - * - * @param index index to open, which may be the primary key index - * @param exactValues first values to set for index; length may be smaller - * than index property count - */ - protected Cursor openKeyCursor(StorableIndex index, - Object[] exactValues) - throws FetchException - { - return openCursor(index, exactValues, - BoundaryType.OPEN, null, - BoundaryType.OPEN, null, - false, - false); - } - - @SuppressWarnings("unchecked") - Comparator makeComparator(OrderedProperty[] orderings) { - if (orderings == null) { - return null; - } - - BeanComparator bc = BeanComparator.forClass(getStorableInfo().getStorableType()); - - for (OrderedProperty property : orderings) { - bc = bc.orderBy(property.getChainedProperty().toString()); - bc = bc.caseSensitive(); - if (property.getDirection() == Direction.DESCENDING) { - bc = bc.reverse(); - } - } - - return bc; - } - - private static class CompiledQuery extends BaseQuery { - private final BaseQueryEngine mEngine; - private final CursorFactory mFactory; - - static Query create(Repository repo, - Storage storage, - FilterValues values, - OrderedProperty[] orderings, - BaseQueryEngine engine, - CursorFactory factory) - throws FetchException - { - if (factory == null) { - throw new IllegalArgumentException(); - } - factory = factory.getActualFactory(); - return new CompiledQuery(repo, storage, values, orderings, engine, factory); - } - - private CompiledQuery(Repository repo, - Storage storage, - FilterValues values, - OrderedProperty[] orderings, - BaseQueryEngine engine, - CursorFactory factory) - throws FetchException - { - super(repo, storage, values, orderings); - mEngine = engine; - mFactory = factory; - } - - private CompiledQuery(Repository repo, - Storage storage, - FilterValues values, - String[] orderings, - BaseQueryEngine engine, - CursorFactory factory) - { - super(repo, storage, values, orderings); - mEngine = engine; - mFactory = factory; - } - - public Query orderBy(String property) - throws FetchException, UnsupportedOperationException - { - return mEngine.getOrderedQuery(getFilterValues(), property); - } - - public Query orderBy(String... properties) - throws FetchException, UnsupportedOperationException - { - return mEngine.getOrderedQuery(getFilterValues(), properties); - } - - public Cursor fetch() throws FetchException { - return mFactory.openCursor(getFilterValues()); - } - - public long count() throws FetchException { - return mFactory.count(getFilterValues()); - } - - public boolean printNative(Appendable app, int indentLevel) throws IOException { - return mFactory.printNative(app, indentLevel, getFilterValues()); - } - - public boolean printPlan(Appendable app, int indentLevel) throws IOException { - return mFactory.printPlan(app, indentLevel, getFilterValues()); - } - - protected BaseQuery newInstance(FilterValues values) { - return new CompiledQuery - (getRepository(), getStorage(), values, getOrderings(), mEngine, mFactory); - } - } - - private static interface CursorFactory { - Cursor openCursor(FilterValues values) throws FetchException; - - long count(FilterValues values) throws FetchException; - - /** - * Append filter rules to the given filter. - * - * @param filter initial filter, might be null. - */ - Filter buildFilter(Filter filter); - - /** - * Applies an ordering to the given query in a new query. - */ - Query applyOrderBy(Query query) throws FetchException; - - /** - * Returns the storage object that this factory needs to use. Usually, - * this is the same as the primary. If multiple storages are needed, - * then null is returned. In either case, if the storage is not the - * primary, then this factory cannot be used. Use the factory from - * getActualFactory instead. - */ - Storage getActualStorage(); - - /** - * Returns another instance of this factory that uses the proper - * storage. - */ - CursorFactory getActualFactory() throws FetchException; - - /** - * @param values optional - */ - boolean printNative(Appendable app, int indentLevel, FilterValues values) - throws IOException; - - /** - * @param values optional - */ - boolean printPlan(Appendable app, int indentLevel, FilterValues values) - throws IOException; - } - - private abstract static class AbstractCursorFactory - implements CursorFactory - { - protected final BaseQueryEngine mEngine; - - AbstractCursorFactory(BaseQueryEngine engine) { - mEngine = engine; - } - - public long count(FilterValues values) throws FetchException { - Cursor cursor = openCursor(values); - try { - long count = cursor.skipNext(Integer.MAX_VALUE); - if (count == Integer.MAX_VALUE) { - int amt; - while ((amt = cursor.skipNext(Integer.MAX_VALUE)) > 0) { - count += amt; - } - } - return count; - } finally { - cursor.close(); - } - } - - public CursorFactory getActualFactory() throws FetchException { - Storage storage = getActualStorage(); - if (storage == mEngine.getStorage()) { - return this; - } - return new QueryCursorFactory(this, storage); - } - - public boolean printNative(Appendable app, int indentLevel, FilterValues values) - throws IOException - { - return false; - } - - void indent(Appendable app, int indentLevel) throws IOException { - for (int i=0; i - extends AbstractCursorFactory - { - protected final StorableIndex mIndex; - - private final boolean mReverseOrder; - private final boolean mReverseRange; - private final Filter mExactFilter; - private final PropertyFilter[] mInclusiveRangeStartFilters; - private final PropertyFilter[] mExclusiveRangeStartFilters; - private final PropertyFilter[] mInclusiveRangeEndFilters; - private final PropertyFilter[] mExclusiveRangeEndFilters; - - IndexCursorFactory(BaseQueryEngine engine, - StorableIndex index, - boolean reverseOrder, - boolean reverseRange, - Filter exactFilter, - PropertyFilter[] inclusiveRangeStartFilters, - PropertyFilter[] exclusiveRangeStartFilters, - PropertyFilter[] inclusiveRangeEndFilters, - PropertyFilter[] exclusiveRangeEndFilters) - { - super(engine); - mIndex = index; - mExactFilter = exactFilter; - mReverseOrder = reverseOrder; - mReverseRange = reverseRange; - mInclusiveRangeStartFilters = inclusiveRangeStartFilters; - mExclusiveRangeStartFilters = exclusiveRangeStartFilters; - mInclusiveRangeEndFilters = inclusiveRangeEndFilters; - mExclusiveRangeEndFilters = exclusiveRangeEndFilters; - } - - public Cursor openCursor(FilterValues values) throws FetchException { - Object[] exactValues = null; - Object rangeStartValue = null; - Object rangeEndValue = null; - BoundaryType rangeStartBoundary = BoundaryType.OPEN; - BoundaryType rangeEndBoundary = BoundaryType.OPEN; - - if (values != null) { - if (mExactFilter != null) { - exactValues = values.getValuesFor(mExactFilter); - } - - // In determining the proper range values and boundary types, - // the order in which this code runs is important. The exclusive - // filters must be checked before the inclusive filters. - - for (PropertyFilter p : mExclusiveRangeStartFilters) { - Object value = values.getValue(p); - if (rangeStartBoundary == BoundaryType.OPEN || - compareWithNullHigh(value, rangeStartValue) > 0) - { - rangeStartValue = value; - rangeStartBoundary = BoundaryType.EXCLUSIVE; - } - } - - for (PropertyFilter p : mInclusiveRangeStartFilters) { - Object value = values.getValue(p); - if (rangeStartBoundary == BoundaryType.OPEN || - compareWithNullHigh(value, rangeStartValue) > 0) - { - rangeStartValue = value; - rangeStartBoundary = BoundaryType.INCLUSIVE; - } - } - - for (PropertyFilter p : mExclusiveRangeEndFilters) { - Object value = values.getValue(p); - if (rangeEndBoundary == BoundaryType.OPEN || - compareWithNullHigh(value, rangeEndValue) < 0) - { - rangeEndValue = value; - rangeEndBoundary = BoundaryType.EXCLUSIVE; - } - } - - for (PropertyFilter p : mInclusiveRangeEndFilters) { - Object value = values.getValue(p); - if (rangeEndBoundary == BoundaryType.OPEN || - compareWithNullHigh(value, rangeEndValue) < 0) - { - rangeEndValue = value; - rangeEndBoundary = BoundaryType.INCLUSIVE; - } - } - } - - return mEngine.openCursor(mIndex, exactValues, - rangeStartBoundary, rangeStartValue, - rangeEndBoundary, rangeEndValue, - mReverseRange, - mReverseOrder); - } - - public Filter buildFilter(Filter filter) { - if (mExactFilter != null) { - filter = filter == null ? mExactFilter : filter.and(mExactFilter); - } - for (PropertyFilter p : mInclusiveRangeStartFilters) { - filter = filter == null ? p : filter.and(p); - } - for (PropertyFilter p : mExclusiveRangeStartFilters) { - filter = filter == null ? p : filter.and(p); - } - for (PropertyFilter p : mInclusiveRangeEndFilters) { - filter = filter == null ? p : filter.and(p); - } - for (PropertyFilter p : mExclusiveRangeEndFilters) { - filter = filter == null ? p : filter.and(p); - } - return filter; - } - - public Query applyOrderBy(Query query) throws FetchException { - if (mIndex == null) { - // Index is null if this is a full scan with no ordering specified. - return query; - } - - int count = mIndex.getPropertyCount(); - String[] orderBy = new String[count]; - - for (int i=0; i getActualStorage() { - return mEngine.getStorageFor(mIndex); - } - - public boolean printPlan(Appendable app, int indentLevel, FilterValues values) - throws IOException - { - indent(app, indentLevel); - if (mReverseOrder) { - app.append("reverse "); - } - if (mIndex.isClustered()) { - app.append("clustered "); - } - app.append("index scan: "); - app.append(mEngine.getStorableInfo().getStorableType().getName()); - app.append('\n'); - indent(app, indentLevel); - app.append("...index: "); - mIndex.appendTo(app); - app.append('\n'); - if (mExactFilter != null) { - indent(app, indentLevel); - app.append("...exact filter: "); - mExactFilter.appendTo(app, values); - app.append('\n'); - } - if (mInclusiveRangeStartFilters.length > 0 || mExclusiveRangeStartFilters.length > 0 || - mInclusiveRangeEndFilters.length > 0 || mExclusiveRangeEndFilters.length > 0) - { - indent(app, indentLevel); - app.append("...range filter: "); - int count = 0; - for (PropertyFilter p : mExclusiveRangeStartFilters) { - if (count++ > 0) { - app.append(" & "); - } - p.appendTo(app, values); - } - for (PropertyFilter p : mInclusiveRangeStartFilters) { - if (count++ > 0) { - app.append(" & "); - } - p.appendTo(app, values); - } - for (PropertyFilter p : mExclusiveRangeEndFilters) { - if (count++ > 0) { - app.append(" & "); - } - p.appendTo(app, values); - } - for (PropertyFilter p : mInclusiveRangeEndFilters) { - if (count++ > 0) { - app.append(" & "); - } - p.appendTo(app, values); - } - app.append('\n'); - } - return true; - } - } - - private static class FullScanCursorFactory extends IndexCursorFactory { - FullScanCursorFactory(BaseQueryEngine engine, StorableIndex index) { - super(engine, index, false, false, - null, NO_FILTERS, NO_FILTERS, NO_FILTERS, NO_FILTERS); - } - - @Override - public Filter buildFilter(Filter filter) { - // Full scan doesn't filter anything. - return filter; - } - - @Override - public boolean printPlan(Appendable app, int indentLevel, FilterValues values) - throws IOException - { - indent(app, indentLevel); - app.append("full scan: "); - app.append(mEngine.getStorableInfo().getStorableType().getName()); - app.append('\n'); - return true; - } - } - - private static class KeyCursorFactory extends AbstractCursorFactory { - private final StorableIndex mIndex; - private final Filter mExactFilter; - - KeyCursorFactory(BaseQueryEngine engine, - StorableIndex index, Filter exactFilter) { - super(engine); - mIndex = index; - mExactFilter = exactFilter; - } - - public Cursor openCursor(FilterValues values) throws FetchException { - return mEngine.openKeyCursor(mIndex, values.getValuesFor(mExactFilter)); - } - - public Filter buildFilter(Filter filter) { - if (mExactFilter != null) { - filter = filter == null ? mExactFilter : filter.and(mExactFilter); - } - return filter; - } - - public Query applyOrderBy(Query query) { - return query; - } - - public Storage getActualStorage() { - return mEngine.getStorageFor(mIndex); - } - - public boolean printPlan(Appendable app, int indentLevel, FilterValues values) - throws IOException - { - indent(app, indentLevel); - app.append("index key: "); - app.append(mEngine.getStorableInfo().getStorableType().getName()); - app.append('\n'); - indent(app, indentLevel); - app.append("...index: "); - mIndex.appendTo(app); - app.append('\n'); - indent(app, indentLevel); - app.append("...exact filter: "); - mExactFilter.appendTo(app, values); - app.append('\n'); - return true; - } - } - - private static class FilteredCursorFactory - extends AbstractCursorFactory - { - private final CursorFactory mFactory; - private final Filter mFilter; - - FilteredCursorFactory(BaseQueryEngine engine, - CursorFactory factory, Filter filter) { - super(engine); - mFactory = factory; - mFilter = filter; - } - - public Cursor openCursor(FilterValues values) throws FetchException { - return FilteredCursor.applyFilter(mFilter, - values, - mFactory.openCursor(values)); - } - - public Filter buildFilter(Filter filter) { - filter = mFactory.buildFilter(filter); - filter = filter == null ? mFilter : filter.and(mFilter); - return filter; - } - - public Query applyOrderBy(Query query) throws FetchException { - return mFactory.applyOrderBy(query); - } - - public Storage getActualStorage() { - return mFactory.getActualStorage(); - } - - public boolean printPlan(Appendable app, int indentLevel, FilterValues values) - throws IOException - { - indent(app, indentLevel); - app.append("filter: "); - mFilter.appendTo(app, values); - app.append('\n'); - mFactory.printPlan(app, indentLevel + 2, values); - return true; - } - } - - private static class SortedCursorFactory extends AbstractCursorFactory { - private final CursorFactory mFactory; - private final OrderedProperty[] mHandledOrderings; - private final OrderedProperty[] mRemainderOrderings; - - private final Comparator mHandledComparator; - private final Comparator mFinisherComparator; - - SortedCursorFactory(BaseQueryEngine engine, - CursorFactory factory, - OrderedProperty[] handledOrderings, - OrderedProperty[] remainderOrderings) { - super(engine); - mFactory = factory; - if (handledOrderings != null && handledOrderings.length == 0) { - handledOrderings = null; - } - if (remainderOrderings != null && remainderOrderings.length == 0) { - remainderOrderings = null; - } - if (handledOrderings == null && remainderOrderings == null) { - throw new IllegalArgumentException(); - } - mHandledOrderings = handledOrderings; - mRemainderOrderings = remainderOrderings; - - mHandledComparator = engine.makeComparator(handledOrderings); - mFinisherComparator = engine.makeComparator(remainderOrderings); - } - - public Cursor openCursor(FilterValues values) throws FetchException { - Cursor cursor = mFactory.openCursor(values); - - SortBuffer buffer = new MergeSortBuffer - (getActualStorage(), mEngine.mMergeSortTempDir); - - return new SortedCursor(cursor, buffer, mHandledComparator, mFinisherComparator); - } - - @Override - public long count(FilterValues values) throws FetchException { - return mFactory.count(values); - } - - - public Filter buildFilter(Filter filter) { - return mFactory.buildFilter(filter); - } - - public Query applyOrderBy(Query query) throws FetchException { - int handledLength = mHandledOrderings == null ? 0 : mHandledOrderings.length; - int remainderLength = mRemainderOrderings == null ? 0 : mRemainderOrderings.length; - String[] orderBy = new String[handledLength + remainderLength]; - int pos = 0; - for (int i=0; i getActualStorage() { - return mFactory.getActualStorage(); - } - - public boolean printPlan(Appendable app, int indentLevel, FilterValues values) - throws IOException - { - indent(app, indentLevel); - if (mHandledOrderings == null) { - app.append("full sort: "); - } else { - app.append("finish sort: "); - } - app.append(Arrays.toString(mRemainderOrderings)); - app.append('\n'); - mFactory.printPlan(app, indentLevel + 2, values); - return true; - } - } - - private static class UnionedCursorFactory - extends AbstractCursorFactory - { - static CursorFactory createUnion - (BaseQueryEngine engine, - List> factories, - OrderedProperty[] totalOrderings) - { - Comparator orderComparator = engine.makeComparator(totalOrderings); - return createUnion(engine, factories, totalOrderings, orderComparator); - } - - @SuppressWarnings("unchecked") - static CursorFactory createUnion - (BaseQueryEngine engine, - List> factories, - OrderedProperty[] totalOrderings, - Comparator orderComparator) - { - if (factories.size() > 1) { - CursorFactory[] array = new CursorFactory[factories.size()]; - factories.toArray(array); - return new UnionedCursorFactory(engine, array, totalOrderings, orderComparator); - } - return factories.get(0); - } - - private final CursorFactory[] mFactories; - private final OrderedProperty[] mTotalOrderings; - private final Comparator mOrderComparator; - - private UnionedCursorFactory(BaseQueryEngine engine, - CursorFactory[] factories, - OrderedProperty[] totalOrderings, - Comparator orderComparator) { - super(engine); - mFactories = factories; - mTotalOrderings = totalOrderings; - mOrderComparator = orderComparator; - } - - public Cursor openCursor(FilterValues values) throws FetchException { - Cursor cursor = null; - for (CursorFactory factory : mFactories) { - Cursor subCursor = factory.openCursor(values); - cursor = (cursor == null) ? subCursor - : new UnionCursor(cursor, subCursor, mOrderComparator); - } - return cursor; - } - - public Filter buildFilter(Filter filter) { - for (CursorFactory factory : mFactories) { - Filter subFilter = factory.buildFilter(null); - filter = filter == null ? subFilter : filter.or(subFilter); - } - return filter; - } - - public Query applyOrderBy(Query query) throws FetchException { - if (mTotalOrderings == null || mTotalOrderings.length == 0) { - return query; - } - - String[] orderBy = new String[mTotalOrderings.length]; - for (int i=mTotalOrderings.length; --i>=0; ) { - orderBy[i] = mTotalOrderings[i].toString(); - } - - return query.orderBy(orderBy); - } - - public Storage getActualStorage() { - Storage storage = null; - for (CursorFactory factory : mFactories) { - Storage subStorage = factory.getActualStorage(); - if (storage == null) { - storage = subStorage; - } else if (storage != subStorage) { - return null; - } - } - return storage; - } - - @Override - public CursorFactory getActualFactory() throws FetchException { - Storage requiredStorage = getActualStorage(); - if (requiredStorage == mEngine.getStorage()) { - // Alternate not really needed. - return this; - } - if (requiredStorage != null) { - // All components require same external storage, so let - // external storage do the union. - return new QueryCursorFactory(this, requiredStorage); - } - - // Group factories by required storage instance, and then create a - // union of unions. - - Comparator> comparator = new Comparator>() { - public int compare(CursorFactory a, CursorFactory b) { - Storage aStorage = a.getActualStorage(); - Storage bStorage = b.getActualStorage(); - if (aStorage == bStorage) { - return 0; - } - Storage engineStorage = mEngine.getStorage(); - if (aStorage == engineStorage) { - return -1; - } else if (bStorage == engineStorage) { - return 1; - } - int aHash = System.identityHashCode(a); - int bHash = System.identityHashCode(b); - if (aHash < bHash) { - return -1; - } else if (aHash > bHash) { - return 1; - } - return 0; - } - }; - - Arrays.sort(mFactories, comparator); - - List> masterList = new ArrayList>(); - - List> subList = new ArrayList>(); - Storage group = null; - for (CursorFactory factory : mFactories) { - Storage storage = factory.getActualStorage(); - if (group != storage) { - if (subList.size() > 0) { - masterList.add(createUnion - (mEngine, subList, mTotalOrderings, mOrderComparator)); - subList.clear(); - } - group = storage; - } - CursorFactory subFactory = new QueryCursorFactory(factory, storage); - subList.add(subFactory); - } - if (subList.size() > 0) { - masterList.add(createUnion(mEngine, subList, mTotalOrderings, mOrderComparator)); - subList.clear(); - } - - return createUnion(mEngine, masterList, mTotalOrderings, mOrderComparator); - } - - public boolean printPlan(Appendable app, int indentLevel, FilterValues values) - throws IOException - { - indent(app, indentLevel); - app.append("union"); - app.append('\n'); - for (CursorFactory factory : mFactories) { - factory.printPlan(app, indentLevel + 2, values); - } - return true; - } - } - - /** - * CursorFactory implementation that reconstructs and calls an external - * Query. - */ - private static class QueryCursorFactory implements CursorFactory { - private final CursorFactory mFactory; - private final Storage mStorage; - private final Query mQuery; - - /** - * @param factory factory to derive this factory from - * @param storage actual storage to query against - */ - QueryCursorFactory(CursorFactory factory, Storage storage) throws FetchException { - mFactory = factory; - mStorage = storage; - - Filter filter = factory.buildFilter(null); - - Query query; - if (filter == null) { - query = storage.query(); - } else { - query = storage.query(filter); - } - - mQuery = factory.applyOrderBy(query); - } - - public Cursor openCursor(FilterValues values) throws FetchException { - return applyFilterValues(values).fetch(); - } - - public long count(FilterValues values) throws FetchException { - return applyFilterValues(values).count(); - } - - public Filter buildFilter(Filter filter) { - return mFactory.buildFilter(filter); - } - - public Query applyOrderBy(Query query) throws FetchException { - return mFactory.applyOrderBy(query); - } - - public Storage getActualStorage() { - return mStorage; - } - - public CursorFactory getActualFactory() { - return this; - } - - public boolean printNative(Appendable app, int indentLevel, FilterValues values) - throws IOException - { - return applyFilterValues(values).printNative(app, indentLevel); - } - - public boolean printPlan(Appendable app, int indentLevel, FilterValues values) - throws IOException - { - Query query; - try { - query = applyFilterValues(values); - } catch (IllegalStateException e) { - query = mQuery; - } - return query.printPlan(app, indentLevel); - } - - private Query applyFilterValues(FilterValues values) { - // FIXME: figure out how to transfer values directly to query. - - Query query = mQuery; - Filter filter = query.getFilter(); - // FIXME: this code can get confused if filter has constants. - if (values != null && filter != null && query.getBlankParameterCount() != 0) { - query = query.withValues(values.getValuesFor(filter)); - } - return query; - } - } - - private static class IndexAnalysis extends Visitor - implements Comparable> - { - private final StorableIndex mPrimaryKeyIndex; - private final StorableIndexSet mIndexSet; - private final OrderedProperty[] mOrderings; - - private List> mResults; - - IndexAnalysis(StorableIndex primaryKeyIndex, - StorableIndexSet indexSet, - OrderedProperty[] orderings) - { - mPrimaryKeyIndex = primaryKeyIndex; - mIndexSet = indexSet; - mOrderings = orderings; - mResults = new ArrayList>(); - } - - public Object visit(OrFilter filter, Object param) { - Filter left = filter.getLeftFilter(); - if (!(left instanceof OrFilter)) { - selectIndex(left); - } else { - left.accept(this, param); - } - Filter right = filter.getRightFilter(); - if (!(right instanceof OrFilter)) { - selectIndex(right); - } else { - right.accept(this, param); - } - return null; - } - - // This method should only be called if root filter has no 'or' operators. - public Object visit(AndFilter filter, Object param) { - selectIndex(filter); - return null; - } - - // This method should only be called if root filter has no logical operators. - public Object visit(PropertyFilter filter, Object param) { - selectIndex(filter); - return null; - } - - /** - * Compares this analysis to another which belongs to a different - * Storable type. Filters that reference a joined property may be best - * served by an index defined in the joined type, and this method aids - * in that selection. - * - * @return <0 if these results are better, 0 if equal, or >0 if other is better - */ - public int compareTo(IndexAnalysis otherAnalysis) { - if (noBestIndex()) { - if (otherAnalysis.noBestIndex()) { - return 0; - } - return 1; - } else if (otherAnalysis.noBestIndex()) { - return -1; - } else { - return IndexSelector.listCompare(mResults, otherAnalysis.mResults); - } - } - - /** - * If more than one result returned, then a union must be performed. - * This is because there exist 'or' operators in the full filter. - */ - List> getResults() { - return mResults; - } - - /** - * If more than one result, then a union must be performed. Attempt to - * reduce the result list by performing unions at the index layer. This - * reduces the number of cursors that need to be opened for a query, - * eliminating duplicate work. - */ - void reduceResults() { - if (mResults.size() <= 1) { - return; - } - - List> reduced = - new ArrayList>(mResults.size()); - - gather: - for (int i=0; i result : mResults) { - if (result.isUseless()) { - return true; - } - } - return false; - } - - private void selectIndex(Filter filter) { - IndexSelector selector = new IndexSelector(filter, mOrderings); - - StorableIndex best = mPrimaryKeyIndex; - if (mIndexSet != null) { - for (StorableIndex candidate : mIndexSet) { - int result = selector.compare(best, candidate); - if (result > 0) { - best = candidate; - } - } - } - - mResults.add(selector.examine(best)); - } - } -} diff --git a/src/main/java/com/amazon/carbonado/spi/IndexSelector.java b/src/main/java/com/amazon/carbonado/spi/IndexSelector.java deleted file mode 100644 index 4f72fdd..0000000 --- a/src/main/java/com/amazon/carbonado/spi/IndexSelector.java +++ /dev/null @@ -1,1205 +0,0 @@ -/* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. - * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks - * of Amazon Technologies, Inc. or its affiliates. All rights reserved. - * - * Licensed 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 com.amazon.carbonado.spi; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.amazon.carbonado.Storable; - -import com.amazon.carbonado.filter.Filter; -import com.amazon.carbonado.filter.OrFilter; -import com.amazon.carbonado.filter.PropertyFilter; -import com.amazon.carbonado.filter.RelOp; -import com.amazon.carbonado.filter.Visitor; - -import com.amazon.carbonado.info.ChainedProperty; -import com.amazon.carbonado.info.Direction; -import com.amazon.carbonado.info.OrderedProperty; -import com.amazon.carbonado.info.StorableIndex; -import com.amazon.carbonado.info.StorableProperty; - -/** - * Tries to find the best index to use for a query. When used to sort a list of - * indexes, the first in the list (the lowest) is the best index. - * - * @author Brian S O'Neill - * @deprecated Use {@link com.amazon.carbonado.qe.CompositeScore} - */ -public class IndexSelector implements Comparator> { - static int intCompare(int a, int b) { - if (a < b) { - return -1; - } - if (a > b) { - return 1; - } - return 0; - } - - // Also called by BaseQueryEngine. - @SuppressWarnings("unchecked") - static int listCompare(List a, - List b) { - int size = Math.min(a.size(), b.size()); - for (int i=0; i size) { - return 1; - } - return 0; - } - - // Original filter passed into constructor - private final Filter mFilter; - - // Elements of original filter, which are combined by logical 'and's. Filters - // which are likely to remove more results are ordered first in the array. - final PropertyFilter[] mFilters; - - final OrderedProperty[] mOrderings; - - /** - * @param filter filter which cannot contain any logical 'or' operations. - * @throws IllegalArgumentException if filter not supported - */ - public IndexSelector(Filter filter) { - this(filter, (OrderedProperty[]) null); - } - - /** - * @param filter optional filter which cannot contain any logical 'or' operations. - * @param orderings optional orderings - * @throws IllegalArgumentException if filter not supported - */ - @SuppressWarnings("unchecked") - public IndexSelector(Filter filter, OrderedProperty... orderings) { - mFilter = filter; - - // Copy property filters. - final List> filterList = new ArrayList>(); - - if (filter != null) { - filter.accept(new Visitor() { - public Object visit(OrFilter filter, Object param) { - throw new IllegalArgumentException("Logical 'or' not allowed"); - } - - public Object visit(PropertyFilter filter, Object param) { - filterList.add(filter); - return null; - } - }, null); - } - - mFilters = filterList.toArray(new PropertyFilter[filterList.size()]); - // Ensure all '=' operators are first, and all '!=' operators are last. - Arrays.sort(mFilters, new PropertyFilterComparator()); - - if (orderings == null || orderings.length == 0) { - mOrderings = null; - } else { - // Copy ordering properties, but don't duplicate properties. - int length = orderings.length; - Map, OrderedProperty> orderingMap = - new LinkedHashMap, OrderedProperty>(length); - for (int i=0; i ordering = orderings[i]; - if (ordering != null) { - ChainedProperty prop = ordering.getChainedProperty(); - if (!orderingMap.containsKey(prop)) { - orderingMap.put(prop, ordering); - } - } - } - - // Drop orderings against exact matches in filter since they aren't needed. - for (PropertyFilter propFilter : filterList) { - if (propFilter.getOperator() == RelOp.EQ) { - orderingMap.remove(propFilter.getChainedProperty()); - } - } - - mOrderings = orderingMap.values().toArray(new OrderedProperty[orderingMap.size()]); - } - } - - /** - * Returns <0 if the current index is better than the candidate index, 0 - * if they are equally good, or >0 if the candidate index is - * better. - *

- * Note: the best index may sort results totally reversed. The cursor that - * uses this index must iterate in reverse to compensate. - * - * @param currentIndex current "best" index - * @param candidateIndex index to test against - */ - public int compare(StorableIndex currentIndex, StorableIndex candidateIndex) { - if (currentIndex == null) { - if (candidateIndex == null) { - return 0; - } else { - return 1; - } - } else if (candidateIndex == null) { - return -1; - } - - IndexScore currentScore = new IndexScore(this, currentIndex); - IndexScore candidateScore = new IndexScore(this, candidateIndex); - - return currentScore.compareTo(candidateScore); - } - - /** - * Examines the given index for overall fitness. - */ - public IndexFitness examine(StorableIndex index) { - return new IndexFitness(this, index, mFilter, mFilters, mOrderings); - } - - /** - * Provides information regarding the overall fitness of an index for use - * in a query, and gives us information about how we can properly apply it. That is, - * if the index provides 3 out of 7 properties, we'll have to scan the output and apply the - * remaining four by hand. If an index does not sort the property for which we're doing an - * inexact match, we'll have to subsort -- and so on. - */ - public static class IndexFitness implements Comparable> { - private final StorableIndex mIndex; - private final IndexScore mIndexScore; - - private final Filter mExactFilter; - private final PropertyFilter[] mInclusiveRangeStartFilters; - private final PropertyFilter[] mExclusiveRangeStartFilters; - private final PropertyFilter[] mInclusiveRangeEndFilters; - private final PropertyFilter[] mExclusiveRangeEndFilters; - private final Filter mRemainderFilter; - - private final OrderedProperty[] mHandledOrderings; - private final OrderedProperty[] mRemainderOrderings; - - private final boolean mShouldReverseOrder; - private final boolean mShouldReverseRange; - - @SuppressWarnings("unchecked") - IndexFitness(IndexSelector selector, StorableIndex index, - Filter fullFilter, PropertyFilter[] fullFilters, - OrderedProperty[] fullOrderings) - { - mIndex = index; - mIndexScore = new IndexScore(selector, index); - - FilterScore filterScore = mIndexScore.getFilterScore(); - - Filter exactFilter; - List> inclusiveRangeStartFilters = - new ArrayList>(); - List> exclusiveRangeStartFilters = - new ArrayList>(); - List> inclusiveRangeEndFilters = new ArrayList>(); - List> exclusiveRangeEndFilters = new ArrayList>(); - Filter remainderFilter; - - Direction rangeDirection = null; - buildFilters: { - if (fullFilter == null) { - exactFilter = null; - remainderFilter = fullFilter; - break buildFilters; - } - - int exactMatches = filterScore.exactMatches(); - int indexPos = 0; - - LinkedList> filterList = - new LinkedList>(Arrays.asList(fullFilters)); - - if (exactMatches <= 0) { - exactFilter = null; - } else { - exactFilter = null; - // Build filter whose left-to-right property order matches - // the order of the index. - for (int i=0; i indexProp = index.getProperty(indexPos++); - Filter next = removeIndexProp(filterList, indexProp, RelOp.EQ); - if (next != null) { - exactFilter = (exactFilter == null) ? next : exactFilter.and(next); - } - } - } - - if (filterScore.hasInexactMatch()) { - // All matches must be consecutive, so first inexact match - // is index property after all exact matches. - StorableProperty indexProp = index.getProperty(indexPos); - rangeDirection = index.getPropertyDirection(indexPos); - - while (true) { - PropertyFilter p = removeIndexProp(filterList, indexProp, RelOp.GE); - if (p == null) { - break; - } - inclusiveRangeStartFilters.add(p); - } - - while (true) { - PropertyFilter p = removeIndexProp(filterList, indexProp, RelOp.GT); - if (p == null) { - break; - } - exclusiveRangeStartFilters.add(p); - } - - while (true) { - PropertyFilter p = removeIndexProp(filterList, indexProp, RelOp.LE); - if (p == null) { - break; - } - inclusiveRangeEndFilters.add(p); - } - - while (true) { - PropertyFilter p = removeIndexProp(filterList, indexProp, RelOp.LT); - if (p == null) { - break; - } - exclusiveRangeEndFilters.add(p); - } - } - - remainderFilter = null; - while (filterList.size() > 0) { - Filter next = filterList.removeFirst(); - remainderFilter = (remainderFilter == null) ? next : remainderFilter.and(next); - } - } - - mExactFilter = exactFilter; - mInclusiveRangeStartFilters = - inclusiveRangeStartFilters.toArray(new PropertyFilter[0]); - mExclusiveRangeStartFilters = - exclusiveRangeStartFilters.toArray(new PropertyFilter[0]); - mInclusiveRangeEndFilters = inclusiveRangeEndFilters.toArray(new PropertyFilter[0]); - mExclusiveRangeEndFilters = exclusiveRangeEndFilters.toArray(new PropertyFilter[0]); - mRemainderFilter = remainderFilter; - - OrderingScore orderingScore = mIndexScore.getOrderingScore(); - - OrderedProperty[] handledOrderings; - OrderedProperty[] remainderOrderings; - boolean shouldReverseOrder; - - buildOrderings: { - int totalMatches = orderingScore.totalMatches(); - if (fullOrderings == null || fullOrderings.length == 0 || totalMatches == 0) { - handledOrderings = null; - remainderOrderings = fullOrderings; - shouldReverseOrder = false; - break buildOrderings; - } - - shouldReverseOrder = totalMatches < 0; - totalMatches = Math.abs(totalMatches); - - if (totalMatches >= fullOrderings.length) { - handledOrderings = fullOrderings; - remainderOrderings = null; - break buildOrderings; - } - - final int pos = orderingScore.startPosition(); - - if (index.isUnique() && (pos + totalMatches) >= index.getPropertyCount()) { - // Since all properties of unique index have been used, additional - // remainder ordering is superfluous, and so it is handled. - handledOrderings = fullOrderings; - remainderOrderings = null; - break buildOrderings; - } - - Set> handledSet = new LinkedHashSet>(); - Set> remainderSet = - new LinkedHashSet>(Arrays.asList(fullOrderings)); - - for (int i=0; i chainedProp = - ChainedProperty.get(index.getProperty(pos + i)); - OrderedProperty op; - op = OrderedProperty.get(chainedProp, Direction.ASCENDING); - if (remainderSet.remove(op)) { - handledSet.add(op); - } - op = OrderedProperty.get(chainedProp, Direction.DESCENDING); - if (remainderSet.remove(op)) { - handledSet.add(op); - } - op = OrderedProperty.get(chainedProp, Direction.UNSPECIFIED); - if (remainderSet.remove(op)) { - handledSet.add(op); - } - } - - if (remainderSet.size() == 0) { - handledOrderings = fullOrderings; - remainderOrderings = null; - break buildOrderings; - } - - if (handledSet.size() == 0) { - handledOrderings = null; - remainderOrderings = fullOrderings; - break buildOrderings; - } - - handledOrderings = handledSet.toArray - (new OrderedProperty[handledSet.size()]); - remainderOrderings = remainderSet.toArray - (new OrderedProperty[remainderSet.size()]); - } - - // If using range match, but index direction is backwards. Flipping - // "shouldReverseOrder" doesn't fix the problem. Instead, swap the - // ranges around. - boolean shouldReverseRange = rangeDirection == Direction.DESCENDING; - - mHandledOrderings = handledOrderings; - mRemainderOrderings = remainderOrderings; - mShouldReverseOrder = shouldReverseOrder; - mShouldReverseRange = shouldReverseRange; - } - - private IndexFitness(StorableIndex index, IndexScore indexScore, - Filter exactFilter, - PropertyFilter[] inclusiveRangeStartFilters, - PropertyFilter[] exclusiveRangeStartFilters, - PropertyFilter[] inclusiveRangeEndFilters, - PropertyFilter[] exclusiveRangeEndFilters, - Filter remainderFilter, - OrderedProperty[] handledOrderings, - OrderedProperty[] remainderOrderings, - boolean shouldReverseOrder, - boolean shouldReverseRange) - { - mIndex = index; - mIndexScore = indexScore; - - mExactFilter = exactFilter; - mInclusiveRangeStartFilters = inclusiveRangeStartFilters; - mExclusiveRangeStartFilters = exclusiveRangeStartFilters; - mInclusiveRangeEndFilters = inclusiveRangeEndFilters; - mExclusiveRangeEndFilters = exclusiveRangeEndFilters; - mRemainderFilter = remainderFilter; - - mHandledOrderings = handledOrderings; - mRemainderOrderings = remainderOrderings; - - mShouldReverseOrder = shouldReverseOrder; - mShouldReverseRange = shouldReverseRange; - } - - private PropertyFilter removeIndexProp(List> filterList, - StorableProperty indexProp, - RelOp operator) - { - Iterator> it = filterList.iterator(); - while (it.hasNext()) { - PropertyFilter filter = it.next(); - - if (operator != filter.getOperator()) { - continue; - } - - ChainedProperty chainedProp = filter.getChainedProperty(); - if (chainedProp.getChainCount() == 0) { - StorableProperty prime = chainedProp.getPrimeProperty(); - if (indexProp.equals(prime)) { - it.remove(); - return filter; - } - } - } - return null; - } - - /** - * Returns the index that this fitness object applies to. - */ - public StorableIndex getIndex() { - return mIndex; - } - - /** - * Returns true if the index doesn't actually do anything to filter - * results or to order them. - */ - public boolean isUseless() { - return mExactFilter == null - && mInclusiveRangeStartFilters.length == 0 - && mExclusiveRangeStartFilters.length == 0 - && mInclusiveRangeEndFilters.length == 0 - && mExclusiveRangeEndFilters.length == 0 - && (mHandledOrderings == null || mHandledOrderings.length == 0); - } - - /** - * Returns true if the index results should be iterated in reverse. - */ - public boolean shouldReverseOrder() { - return mShouldReverseOrder; - } - - /** - * Returns true if start and end ranges should be reversed. - */ - public boolean shouldReverseRange() { - return mShouldReverseRange; - } - - /** - * Returns the filter handled by the applicable index for exact - * matches. Is null if none. - */ - public Filter getExactFilter() { - return mExactFilter; - } - - /** - * Returns true if index is unique and exact filter matches each index - * property. Using this index guarantees one fetch result. - */ - public boolean isKeyFilter() { - if (mExactFilter == null || !mIndex.isUnique()) { - return false; - } - - final Set> properties; - { - int propertyCount = mIndex.getPropertyCount(); - properties = new HashSet>(propertyCount); - for (int i=0; i() { - public Object visit(PropertyFilter filter, Object param) { - ChainedProperty chained = filter.getChainedProperty(); - if (chained.getChainCount() == 0) { - properties.remove(chained.getPrimeProperty()); - } - return null; - } - }, null); - - return properties.size() == 0; - } - - /** - * Returns the filters handled by the applicable index for range - * matches. All property names are the same and operator is GE. - */ - public PropertyFilter[] getInclusiveRangeStartFilters() { - return mInclusiveRangeStartFilters; - } - - /** - * Returns the filters handled by the applicable index for range - * matches. All property names are the same and operator is GT. - */ - public PropertyFilter[] getExclusiveRangeStartFilters() { - return mExclusiveRangeStartFilters; - } - - /** - * Returns the filters handled by the applicable index for range - * matches. All property names are the same and operator is LE. - */ - public PropertyFilter[] getInclusiveRangeEndFilters() { - return mInclusiveRangeEndFilters; - } - - /** - * Returns the filters handled by the applicable index for range - * matches. All property names are the same and operator is LT. - */ - public PropertyFilter[] getExclusiveRangeEndFilters() { - return mExclusiveRangeEndFilters; - } - - /** - * Returns a filter which contains terms not handled by the applicable - * index. If the selector has no filter or if the index is complete, - * null is returned. If the index filters nothing required by the - * selector, the complete filter is returned. - */ - public Filter getRemainderFilter() { - return mRemainderFilter; - } - - /** - * Returns the desired orderings handled by the applicable index, - * possibly when reversed. - */ - public OrderedProperty[] getHandledOrderings() { - return (mHandledOrderings == null) ? null : mHandledOrderings.clone(); - } - - /** - * Returns desired orderings not handled by the applicable index, - * possibly when reversed. If the selector has no ordering or the index - * is complete, null is returned. If the index orders nothing required - * by the selector, the complete reduced ordering is returned. - */ - public OrderedProperty[] getRemainderOrderings() { - return (mRemainderOrderings == null) ? null : mRemainderOrderings.clone(); - } - - /** - * Returns the orderings actually provided by the applicable index, - * possibly when reversed. Natural order is not a total order unless - * index is unique. - */ - public OrderedProperty[] getNaturalOrderings() { - return getActualOrderings(false); - } - - /** - * Returns the orderings actually provided by the applicable index, - * excluding exact filter matches, possibly when reversed. Effective - * order is not a total order unless index is unique. - */ - public OrderedProperty[] getEffectiveOrderings() { - return getActualOrderings(true); - } - - @SuppressWarnings("unchecked") - private OrderedProperty[] getActualOrderings(boolean excludeExactMatches) { - int exactMatches = 0; - if (excludeExactMatches) { - exactMatches = mIndexScore.getFilterScore().exactMatches(); - } - - int count = mIndex.getPropertyCount(); - OrderedProperty[] orderings = new OrderedProperty[count - exactMatches]; - for (int i=exactMatches; i property = mIndex.getProperty(i); - Direction direction = mIndex.getPropertyDirection(i); - if (mShouldReverseOrder) { - direction = direction.reverse(); - } - orderings[i - exactMatches] = OrderedProperty.get(property, direction); - } - return orderings; - } - - /** - * Compares this fitness to another which belongs to a different - * Storable type. Filters that reference a joined property may be best - * served by an index defined in the joined type, and this method aids - * in that selection. - * - * @return <0 if this score is better, 0 if equal, or >0 if other is better - */ - public int compareTo(IndexFitness otherFitness) { - return mIndexScore.compareTo(otherFitness.mIndexScore); - } - - /** - * Returns true if given fitness result uses the same index, and in the - * same way. - */ - public boolean canUnion(IndexFitness fitness) { - if (this == fitness) { - return true; - } - - return mIndex.equals(fitness.mIndex) && - (mExactFilter == null ? - fitness.mExactFilter == null : - (mExactFilter.equals(fitness.mExactFilter))) && - Arrays.equals(mInclusiveRangeStartFilters, - fitness.mInclusiveRangeStartFilters) && - Arrays.equals(mExclusiveRangeStartFilters, - fitness.mExclusiveRangeStartFilters) && - Arrays.equals(mInclusiveRangeEndFilters, - fitness.mInclusiveRangeEndFilters) && - Arrays.equals(mExclusiveRangeEndFilters, - fitness.mExclusiveRangeEndFilters) && - mShouldReverseOrder == fitness.mShouldReverseOrder && - mShouldReverseRange == fitness.mShouldReverseRange && - (mHandledOrderings == null ? - fitness.mHandledOrderings == null : - (Arrays.equals(mHandledOrderings, fitness.mHandledOrderings))); - } - - /** - * If the given fitness can union with this one, return a new unioned - * one. If union not possible, null is returned. - */ - public IndexFitness union(IndexFitness fitness) { - if (this == fitness) { - return this; - } - - if (!canUnion(fitness)) { - return null; - } - - // Union the remainder filter and orderings. - - Filter remainderFilter; - if (mRemainderFilter == null) { - if (fitness.mRemainderFilter == null) { - remainderFilter = null; - } else { - remainderFilter = fitness.mRemainderFilter; - } - } else { - if (fitness.mRemainderFilter == null) { - remainderFilter = mRemainderFilter; - } else if (mRemainderFilter.equals(fitness.mRemainderFilter)) { - remainderFilter = mRemainderFilter; - } else { - remainderFilter = mRemainderFilter.or(fitness.mRemainderFilter); - } - } - - OrderedProperty[] remainderOrderings; - if (mRemainderOrderings == null) { - if (fitness.mRemainderOrderings == null) { - remainderOrderings = null; - } else { - remainderOrderings = fitness.mRemainderOrderings; - } - } else { - if (fitness.mRemainderOrderings == null) { - remainderOrderings = mRemainderOrderings; - } else if (mRemainderOrderings.length >= fitness.mRemainderOrderings.length) { - remainderOrderings = mRemainderOrderings; - } else { - remainderOrderings = fitness.mRemainderOrderings; - } - } - - return new IndexFitness(mIndex, mIndexScore, - mExactFilter, - mInclusiveRangeStartFilters, - mExclusiveRangeStartFilters, - mInclusiveRangeEndFilters, - mExclusiveRangeEndFilters, - remainderFilter, - mHandledOrderings, - remainderOrderings, - mShouldReverseOrder, - mShouldReverseRange); - } - - public String toString() { - return "IndexFitness [index=" + mIndex - + ", filterScore=" + mIndexScore.getFilterScore() - + ", orderingScore=" + mIndexScore.getOrderingScore() - + ", exactFilter=" + quoteNonNull(mExactFilter) - + ", inclusiveRangeStartFilters=" + mInclusiveRangeStartFilters - + ", exclusiveRangeStartFilters=" + mExclusiveRangeStartFilters - + ", inclusiveRangeEndFilters=" + mInclusiveRangeEndFilters - + ", exclusiveRangeEndFilters=" + mExclusiveRangeEndFilters - + ", remainderFilter=" + quoteNonNull(mRemainderFilter) - + ", handledOrderings=" + Arrays.toString(mHandledOrderings) - + ", remainderOrderings=" + Arrays.toString(mRemainderOrderings) - + ", shouldReverse=" + mShouldReverseOrder - + ']'; - } - - private static String quoteNonNull(Filter value) { - return value == null ? null : ('"' + String.valueOf(value) + '"'); - } - } - - /** - * Composite of filter score and ordering score. The filter score measures - * how well an index performs the desired level of filtering. Likewise, the - * ordering score measures how well an index performs the desired ordering. - */ - private static class IndexScore implements Comparable> { - private final IndexSelector mSelector; - private final StorableIndex mIndex; - - private FilterScore mFilterScore; - private OrderingScore mOrderingScore; - - IndexScore(IndexSelector selector, StorableIndex index) { - mSelector = selector; - mIndex = index; - } - - @SuppressWarnings("unchecked") - public int compareTo(IndexScore candidateScore) { - final FilterScore thisFilterScore = this.getFilterScore(); - final FilterScore candidateFilterScore = candidateScore.getFilterScore(); - - // Compare total count of exact matching properties. - { - int result = thisFilterScore.compareExact(candidateFilterScore); - if (result != 0) { - return result; - } - } - - // Exact matches same, choose clustered index if more than one match. - if (thisFilterScore.exactMatches() > 1) { - if (mIndex.isClustered()) { - if (!candidateScore.mIndex.isClustered()) { - return -1; - } - } else if (candidateScore.mIndex.isClustered()) { - return 1; - } - } - - // Compare range match. (index can have at most one range match) - if (thisFilterScore.hasRangeMatch()) { - if (candidateFilterScore.hasRangeMatch()) { - // Both have range match, choose clustered index. - if (mIndex.isClustered()) { - if (!candidateScore.mIndex.isClustered()) { - return -1; - } - } else if (candidateScore.mIndex.isClustered()) { - return 1; - } - } else { - return -1; - } - } else if (candidateFilterScore.hasRangeMatch()) { - return 1; - } - - final OrderingScore thisOrderingScore = this.getOrderingScore(); - final OrderingScore candidateOrderingScore = candidateScore.getOrderingScore(); - - int finalResult = 0; - - // Compare orderings, but only if candidate filters anything. It is - // generally slower to scan an index just for correct ordering, - // than it is to sort the results of a full scan. Why? Because an - // index scan results in a lot of random file accesses, and disk is - // so slow. There is an exception to this rule if the candidate is - // a clustered index, in which case there are no random file - // accesses. - if (candidateFilterScore.anyMatches() || candidateScore.mIndex.isClustered()) { - int currentMatches = thisOrderingScore.totalMatches(); - int candidateMatches = candidateOrderingScore.totalMatches(); - if (currentMatches != candidateMatches) { - if (Math.abs(currentMatches) > Math.abs(candidateMatches)) { - // Only select current filter if it filters anything. - if (thisFilterScore.anyMatches()) { - return -1; - } - // Potentially use this result later. - finalResult = -1; - } else if (Math.abs(currentMatches) < Math.abs(candidateMatches)) { - return 1; - } else { - // Magnitudes are equal, but sign differs. Choose positive, - // but not yet. - finalResult = (currentMatches > 0) ? -1 : 1; - } - } - } - - // Compare total count of inexact matching properties. - { - int result = thisFilterScore.compareInexact(candidateFilterScore); - if (result != 0) { - return result; - } - } - - // Compare positioning of matching properties (favor index that best - // matches specified property sequence of filter) - { - int result = thisFilterScore.compareExactPositions(candidateFilterScore); - if (result != 0) { - return result; - } - result = thisFilterScore.compareInexactPosition(candidateFilterScore); - if (result != 0) { - return result; - } - } - - // If both indexes have a non-zero score (that is, either index would - // actually be useful), choose the one that has the least number of - // properties in it. The theory being that smaller index keys mean more - // nodes will fit into the memory cache during an index scan. This - // extra test doesn't try to estimate the average size of properties, - // so it may choose wrong. - { - if ((thisFilterScore.anyMatches() && candidateFilterScore.anyMatches()) || - (thisOrderingScore.anyMatches() && candidateOrderingScore.anyMatches())) - { - if (mIndex.getPropertyCount() < candidateScore.mIndex.getPropertyCount()) { - return -1; - } - if (mIndex.getPropertyCount() > candidateScore.mIndex.getPropertyCount()) { - return 1; - } - } - } - - // Final result derived from ordering comparison earlier. - return finalResult; - } - - /** - * Total matches on score indicates how many consecutive index - * properties (from the start) match the filter requirements. - */ - public FilterScore getFilterScore() { - if (mFilterScore != null) { - return mFilterScore; - } - - mFilterScore = new FilterScore(); - - int indexPropCount = mIndex.getPropertyCount(); - PropertyFilter[] filters = mSelector.mFilters; - int filterCount = filters.length; - - for (int i=0; i prop = mIndex.getProperty(i); - int matchesBefore = mFilterScore.totalMatches(); - for (int pos=0; pos[] orderings = mSelector.mOrderings; - - if (orderings == null || orderings.length == 0) { - return mOrderingScore = new OrderingScore(0, 0); - } - - int indexPropCount = mIndex.getPropertyCount(); - if (indexPropCount <= 0) { - return mOrderingScore = new OrderingScore(0, 0); - } - - // Make sure first ordering property follows exact matches. - - if (orderings[0].getChainedProperty().getChainCount() > 0) { - // Indexes don't currently support chained properties. - return mOrderingScore = new OrderingScore(0, 0); - } - - final StorableProperty first = - orderings[0].getChainedProperty().getPrimeProperty(); - - // Start pos after all exact matching filter properties - int pos = getFilterScore().exactMatches(); - - if (pos >= indexPropCount || !mIndex.getProperty(pos).equals(first)) { - return mOrderingScore = new OrderingScore(0, 0); - } - - boolean reverse = false; - switch (mIndex.getPropertyDirection(pos)) { - case ASCENDING: - reverse = (orderings[0].getDirection() == Direction.DESCENDING); - break; - case DESCENDING: - reverse = (orderings[0].getDirection() == Direction.ASCENDING); - break; - } - - // Match count is the run length of matching properties. - int matches = 1; - final int startPos = pos; - - calcMatches: - for (int i=1; i 0) { - // Indexes don't currently support chained properties. - break; - } - - if (mIndex.getProperty(pos).equals - (orderings[i].getChainedProperty().getPrimeProperty())) { - if (orderings[i].getDirection() != Direction.UNSPECIFIED) { - Direction expected = mIndex.getPropertyDirection(pos); - if (reverse) { - expected = expected.reverse(); - } - if (orderings[i].getDirection() != expected) { - break calcMatches; - } - } - matches++; - } - } - - return mOrderingScore = new OrderingScore(startPos, reverse ? -matches : matches); - } - } - - /** - * One of the scores that evaluates an index's fitness for a given filter. - *

A filter mentions properties, either as exact ("=") or inexact (">" "<", et al) - *

An index contains properties, in order. - *

An index property matches a filter if the filter uses that property, and if all of the - * properties in the index to the left of the property are also in the filter (since holes - * in the index make the index useless for subsequent properties) - *

Then the index filter score is a function of the number of matches it contains, - * and how early in the filter they appear. - *

Any exact filter match beats an inexact filter. - *

More exact filter matches beats fewer. - *

Inexact will be selected if there are no exact matches - * - *

Note that there will be only one inexact match, since once we're in an inexact range we - * have to scan the entire range (and a later inexact match will be arbitrarily ordered within - * that range). - * - *

For example: - *

-     * user query: "a>? & b=? & c = ?"
-     * will be presorted to
-     * "b=? & c=? & a>?
-     * a, b, c     == a[inexact]->2, b->0, c->1
-     * d, a, b, c  == useless
-     * c, d, b     == c->1
-     * 
- * ...so the "c,d,b" index will win. - */ - private static class FilterScore { - // Positions of exact matches - private List mExactMatches = new ArrayList(); - - // Properties which have been used for exact matching -- these should - // show up only once per filter set - private Set mExactMatchProps = new HashSet(); - - // Position of inexact match - private int mInexactMatchPos; - - // Property used for inexact match - private StorableProperty mInexactMatch; - - private boolean mHasRangeStart; - private boolean mHasRangeEnd; - - FilterScore() { - } - - /** - * Tally up filter score. - * - * @param prop property of candidate index - * @param filter property filter to check for index fitness - * @param pos position of filter in filter list - */ - void tally(StorableProperty prop, PropertyFilter filter, int pos) { - ChainedProperty chained = filter.getChainedProperty(); - - if (chained.getChainCount() == 0 && chained.getPrimeProperty().equals(prop)) { - switch (filter.getOperator()) { - case EQ: - // Exact match - if (mInexactMatch == null) { - mExactMatches.add(pos); - mExactMatchProps.add(prop); - } - - break; - - case LT: case GE: case GT: case LE: - // Inexact match - - if (mInexactMatch == null) { - // If for some reason the query contains an exact and - // an inexact match on the same property (a>1 & a=14) - // we'll never care about the inexact match. - if (!mExactMatchProps.contains(prop)) { - mInexactMatchPos = pos; - mInexactMatch = prop; - } - } - - // Check for range match - if (prop.equals(mInexactMatch)) { - switch (filter.getOperator()) { - case LT: case LE: - mHasRangeStart = true; - break; - case GT: case GE: - mHasRangeEnd = true; - break; - } - } - - break; - } - } - } - - int compareExact(FilterScore candidate) { - return -intCompare(mExactMatches.size(), candidate.mExactMatches.size()); - } - - int compareInexact(FilterScore candidate) { - if (mInexactMatch == null && candidate.mInexactMatch != null) { - return 1; - } else if (mInexactMatch != null && candidate.mInexactMatch == null) { - return -1; - } - return 0; - } - - int compareExactPositions(FilterScore candidate) { - return listCompare(mExactMatches, candidate.mExactMatches); - } - - int compareInexactPosition(FilterScore candidate) { - return intCompare(mInexactMatchPos, candidate.mInexactMatchPos); - } - - boolean anyMatches() { - return mExactMatches.size() > 0 || mInexactMatch != null; - } - - int exactMatches() { - return mExactMatches.size(); - } - - boolean hasRangeMatch() { - return mHasRangeStart && mHasRangeEnd; - } - - boolean hasInexactMatch() { - return mInexactMatch != null; - } - - int totalMatches() { - return mExactMatches.size() + (mInexactMatch == null ? 0 : 1); - } - - public String toString() { - return "FilterScore [exactMatches=" + mExactMatches - + ", exactMatchProps=" + mExactMatchProps - + ", inexactMatch=" + (mInexactMatch == null ? null : mInexactMatch.getName()) - + ", rangeMatch=" + hasRangeMatch() - + ']'; - } - } - - /** - * How well does this index help me sort things - */ - private static class OrderingScore { - private final int mStartPos; - private final int mTotalMatches; - - OrderingScore(int startPos, int totalMatches) { - mStartPos = startPos; - mTotalMatches = totalMatches; - } - - /** - * Returns start position of index. - */ - int startPosition() { - return mStartPos; - } - - boolean anyMatches() { - return mTotalMatches > 0; - } - - /** - * Magnitude represents count of matching orderings. If negative - * result, index produces reversed ordering. - */ - int totalMatches() { - return mTotalMatches; - } - - public String toString() { - return "OrderingScore [startPos=" + mStartPos - + ", totalMatches=" + mTotalMatches - + ']'; - } - } - - /** - * Sorts property filters such that '==' operations come before '!=' - * operations. Assuming a stable sort is used, all other property filters - * are left in place - */ - private static class PropertyFilterComparator - implements Comparator> - { - public int compare(PropertyFilter a, PropertyFilter b) { - if (a.getOperator() != b.getOperator()) { - if (a.getOperator() == RelOp.EQ) { - return -1; - } - if (a.getOperator() == RelOp.NE) { - return 1; - } - if (b.getOperator() == RelOp.EQ) { - return 1; - } - if (b.getOperator() == RelOp.NE) { - return -1; - } - } - return 0; - } - } -} diff --git a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java index 29e094d..740f26c 100644 --- a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java +++ b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java @@ -309,26 +309,22 @@ public class TestIndexedQueryAnalyzer extends TestCase { throw new UnsupportedOperationException(); } - public Cursor fetch() { + public Cursor fetchAll() { throw new UnsupportedOperationException(); } - public Cursor fetch(StorableIndex index) { + public Cursor fetchOne(StorableIndex index, Object[] identityValues) { throw new UnsupportedOperationException(); } - public Cursor fetch(StorableIndex index, Object[] identityValues) { - throw new UnsupportedOperationException(); - } - - public Cursor fetch(StorableIndex index, - Object[] identityValues, - BoundaryType rangeStartBoundary, - Object rangeStartValue, - BoundaryType rangeEndBoundary, - Object rangeEndValue, - boolean reverseRange, - boolean reverseOrder) + public Cursor fetchSubset(StorableIndex index, + Object[] identityValues, + BoundaryType rangeStartBoundary, + Object rangeStartValue, + BoundaryType rangeEndBoundary, + Object rangeEndValue, + boolean reverseRange, + boolean reverseOrder) { throw new UnsupportedOperationException(); } diff --git a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java index 3226e13..4a4511e 100644 --- a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java +++ b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java @@ -680,7 +680,7 @@ public class TestIndexedQueryExecutor extends TestCase { assertEquals(values.getFilter(), executor.getFilter()); List> expectedOrdering = - makeOrdering(StorableTestBasic.class, "intProp", "-doubleProp", "stringProp"); + makeOrdering(StorableTestBasic.class, "stringProp"); assertEquals(expectedOrdering, executor.getOrdering()); } @@ -711,14 +711,14 @@ public class TestIndexedQueryExecutor extends TestCase { static class MockSupport implements IndexedQueryExecutor.Support { Mock mMock; - public Cursor fetch(StorableIndex index, - Object[] identityValues, - BoundaryType rangeStartBoundary, - Object rangeStartValue, - BoundaryType rangeEndBoundary, - Object rangeEndValue, - boolean reverseRange, - boolean reverseOrder) + public Cursor fetchSubset(StorableIndex index, + Object[] identityValues, + BoundaryType rangeStartBoundary, + Object rangeStartValue, + BoundaryType rangeEndBoundary, + Object rangeEndValue, + boolean reverseRange, + boolean reverseOrder) { mMock.mIdentityValues = identityValues; mMock.mRangeStartBoundary = rangeStartBoundary; diff --git a/src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java index ac4ebea..423cd74 100644 --- a/src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java +++ b/src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java @@ -194,7 +194,7 @@ public class TestJoinedQueryExecutor extends TestQueryExecutor { return mQuery.getStorableType(); } - public Cursor fetch() throws FetchException { + public Cursor fetchAll() throws FetchException { return mQuery.fetch(); } } diff --git a/src/test/java/com/amazon/carbonado/qe/TestUnionQueryAnalyzer.java b/src/test/java/com/amazon/carbonado/qe/TestUnionQueryAnalyzer.java index 7488c5b..0278eab 100644 --- a/src/test/java/com/amazon/carbonado/qe/TestUnionQueryAnalyzer.java +++ b/src/test/java/com/amazon/carbonado/qe/TestUnionQueryAnalyzer.java @@ -68,6 +68,15 @@ public class TestUnionQueryAnalyzer extends TestCase { super(name); } + public void testNullFilter() throws Exception { + UnionQueryAnalyzer uqa = + new UnionQueryAnalyzer(Shipment.class, TestIndexedQueryAnalyzer.RepoAccess.INSTANCE); + UnionQueryAnalyzer.Result result = uqa.analyze(null, null); + List.Result> subResults = result.getSubResults(); + + assertEquals(1, subResults.size()); + } + public void testSingleSubResult() throws Exception { UnionQueryAnalyzer uqa = new UnionQueryAnalyzer(Shipment.class, TestIndexedQueryAnalyzer.RepoAccess.INSTANCE); -- cgit v1.2.3