summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorBrian S. O'Neill <bronee@gmail.com>2006-09-03 06:14:41 +0000
committerBrian S. O'Neill <bronee@gmail.com>2006-09-03 06:14:41 +0000
commit39fce59a840b723eb013bc79285687986592b2da (patch)
tree6ae5e1602fca5bedfe63643286588a1d35575ccc /src/main
parent967e17697f343849a4c0e420c813d382b11eda44 (diff)
More work on query engine.
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/com/amazon/carbonado/cursor/IteratorCursor.java36
-rw-r--r--src/main/java/com/amazon/carbonado/filter/Filter.java2
-rw-r--r--src/main/java/com/amazon/carbonado/qe/AbstractQuery.java4
-rw-r--r--src/main/java/com/amazon/carbonado/qe/AbstractQueryExecutor.java2
-rw-r--r--src/main/java/com/amazon/carbonado/qe/DelegatedQueryExecutor.java2
-rw-r--r--src/main/java/com/amazon/carbonado/qe/EmptyQuery.java1
-rw-r--r--src/main/java/com/amazon/carbonado/qe/FilteredQueryExecutor.java4
-rw-r--r--src/main/java/com/amazon/carbonado/qe/FullScanIndexedQueryExecutor.java10
-rw-r--r--src/main/java/com/amazon/carbonado/qe/FullScanQueryExecutor.java6
-rw-r--r--src/main/java/com/amazon/carbonado/qe/IndexedQueryExecutor.java28
-rw-r--r--src/main/java/com/amazon/carbonado/qe/IterableQueryExecutor.java18
-rw-r--r--src/main/java/com/amazon/carbonado/qe/JoinedQueryExecutor.java4
-rw-r--r--src/main/java/com/amazon/carbonado/qe/KeyQueryExecutor.java6
-rw-r--r--src/main/java/com/amazon/carbonado/qe/QueryExecutor.java2
-rw-r--r--src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java4
-rw-r--r--src/main/java/com/amazon/carbonado/qe/StandardQuery.java404
-rw-r--r--src/main/java/com/amazon/carbonado/qe/UnionQueryExecutor.java4
-rw-r--r--src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorage.java4
-rw-r--r--src/main/java/com/amazon/carbonado/spi/MasterStorableGenerator.java2
-rw-r--r--src/main/java/com/amazon/carbonado/spi/StorableGenerator.java2
20 files changed, 496 insertions, 49 deletions
diff --git a/src/main/java/com/amazon/carbonado/cursor/IteratorCursor.java b/src/main/java/com/amazon/carbonado/cursor/IteratorCursor.java
index 0330629..a32b45f 100644
--- a/src/main/java/com/amazon/carbonado/cursor/IteratorCursor.java
+++ b/src/main/java/com/amazon/carbonado/cursor/IteratorCursor.java
@@ -21,19 +21,44 @@ package com.amazon.carbonado.cursor;
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.concurrent.locks.Lock;
+
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
/**
* Adapts an Iterator into a Cursor.
*
* @author Brian S O'Neill
*/
public class IteratorCursor<S> extends AbstractCursor<S> {
- private Iterator<S> mIterator;
+ private static final AtomicReferenceFieldUpdater<IteratorCursor, Lock> lockUpdater =
+ AtomicReferenceFieldUpdater.newUpdater(IteratorCursor.class, Lock.class, "mLock");
+
+ private volatile Iterator<S> mIterator;
+ private volatile Lock mLock;
/**
* @param iterable collection to iterate over, or null for empty cursor
*/
public IteratorCursor(Iterable<S> iterable) {
- this(iterable == null ? (Iterator<S>) null : iterable.iterator());
+ this(iterable, null);
+ }
+
+ /**
+ * @param iterable collection to iterate over, or null for empty cursor
+ * @param lock optional lock to hold while cursor is open
+ */
+ public IteratorCursor(Iterable<S> iterable, Lock lock) {
+ if (iterable == null) {
+ mIterator = null;
+ mLock = null;
+ } else {
+ if (lock != null) {
+ lock.lock();
+ }
+ mIterator = iterable.iterator();
+ mLock = lock;
+ }
}
/**
@@ -41,10 +66,17 @@ public class IteratorCursor<S> extends AbstractCursor<S> {
*/
public IteratorCursor(Iterator<S> iterator) {
mIterator = iterator;
+ mLock = null;
}
public void close() {
mIterator = null;
+ // Use AtomicReferenceFieldUpdater to allow close method to be safely
+ // called multiple times without unlocking multiple times.
+ Lock lock = lockUpdater.getAndSet(this, null);
+ if (lock != null) {
+ lock.unlock();
+ }
}
public boolean hasNext() {
diff --git a/src/main/java/com/amazon/carbonado/filter/Filter.java b/src/main/java/com/amazon/carbonado/filter/Filter.java
index f688d60..9d55a99 100644
--- a/src/main/java/com/amazon/carbonado/filter/Filter.java
+++ b/src/main/java/com/amazon/carbonado/filter/Filter.java
@@ -132,7 +132,7 @@ public abstract class Filter<S extends Storable> implements Appender {
synchronized (cCache) {
Map<Object, Filter<S>> filterCache = (Map<Object, Filter<S>>) cCache.get(type);
if (filterCache == null) {
- filterCache = new SoftValuedHashMap(7);
+ filterCache = new SoftValuedHashMap();
cCache.put(type, filterCache);
}
return filterCache;
diff --git a/src/main/java/com/amazon/carbonado/qe/AbstractQuery.java b/src/main/java/com/amazon/carbonado/qe/AbstractQuery.java
index 4549151..eb2e977 100644
--- a/src/main/java/com/amazon/carbonado/qe/AbstractQuery.java
+++ b/src/main/java/com/amazon/carbonado/qe/AbstractQuery.java
@@ -44,8 +44,10 @@ import com.amazon.carbonado.util.Appender;
* @author Brian S O'Neill
*/
public abstract class AbstractQuery<S extends Storable> implements Query<S>, Appender {
+ // FIXME: remove this
protected static final String[] EMPTY_ORDERINGS = {};
+ // FIXME: remove this
protected static String[] extractOrderingNames(OrderedProperty<?>[] orderings) {
String[] orderingStrings;
if (orderings == null || orderings.length == 0) {
@@ -58,8 +60,6 @@ public abstract class AbstractQuery<S extends Storable> implements Query<S>, App
return orderingStrings;
}
- // Note: Since constructor takes no parameters, this class is called
- // Abstract instead of Base.
protected AbstractQuery() {
}
diff --git a/src/main/java/com/amazon/carbonado/qe/AbstractQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/AbstractQueryExecutor.java
index 01b4c57..dc67fc1 100644
--- a/src/main/java/com/amazon/carbonado/qe/AbstractQueryExecutor.java
+++ b/src/main/java/com/amazon/carbonado/qe/AbstractQueryExecutor.java
@@ -40,7 +40,7 @@ public abstract class AbstractQueryExecutor<S extends Storable> implements Query
* Counts results by opening a cursor and skipping entries.
*/
public long count(FilterValues<S> values) throws FetchException {
- Cursor<S> cursor = openCursor(values);
+ Cursor<S> cursor = fetch(values);
try {
long count = cursor.skipNext(Integer.MAX_VALUE);
if (count == Integer.MAX_VALUE) {
diff --git a/src/main/java/com/amazon/carbonado/qe/DelegatedQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/DelegatedQueryExecutor.java
index 8bde825..9c950e0 100644
--- a/src/main/java/com/amazon/carbonado/qe/DelegatedQueryExecutor.java
+++ b/src/main/java/com/amazon/carbonado/qe/DelegatedQueryExecutor.java
@@ -83,7 +83,7 @@ public class DelegatedQueryExecutor<S extends Storable> implements QueryExecutor
return mStorage.getStorableType();
}
- public Cursor<S> openCursor(FilterValues<S> values) throws FetchException {
+ public Cursor<S> fetch(FilterValues<S> values) throws FetchException {
return applyFilterValues(values).fetch();
}
diff --git a/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java b/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java
index 60b027f..ba18531 100644
--- a/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java
+++ b/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java
@@ -50,6 +50,7 @@ public final class EmptyQuery<S extends Storable> extends AbstractQuery<S> {
* @param storage required storage object
* @param orderings optional order-by properties
*/
+ // FIXME: remove this
public EmptyQuery(Storage<S> storage, OrderedProperty[] orderings) {
if (storage == null) {
throw new IllegalArgumentException();
diff --git a/src/main/java/com/amazon/carbonado/qe/FilteredQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/FilteredQueryExecutor.java
index 4da38d9..d2c45aa 100644
--- a/src/main/java/com/amazon/carbonado/qe/FilteredQueryExecutor.java
+++ b/src/main/java/com/amazon/carbonado/qe/FilteredQueryExecutor.java
@@ -62,8 +62,8 @@ public class FilteredQueryExecutor<S extends Storable> extends AbstractQueryExec
mFilter = filter.initialFilterValues().getFilter();
}
- public Cursor<S> openCursor(FilterValues<S> values) throws FetchException {
- return FilteredCursor.applyFilter(mFilter, values, mExecutor.openCursor(values));
+ public Cursor<S> fetch(FilterValues<S> values) throws FetchException {
+ return FilteredCursor.applyFilter(mFilter, values, mExecutor.fetch(values));
}
/**
diff --git a/src/main/java/com/amazon/carbonado/qe/FullScanIndexedQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/FullScanIndexedQueryExecutor.java
index 33cd1c2..3ac0169 100644
--- a/src/main/java/com/amazon/carbonado/qe/FullScanIndexedQueryExecutor.java
+++ b/src/main/java/com/amazon/carbonado/qe/FullScanIndexedQueryExecutor.java
@@ -62,8 +62,8 @@ public abstract class FullScanIndexedQueryExecutor<S extends Storable>
}
@Override
- public Cursor<S> openCursor(FilterValues<S> values) throws FetchException {
- return openCursor(mIndex);
+ public Cursor<S> fetch(FilterValues<S> values) throws FetchException {
+ return fetch(mIndex);
}
/**
@@ -74,8 +74,8 @@ public abstract class FullScanIndexedQueryExecutor<S extends Storable>
return Collections.unmodifiableList(Arrays.asList(mIndex.getOrderedProperties()));
}
- protected Cursor<S> openCursor() throws FetchException {
- return openCursor(mIndex);
+ protected Cursor<S> fetch() throws FetchException {
+ return fetch(mIndex);
}
/**
@@ -83,5 +83,5 @@ public abstract class FullScanIndexedQueryExecutor<S extends Storable>
*
* @param index index to open, which may be a primary key index
*/
- protected abstract Cursor<S> openCursor(StorableIndex<S> index) throws FetchException;
+ protected abstract Cursor<S> fetch(StorableIndex<S> 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 e5072ce..1e2ad16 100644
--- a/src/main/java/com/amazon/carbonado/qe/FullScanQueryExecutor.java
+++ b/src/main/java/com/amazon/carbonado/qe/FullScanQueryExecutor.java
@@ -58,8 +58,8 @@ public abstract class FullScanQueryExecutor<S extends Storable> extends Abstract
return Filter.getOpenFilter(mType);
}
- public Cursor<S> openCursor(FilterValues<S> values) throws FetchException {
- return openCursor();
+ public Cursor<S> fetch(FilterValues<S> values) throws FetchException {
+ return fetch();
}
/**
@@ -82,5 +82,5 @@ public abstract class FullScanQueryExecutor<S extends Storable> extends Abstract
/**
* Return a new Cursor instance.
*/
- protected abstract Cursor<S> openCursor() throws FetchException;
+ protected abstract Cursor<S> fetch() throws FetchException;
}
diff --git a/src/main/java/com/amazon/carbonado/qe/IndexedQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/IndexedQueryExecutor.java
index 7249164..28bd7e4 100644
--- a/src/main/java/com/amazon/carbonado/qe/IndexedQueryExecutor.java
+++ b/src/main/java/com/amazon/carbonado/qe/IndexedQueryExecutor.java
@@ -90,7 +90,7 @@ public abstract class IndexedQueryExecutor<S extends Storable> extends AbstractQ
return mIndex.getStorableType();
}
- public Cursor<S> openCursor(FilterValues<S> values) throws FetchException {
+ public Cursor<S> fetch(FilterValues<S> values) throws FetchException {
Object[] identityValues = null;
Object rangeStartValue = null;
Object rangeEndValue = null;
@@ -147,11 +147,11 @@ public abstract class IndexedQueryExecutor<S extends Storable> extends AbstractQ
}
}
- return openCursor(mIndex, identityValues,
- rangeStartBoundary, rangeStartValue,
- rangeEndBoundary, rangeEndValue,
- mReverseRange,
- mReverseOrder);
+ return fetch(mIndex, identityValues,
+ rangeStartBoundary, rangeStartValue,
+ rangeEndBoundary, rangeEndValue,
+ mReverseRange,
+ mReverseOrder);
}
public Filter<S> getFilter() {
@@ -254,13 +254,13 @@ public abstract class IndexedQueryExecutor<S extends Storable> extends AbstractQ
* @param reverseOrder when true, iteration should be reversed from its
* natural order
*/
- protected abstract Cursor<S> openCursor(StorableIndex<S> index,
- Object[] identityValues,
- BoundaryType rangeStartBoundary,
- Object rangeStartValue,
- BoundaryType rangeEndBoundary,
- Object rangeEndValue,
- boolean reverseRange,
- boolean reverseOrder)
+ protected abstract Cursor<S> fetch(StorableIndex<S> 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/IterableQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/IterableQueryExecutor.java
index 7788fba..b80066e 100644
--- a/src/main/java/com/amazon/carbonado/qe/IterableQueryExecutor.java
+++ b/src/main/java/com/amazon/carbonado/qe/IterableQueryExecutor.java
@@ -18,6 +18,8 @@
package com.amazon.carbonado.qe;
+import java.util.concurrent.locks.Lock;
+
import com.amazon.carbonado.Cursor;
import com.amazon.carbonado.Storable;
@@ -31,6 +33,7 @@ import com.amazon.carbonado.cursor.IteratorCursor;
*/
public class IterableQueryExecutor<S extends Storable> extends FullScanQueryExecutor<S> {
private final Iterable<S> mIterable;
+ private final Lock mLock;
/**
* @param type type of Storable
@@ -38,11 +41,22 @@ public class IterableQueryExecutor<S extends Storable> extends FullScanQueryExec
* @throws IllegalArgumentException if type is null
*/
public IterableQueryExecutor(Class<S> type, Iterable<S> iterable) {
+ this(type, iterable, null);
+ }
+
+ /**
+ * @param type type of Storable
+ * @param iterable collection to iterate over, or null for empty cursor
+ * @param lock optional lock to hold while cursor is open
+ * @throws IllegalArgumentException if type is null
+ */
+ public IterableQueryExecutor(Class<S> type, Iterable<S> iterable, Lock lock) {
super(type);
mIterable = iterable;
+ mLock = lock;
}
- protected Cursor<S> openCursor() {
- return new IteratorCursor<S>(mIterable);
+ protected Cursor<S> fetch() {
+ return new IteratorCursor<S>(mIterable, mLock);
}
}
diff --git a/src/main/java/com/amazon/carbonado/qe/JoinedQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/JoinedQueryExecutor.java
index 4d8f48d..b5dc0c9 100644
--- a/src/main/java/com/amazon/carbonado/qe/JoinedQueryExecutor.java
+++ b/src/main/java/com/amazon/carbonado/qe/JoinedQueryExecutor.java
@@ -141,8 +141,8 @@ public class JoinedQueryExecutor<A extends Storable, B extends Storable>
return mBFilter;
}
- public Cursor<B> openCursor(FilterValues<B> values) throws FetchException {
- return mFactory.join(mAExecutor.openCursor(transferValues(values)));
+ public Cursor<B> fetch(FilterValues<B> values) throws FetchException {
+ return mFactory.join(mAExecutor.fetch(transferValues(values)));
}
public List<OrderedProperty<B>> getOrdering() {
diff --git a/src/main/java/com/amazon/carbonado/qe/KeyQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/KeyQueryExecutor.java
index e2d692d..fe07ed6 100644
--- a/src/main/java/com/amazon/carbonado/qe/KeyQueryExecutor.java
+++ b/src/main/java/com/amazon/carbonado/qe/KeyQueryExecutor.java
@@ -67,8 +67,8 @@ public abstract class KeyQueryExecutor<S extends Storable> extends AbstractQuery
return mIndex.getStorableType();
}
- public Cursor<S> openCursor(FilterValues<S> values) throws FetchException {
- return openCursor(mIndex, values.getValuesFor(mKeyFilter));
+ public Cursor<S> fetch(FilterValues<S> values) throws FetchException {
+ return fetch(mIndex, values.getValuesFor(mKeyFilter));
}
public Filter<S> getFilter() {
@@ -106,6 +106,6 @@ public abstract class KeyQueryExecutor<S extends Storable> extends AbstractQuery
* @param index index to open, which may be a primary key index
* @param keyValues list of exactly matching values to apply to index
*/
- protected abstract Cursor<S> openCursor(StorableIndex<S> index, Object[] keyValues)
+ protected abstract Cursor<S> fetch(StorableIndex<S> index, Object[] keyValues)
throws FetchException;
}
diff --git a/src/main/java/com/amazon/carbonado/qe/QueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/QueryExecutor.java
index 6861b2c..024c31c 100644
--- a/src/main/java/com/amazon/carbonado/qe/QueryExecutor.java
+++ b/src/main/java/com/amazon/carbonado/qe/QueryExecutor.java
@@ -46,7 +46,7 @@ public interface QueryExecutor<S extends Storable> {
/**
* Returns a new cursor using the given filter values.
*/
- Cursor<S> openCursor(FilterValues<S> values) throws FetchException;
+ Cursor<S> fetch(FilterValues<S> values) throws FetchException;
/**
* Counts the query results using the given filter values.
diff --git a/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java
index 5c5258a..822cde8 100644
--- a/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java
+++ b/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java
@@ -89,8 +89,8 @@ public abstract class SortedQueryExecutor<S extends Storable> extends AbstractQu
mRemainderOrderings = remainderOrderings;
}
- public Cursor<S> openCursor(FilterValues<S> values) throws FetchException {
- Cursor<S> cursor = mExecutor.openCursor(values);
+ public Cursor<S> fetch(FilterValues<S> values) throws FetchException {
+ Cursor<S> cursor = mExecutor.fetch(values);
SortBuffer<S> buffer = createSortBuffer();
return new SortedCursor<S>(cursor, buffer, mHandledComparator, mFinisherComparator);
}
diff --git a/src/main/java/com/amazon/carbonado/qe/StandardQuery.java b/src/main/java/com/amazon/carbonado/qe/StandardQuery.java
new file mode 100644
index 0000000..7152e55
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/qe/StandardQuery.java
@@ -0,0 +1,404 @@
+/*
+ * 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 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.Storage;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.Transaction;
+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.filter.OpenFilter;
+import com.amazon.carbonado.filter.RelOp;
+
+import com.amazon.carbonado.info.OrderedProperty;
+
+import com.amazon.carbonado.util.Appender;
+
+/**
+ * Abstract query implementation which uses a {@link QueryExecutor}.
+ *
+ * @author Brian S O'Neill
+ */
+public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>
+ implements Appender
+{
+ // Values for this query, which may be null.
+ private final FilterValues<S> mValues;
+ // Properties that this query is ordered by.
+ private final String[] mOrderings;
+
+ private volatile QueryExecutor<S> mExecutor;
+
+ /**
+ * @param values optional values object, defaults to open filter if null
+ * @param orderings optional order-by properties
+ */
+ // FIXME: remove this
+ protected StandardQuery(FilterValues<S> values, OrderedProperty<S>... orderings) {
+ this(values, extractOrderingNames(orderings));
+ }
+
+ /**
+ * @param values optional values object, defaults to open filter if null
+ * @param orderings optional order-by properties, not cloned, which may be
+ * prefixed with '+' or '-'
+ */
+ protected StandardQuery(FilterValues<S> values, String... orderings) {
+ mValues = values;
+ mOrderings = orderings == null ? EMPTY_ORDERINGS : orderings;
+ }
+
+ public Class<S> getStorableType() {
+ return getStorage().getStorableType();
+ }
+
+ public Filter<S> getFilter() {
+ FilterValues<S> values = mValues;
+ if (values != null) {
+ return values.getFilter();
+ }
+ return Filter.getOpenFilter(getStorage().getStorableType());
+ }
+
+ public FilterValues<S> getFilterValues() {
+ return mValues;
+ }
+
+ public int getBlankParameterCount() {
+ return mValues == null ? 0 : mValues.getBlankParameterCount();
+ }
+
+ public Query<S> with(int value) {
+ return newInstance(requireValues().with(value));
+ }
+
+ public Query<S> with(long value) {
+ return newInstance(requireValues().with(value));
+ }
+
+ public Query<S> with(float value) {
+ return newInstance(requireValues().with(value));
+ }
+
+ public Query<S> with(double value) {
+ return newInstance(requireValues().with(value));
+ }
+
+ public Query<S> with(boolean value) {
+ return newInstance(requireValues().with(value));
+ }
+
+ public Query<S> with(char value) {
+ return newInstance(requireValues().with(value));
+ }
+
+ public Query<S> with(byte value) {
+ return newInstance(requireValues().with(value));
+ }
+
+ public Query<S> with(short value) {
+ return newInstance(requireValues().with(value));
+ }
+
+ public Query<S> with(Object value) {
+ return newInstance(requireValues().with(value));
+ }
+
+ public Query<S> withValues(Object... values) {
+ if (values == null || values.length == 0) {
+ return this;
+ }
+ return newInstance(requireValues().withValues(values));
+ }
+
+ public Query<S> and(Filter<S> filter) throws FetchException {
+ FilterValues<S> values = mValues;
+ Query<S> newQuery;
+ if (values == null) {
+ newQuery = getStorage().query(filter);
+ } else {
+ newQuery = getStorage().query(values.getFilter().and(filter));
+ newQuery = newQuery.withValues(values.getValues());
+ }
+ return mOrderings.length == 0 ? newQuery : newQuery.orderBy(mOrderings);
+ }
+
+ public Query<S> or(Filter<S> filter) throws FetchException {
+ FilterValues<S> values = mValues;
+ if (values == null) {
+ throw new IllegalStateException("Query is already guaranteed to fetch everything");
+ }
+ Query<S> newQuery = getStorage().query(values.getFilter().or(filter));
+ newQuery = newQuery.withValues(values.getValues());
+ return mOrderings.length == 0 ? newQuery : newQuery.orderBy(mOrderings);
+ }
+
+ public Query<S> not() throws FetchException {
+ FilterValues<S> values = mValues;
+ if (values == null) {
+ return new EmptyQuery<S>(getStorage(), mOrderings);
+ }
+ Query<S> newQuery = getStorage().query(values.getFilter().not());
+ newQuery = newQuery.withValues(values.getSuppliedValues());
+ return mOrderings.length == 0 ? newQuery : newQuery.orderBy(mOrderings);
+ }
+
+ public Query<S> orderBy(String property) throws FetchException {
+ StandardQuery<S> query = newInstance(mValues, property);
+ // Get executor to ensure order property is correct.
+ query.executor();
+ return query;
+ }
+
+ public Query<S> orderBy(String... properties) throws FetchException {
+ StandardQuery<S> query = newInstance(mValues, properties);
+ // Get executor to ensure order properties are correct.
+ query.executor();
+ return query;
+ }
+
+ public Cursor<S> fetch() throws FetchException {
+ return executor().fetch(mValues);
+ }
+
+ public Cursor<S> fetchAfter(S start) throws FetchException {
+ String[] orderings;
+ if (start == null || (orderings = mOrderings).length == 0) {
+ return fetch();
+ }
+
+ Class<S> storableType = getStorage().getStorableType();
+ Filter<S> orderFilter = Filter.getClosedFilter(storableType);
+ Filter<S> 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<S> newQuery = this.and(orderFilter);
+
+ for (int i=0; i<values.length; i++) {
+ for (int j=0; j<=i; j++) {
+ newQuery = newQuery.with(values[j]);
+ }
+ }
+
+ return newQuery.fetch();
+ }
+
+ public boolean tryDeleteOne() throws PersistException {
+ Transaction txn = enterTransactionForDelete(IsolationLevel.READ_COMMITTED);
+ try {
+ Cursor<S> 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();
+ }
+ if (txn != null) {
+ txn.commit();
+ }
+ return result;
+ } catch (FetchException e) {
+ throw e.toPersistException();
+ } finally {
+ if (txn != null) {
+ txn.exit();
+ }
+ }
+ }
+
+ public void deleteAll() throws PersistException {
+ Transaction txn = enterTransactionForDelete(IsolationLevel.READ_COMMITTED);
+ try {
+ Cursor<S> cursor = fetch();
+ try {
+ while (cursor.hasNext()) {
+ cursor.next().tryDelete();
+ }
+ } finally {
+ cursor.close();
+ }
+ if (txn != null) {
+ txn.commit();
+ }
+ } catch (FetchException e) {
+ throw e.toPersistException();
+ } finally {
+ if (txn != null) {
+ txn.exit();
+ }
+ }
+ }
+
+ public long count() throws FetchException {
+ return executor().count(mValues);
+ }
+
+ public boolean printNative(Appendable app, int indentLevel) throws IOException {
+ return executor().printNative(app, indentLevel, mValues);
+ }
+
+ public boolean printPlan(Appendable app, int indentLevel) throws IOException {
+ return executor().printPlan(app, indentLevel, mValues);
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = getStorage().hashCode() * 31;
+ if (mValues != null) {
+ hash += mValues.hashCode();
+ }
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof StandardQuery) {
+ StandardQuery<?> other = (StandardQuery<?>) obj;
+ return getStorage().equals(other.getStorage()) &&
+ (mValues == null ? (other.mValues == null) : (mValues.equals(other.mValues)));
+ }
+ return false;
+ }
+
+ public void appendTo(Appendable app) throws IOException {
+ app.append("Query {type=");
+ app.append(getStorableType().getName());
+ app.append(", filter=");
+ Filter<S> filter = getFilter();
+ if (filter instanceof OpenFilter || filter instanceof ClosedFilter) {
+ filter.appendTo(app);
+ } else {
+ app.append('"');
+ filter.appendTo(app, mValues);
+ app.append('"');
+ }
+
+ if (mOrderings != null && mOrderings.length > 0) {
+ app.append(", orderBy=[");
+ for (int i=0; i<mOrderings.length; i++) {
+ if (i > 0) {
+ app.append(", ");
+ }
+ app.append(mOrderings[i]);
+ }
+ app.append(']');
+ }
+
+ app.append('}');
+ }
+
+ private FilterValues<S> requireValues() {
+ FilterValues<S> values = mValues;
+ if (values == null) {
+ throw new IllegalStateException("Query doesn't have any parameters");
+ }
+ return values;
+ }
+
+ /**
+ * Return the Storage object that the query is operating on.
+ */
+ protected abstract Storage<S> getStorage();
+
+ /**
+ * Enter a transaction as needed by the standard delete operation, or null
+ * if transactions are not supported.
+ *
+ * @param level minimum desired isolation level
+ */
+ protected abstract Transaction enterTransactionForDelete(IsolationLevel level);
+
+ /**
+ * Return a new or cached executor.
+ *
+ * @param values optional values object, defaults to open filter if null
+ * @param orderings optional order-by properties, which may be prefixed
+ * with '+' or '-'
+ */
+ protected abstract QueryExecutor<S> getExecutor(FilterValues<S> values, String... orderings);
+
+ /**
+ * Return a new or cached instance of StandardQuery implementation, using
+ * new filter values. The Filter in the FilterValues is the same as was
+ * passed in the constructor.
+ *
+ * @param values optional values object, defaults to open filter if null
+ * @param orderings order-by properties, never null
+ */
+ protected abstract StandardQuery<S> newInstance(FilterValues<S> values, String... orderings);
+
+ private StandardQuery<S> newInstance(FilterValues<S> values) {
+ return newInstance(values, mOrderings);
+ }
+
+ private QueryExecutor<S> executor() {
+ QueryExecutor<S> executor = mExecutor;
+ if (executor == null) {
+ mExecutor = executor = getExecutor(mValues, mOrderings);
+ }
+ return executor;
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/qe/UnionQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/UnionQueryExecutor.java
index cc3d366..39e6157 100644
--- a/src/main/java/com/amazon/carbonado/qe/UnionQueryExecutor.java
+++ b/src/main/java/com/amazon/carbonado/qe/UnionQueryExecutor.java
@@ -83,10 +83,10 @@ public class UnionQueryExecutor<S extends Storable> extends AbstractQueryExecuto
mOrderComparator = SortedCursor.createComparator(totalOrderings);
}
- public Cursor<S> openCursor(FilterValues<S> values) throws FetchException {
+ public Cursor<S> fetch(FilterValues<S> values) throws FetchException {
Cursor<S> cursor = null;
for (QueryExecutor<S> executor : mExecutors) {
- Cursor<S> subCursor = executor.openCursor(values);
+ Cursor<S> subCursor = executor.fetch(values);
cursor = (cursor == null) ? subCursor
: new UnionCursor<S>(cursor, subCursor, mOrderComparator);
}
diff --git a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorage.java b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorage.java
index 99c4bea..d5b2187 100644
--- a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorage.java
+++ b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorage.java
@@ -641,10 +641,6 @@ class JDBCStorage<S extends Storable> extends BaseQueryCompiler<S>
protected BaseQuery<S> newInstance(FilterValues<S> values) {
return new JDBCQuery(mCursorFactory, values, getOrderings());
}
-
- protected BaseQuery<S> cachedInstance(Filter<S> filter) throws FetchException {
- return (BaseQuery<S>) JDBCStorage.this.getCompiledQuery(filter);
- }
}
/**
diff --git a/src/main/java/com/amazon/carbonado/spi/MasterStorableGenerator.java b/src/main/java/com/amazon/carbonado/spi/MasterStorableGenerator.java
index 5ada2bd..317aac6 100644
--- a/src/main/java/com/amazon/carbonado/spi/MasterStorableGenerator.java
+++ b/src/main/java/com/amazon/carbonado/spi/MasterStorableGenerator.java
@@ -107,7 +107,7 @@ public final class MasterStorableGenerator<S extends Storable> {
* </pre>
*
* Subclasses can access the MasterSupport instance via the protected field
- * named by MASTER_SUPPORT_FIELD_NAME.
+ * named by {@link StorableGenerator#SUPPORT_FIELD_NAME SUPPORT_FIELD_NAME}.
*
* @throws com.amazon.carbonado.MalformedTypeException if Storable type is not well-formed
* @throws IllegalArgumentException if type is null
diff --git a/src/main/java/com/amazon/carbonado/spi/StorableGenerator.java b/src/main/java/com/amazon/carbonado/spi/StorableGenerator.java
index fd41677..4972bfa 100644
--- a/src/main/java/com/amazon/carbonado/spi/StorableGenerator.java
+++ b/src/main/java/com/amazon/carbonado/spi/StorableGenerator.java
@@ -144,7 +144,7 @@ public final class StorableGenerator<S extends Storable> {
/** Constraint field names are propertyName + "$constraint$" + ordinal */
public static final String CONSTRAINT_FIELD_ELEMENT = "$constraint$";
- /** Reference to Support class */
+ /** Reference to TriggerSupport or WrappedSupport instance */
public static final String SUPPORT_FIELD_NAME = "support$";
/** Property state indicating that property has never been set, loaded, or saved */