summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--src/test/java/com/amazon/carbonado/qe/TestFilteredQueryExecutor.java2
-rw-r--r--src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java259
-rw-r--r--src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java76
-rw-r--r--src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java203
-rw-r--r--src/test/java/com/amazon/carbonado/qe/TestSortedQueryExecutor.java4
-rw-r--r--src/test/java/com/amazon/carbonado/qe/TestUnionQueryExecutor.java2
-rw-r--r--src/test/java/com/amazon/carbonado/repo/toy/ToyRepository.java106
-rw-r--r--src/test/java/com/amazon/carbonado/repo/toy/ToyStorableGenerator.java143
-rw-r--r--src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java242
-rw-r--r--src/test/java/com/amazon/carbonado/repo/toy/ToyTransaction.java51
-rw-r--r--src/test/java/com/amazon/carbonado/repo/toy/package-info.java25
-rw-r--r--src/test/java/com/amazon/carbonado/stored/Order.java68
-rw-r--r--src/test/java/com/amazon/carbonado/stored/OrderItem.java68
-rw-r--r--src/test/java/com/amazon/carbonado/stored/Promotion.java55
-rw-r--r--src/test/java/com/amazon/carbonado/stored/Shipment.java69
-rw-r--r--src/test/java/com/amazon/carbonado/stored/Shipper.java52
-rw-r--r--src/test/java/com/amazon/carbonado/stored/UserAddress.java66
-rw-r--r--src/test/java/com/amazon/carbonado/stored/UserInfo.java57
38 files changed, 2002 insertions, 91 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 */
diff --git a/src/test/java/com/amazon/carbonado/qe/TestFilteredQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestFilteredQueryExecutor.java
index 76ea081..1210bd4 100644
--- a/src/test/java/com/amazon/carbonado/qe/TestFilteredQueryExecutor.java
+++ b/src/test/java/com/amazon/carbonado/qe/TestFilteredQueryExecutor.java
@@ -52,6 +52,6 @@ public class TestFilteredQueryExecutor extends TestQueryExecutor {
assertEquals(0, executor.getOrdering().size());
- compareElements(executor.openCursor(values.with("country_2")), 3, 4);
+ compareElements(executor.fetch(values.with("country_2")), 3, 4);
}
}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java
new file mode 100644
index 0000000..85c78cb
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java
@@ -0,0 +1,259 @@
+/*
+ * 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.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.Repository;
+import com.amazon.carbonado.Storable;
+
+import com.amazon.carbonado.info.StorableIndex;
+
+import com.amazon.carbonado.filter.Filter;
+import com.amazon.carbonado.filter.FilterValues;
+
+import com.amazon.carbonado.repo.toy.ToyRepository;
+
+import com.amazon.carbonado.stored.Address;
+import com.amazon.carbonado.stored.Order;
+import com.amazon.carbonado.stored.Shipment;
+import com.amazon.carbonado.stored.Shipper;
+
+import static com.amazon.carbonado.qe.TestIndexedQueryExecutor.Mock;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestIndexedQueryAnalyzer extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestIndexedQueryAnalyzer.class);
+ }
+
+ static <S extends Storable> StorableIndex<S> makeIndex(Class<S> type, String... props) {
+ return TestOrderingScore.makeIndex(type, props);
+ }
+
+ public TestIndexedQueryAnalyzer(String name) {
+ super(name);
+ }
+
+ // Note: these tests don't perform exhaustive tests to find the best index, as those tests
+ // are performed by TestFilteringScore and TestOrderingScore.
+
+ public void testFullScan() throws Exception {
+ IndexedQueryAnalyzer iqa = new IndexedQueryAnalyzer(Address.class, IxProvider.INSTANCE);
+ Filter<Address> filter = Filter.filterFor(Address.class, "addressZip = ?");
+ filter = filter.bind();
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter);
+
+ assertFalse(result.handlesAnything());
+ assertEquals(filter, result.getCompositeScore().getFilteringScore().getRemainderFilter());
+ assertEquals(makeIndex(Address.class, "addressID"), result.getLocalIndex());
+ assertEquals(null, result.getForeignIndex());
+ assertEquals(null, result.getForeignProperty());
+ }
+
+ public void testIndexScan() throws Exception {
+ IndexedQueryAnalyzer iqa = new IndexedQueryAnalyzer(Address.class, IxProvider.INSTANCE);
+ Filter<Address> filter = Filter.filterFor(Address.class, "addressID = ?");
+ filter = filter.bind();
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter);
+
+ assertTrue(result.handlesAnything());
+ assertEquals(filter, result.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(makeIndex(Address.class, "addressID"), result.getLocalIndex());
+ assertEquals(null, result.getForeignIndex());
+ assertEquals(null, result.getForeignProperty());
+ }
+
+ public void testBasic() throws Exception {
+ IndexedQueryAnalyzer iqa = new IndexedQueryAnalyzer(Shipment.class, IxProvider.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor(Shipment.class, "shipmentID = ?");
+ filter = filter.bind();
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter);
+
+ assertTrue(result.handlesAnything());
+ assertEquals(filter, result.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(makeIndex(Shipment.class, "shipmentID"), result.getLocalIndex());
+ assertEquals(null, result.getForeignIndex());
+ assertEquals(null, result.getForeignProperty());
+
+ filter = Filter.filterFor(Shipment.class, "orderID = ?");
+ filter = filter.bind();
+ result = iqa.analyze(filter);
+
+ assertTrue(result.handlesAnything());
+ assertEquals(filter, result.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(makeIndex(Shipment.class, "orderID"), result.getLocalIndex());
+ assertEquals(null, result.getForeignIndex());
+ assertEquals(null, result.getForeignProperty());
+ }
+
+ public void testSimpleJoin() throws Exception {
+ IndexedQueryAnalyzer iqa = new IndexedQueryAnalyzer(Shipment.class, IxProvider.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor(Shipment.class, "order.orderTotal >= ?");
+ filter = filter.bind();
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter);
+
+ assertTrue(result.handlesAnything());
+ assertTrue(result.getCompositeScore().getFilteringScore().hasRangeStart());
+ assertEquals(null, result.getLocalIndex());
+ assertEquals(makeIndex(Order.class, "orderTotal"), result.getForeignIndex());
+ assertEquals("order", result.getForeignProperty().toString());
+ }
+
+ public void testJoinPriority() throws Exception {
+ // Selects foreign index because filter score is better.
+
+ IndexedQueryAnalyzer iqa = new IndexedQueryAnalyzer(Shipment.class, IxProvider.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class, "shipmentNotes = ? & order.orderTotal >= ?");
+ filter = filter.bind();
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter);
+
+ assertTrue(result.handlesAnything());
+ assertTrue(result.getCompositeScore().getFilteringScore().hasRangeStart());
+ assertEquals(Filter.filterFor(Shipment.class, "shipmentNotes = ?").bind(),
+ result.getCompositeScore().getFilteringScore().getRemainderFilter());
+ assertEquals(null, result.getLocalIndex());
+ assertEquals(makeIndex(Order.class, "orderTotal"), result.getForeignIndex());
+ assertEquals("order", result.getForeignProperty().toString());
+ }
+
+ public void testJoinNonPriority() throws Exception {
+ // Selects local index because filter score is just as good and local
+ // indexes are preferred.
+
+ IndexedQueryAnalyzer iqa = new IndexedQueryAnalyzer(Shipment.class, IxProvider.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class, "orderID >= ? & order.orderTotal >= ?");
+ filter = filter.bind();
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter);
+
+ assertTrue(result.handlesAnything());
+ assertTrue(result.getCompositeScore().getFilteringScore().hasRangeStart());
+ assertEquals(Filter.filterFor(Shipment.class, "order.orderTotal >= ?").bind(),
+ result.getCompositeScore().getFilteringScore().getRemainderFilter());
+ assertEquals(makeIndex(Shipment.class, "orderID"), result.getLocalIndex());
+ assertEquals(null, result.getForeignIndex());
+ assertEquals(null, result.getForeignProperty());
+ }
+
+ public void testChainedJoin() throws Exception {
+ IndexedQueryAnalyzer iqa = new IndexedQueryAnalyzer(Shipment.class, IxProvider.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class, "order.address.addressState = ?");
+ filter = filter.bind();
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter);
+
+ assertTrue(result.handlesAnything());
+ assertEquals(filter, result.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(null, result.getLocalIndex());
+ assertEquals(makeIndex(Address.class, "addressState"), result.getForeignIndex());
+ assertEquals("order.address", result.getForeignProperty().toString());
+ }
+
+ public void testChainedJoinExecutor() throws Exception {
+ Repository repo = new ToyRepository();
+
+ IndexedQueryAnalyzer<Shipment> iqa =
+ new IndexedQueryAnalyzer<Shipment>(Shipment.class, IxProvider.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class, "order.address.addressState = ? & order.address.addressZip = ?");
+ FilterValues<Shipment> values = filter.initialFilterValues();
+ filter = values.getFilter();
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter);
+
+ assertTrue(result.handlesAnything());
+ assertEquals(Filter.filterFor(Shipment.class, "order.address.addressState = ?").bind(),
+ result.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(Filter.filterFor(Shipment.class, "order.address.addressZip = ?").bind(),
+ result.getCompositeScore().getFilteringScore().getRemainderFilter());
+ assertEquals(null, result.getLocalIndex());
+ assertEquals(makeIndex(Address.class, "addressState"), result.getForeignIndex());
+ assertEquals("order.address", result.getForeignProperty().toString());
+
+ Mock ixExec = new Mock(result.getForeignIndex(), result.getCompositeScore());
+
+ QueryExecutor joinExec = new JoinedQueryExecutor
+ (repo, result.getForeignProperty(), ixExec);
+
+ QueryExecutor filteredExec = new FilteredQueryExecutor
+ (joinExec, result.getCompositeScore().getFilteringScore().getRemainderFilter());
+
+ //System.out.println();
+ //filteredExec.printPlan(System.out, 0, null);
+
+ joinExec.fetch(values.with("WA"));
+
+ assertEquals(1, ixExec.mIdentityValues.length);
+ assertEquals("WA", ixExec.mIdentityValues[0]);
+ assertEquals(BoundaryType.OPEN, ixExec.mRangeStartBoundary);
+ assertEquals(null, ixExec.mRangeStartValue);
+ assertEquals(BoundaryType.OPEN, ixExec.mRangeEndBoundary);
+ assertEquals(null, ixExec.mRangeEndValue);
+ assertFalse(ixExec.mReverseRange);
+ assertFalse(ixExec.mReverseOrder);
+ }
+
+ static class IxProvider implements IndexProvider {
+ static final IxProvider INSTANCE = new IxProvider();
+
+ public <S extends Storable> Collection<StorableIndex<S>> indexesFor(Class<S> type) {
+ StorableIndex<S>[] indexes;
+
+ if (Address.class.isAssignableFrom(type)) {
+ indexes = new StorableIndex[] {
+ makeIndex(type, "addressID"),
+ makeIndex(type, "addressState")
+ };
+ } else if (Order.class.isAssignableFrom(type)) {
+ indexes = new StorableIndex[] {
+ makeIndex(type, "orderID"),
+ makeIndex(type, "orderTotal"),
+ makeIndex(type, "addressID")
+ };
+ } else if (Shipment.class.isAssignableFrom(type)) {
+ indexes = new StorableIndex[] {
+ makeIndex(type, "shipmentID"),
+ makeIndex(type, "orderID"),
+ };
+ } else if (Shipper.class.isAssignableFrom(type)) {
+ indexes = new StorableIndex[] {
+ makeIndex(type, "shipperID")
+ };
+ } else {
+ indexes = new StorableIndex[0];
+ }
+
+ return Arrays.asList(indexes);
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java
index a039e63..3f865e6 100644
--- a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java
+++ b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java
@@ -78,7 +78,7 @@ public class TestIndexedQueryExecutor extends TestCase {
Mock<StorableTestBasic> executor = new Mock(index, score);
- executor.openCursor(values.with(100));
+ executor.fetch(values.with(100));
assertEquals(1, executor.mIdentityValues.length);
assertEquals(100, executor.mIdentityValues[0]);
@@ -98,7 +98,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(100).with(5));
+ executor.fetch(values.with(100).with(5));
assertEquals(2, executor.mIdentityValues.length);
assertEquals(100, executor.mIdentityValues[0]);
@@ -119,7 +119,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(200));
+ executor.fetch(values.with(200));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
@@ -142,7 +142,7 @@ public class TestIndexedQueryExecutor extends TestCase {
Mock<StorableTestBasic> executor = new Mock(index, score);
- executor.openCursor(values.with(100));
+ executor.fetch(values.with(100));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
@@ -161,7 +161,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(100));
+ executor.fetch(values.with(100));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.INCLUSIVE, executor.mRangeStartBoundary);
@@ -180,7 +180,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(10).with(30));
+ executor.fetch(values.with(10).with(30));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
@@ -199,7 +199,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(10).with(30));
+ executor.fetch(values.with(10).with(30));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
@@ -218,7 +218,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(10).with(10));
+ executor.fetch(values.with(10).with(10));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
@@ -237,7 +237,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(30).with(10));
+ executor.fetch(values.with(30).with(10));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.INCLUSIVE, executor.mRangeStartBoundary);
@@ -257,7 +257,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(100).with(30));
+ executor.fetch(values.with(100).with(30));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
@@ -278,7 +278,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(100));
+ executor.fetch(values.with(100));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
@@ -298,7 +298,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(100));
+ executor.fetch(values.with(100));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
@@ -318,7 +318,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(100));
+ executor.fetch(values.with(100));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
@@ -341,7 +341,7 @@ public class TestIndexedQueryExecutor extends TestCase {
Mock<StorableTestBasic> executor = new Mock(index, score);
- executor.openCursor(values.with(100));
+ executor.fetch(values.with(100));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
@@ -360,7 +360,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(100));
+ executor.fetch(values.with(100));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
@@ -379,7 +379,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(10).with(30));
+ executor.fetch(values.with(10).with(30));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
@@ -398,7 +398,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(10).with(30));
+ executor.fetch(values.with(10).with(30));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
@@ -417,7 +417,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(10).with(10));
+ executor.fetch(values.with(10).with(10));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
@@ -436,7 +436,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(30).with(10));
+ executor.fetch(values.with(30).with(10));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
@@ -456,7 +456,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(100).with(30));
+ executor.fetch(values.with(100).with(30));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
@@ -477,7 +477,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(100));
+ executor.fetch(values.with(100));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
@@ -497,7 +497,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(100));
+ executor.fetch(values.with(100));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
@@ -517,7 +517,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(100));
+ executor.fetch(values.with(100));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
@@ -543,7 +543,7 @@ public class TestIndexedQueryExecutor extends TestCase {
Mock<StorableTestBasic> executor = new Mock(index, score);
- executor.openCursor(values.with(100).with(200));
+ executor.fetch(values.with(100).with(200));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
@@ -562,7 +562,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(100).with(10));
+ executor.fetch(values.with(100).with(10));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.INCLUSIVE, executor.mRangeStartBoundary);
@@ -582,7 +582,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(10).with(100).with(30));
+ executor.fetch(values.with(10).with(100).with(30));
assertEquals(null, executor.mIdentityValues);
assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
@@ -614,7 +614,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(3).with(56.5).with(200.2));
+ executor.fetch(values.with(3).with(56.5).with(200.2));
assertEquals(3, executor.mIdentityValues[0]);
assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
@@ -630,7 +630,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(3).with(56.5).with(200.2));
+ executor.fetch(values.with(3).with(56.5).with(200.2));
assertEquals(3, executor.mIdentityValues[0]);
assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
@@ -646,7 +646,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(3).with(56.5).with(200.2));
+ executor.fetch(values.with(3).with(56.5).with(200.2));
assertEquals(3, executor.mIdentityValues[0]);
assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
@@ -667,7 +667,7 @@ public class TestIndexedQueryExecutor extends TestCase {
executor = new Mock(index, score);
- executor.openCursor(values.with(3).with(56.5).with("foo"));
+ executor.fetch(values.with(3).with(56.5).with("foo"));
assertEquals(3, executor.mIdentityValues[0]);
assertEquals(56.5, executor.mIdentityValues[1]);
@@ -701,14 +701,14 @@ public class TestIndexedQueryExecutor extends TestCase {
super(index, score);
}
- protected Cursor<S> openCursor(StorableIndex<S> index,
- Object[] identityValues,
- BoundaryType rangeStartBoundary,
- Object rangeStartValue,
- BoundaryType rangeEndBoundary,
- Object rangeEndValue,
- boolean reverseRange,
- boolean reverseOrder)
+ protected Cursor<S> fetch(StorableIndex<S> index,
+ Object[] identityValues,
+ BoundaryType rangeStartBoundary,
+ Object rangeStartValue,
+ BoundaryType rangeEndBoundary,
+ Object rangeEndValue,
+ boolean reverseRange,
+ boolean reverseOrder)
{
mIdentityValues = identityValues;
mRangeStartBoundary = rangeStartBoundary;
diff --git a/src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java
new file mode 100644
index 0000000..8a82758
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java
@@ -0,0 +1,203 @@
+/*
+ * 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.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestSuite;
+
+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.Filter;
+import com.amazon.carbonado.filter.FilterValues;
+
+import com.amazon.carbonado.info.OrderedProperty;
+import com.amazon.carbonado.info.StorableIntrospector;
+import com.amazon.carbonado.info.StorableProperty;
+
+import com.amazon.carbonado.repo.toy.ToyRepository;
+
+import com.amazon.carbonado.stored.UserAddress;
+import com.amazon.carbonado.stored.UserInfo;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestJoinedQueryExecutor extends TestQueryExecutor {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestJoinedQueryExecutor.class);
+ }
+
+ private Repository mRepository;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ mRepository = new ToyRepository();
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testJoin() throws Exception {
+ QueryExecutor<UserAddress> addressExecutor = addressExecutor();
+
+ QueryExecutor<UserInfo> userExecutor = new JoinedQueryExecutor<UserAddress, UserInfo>
+ (mRepository, UserInfo.class, "address", addressExecutor);
+
+ assertEquals("address.state = ?", userExecutor.getFilter().toString());
+ assertEquals("address.country", userExecutor.getOrdering().get(0).toString());
+
+ // Create some addresses
+ Storage<UserAddress> addressStorage = mRepository.storageFor(UserAddress.class);
+ UserAddress addr = addressStorage.prepare();
+ addr.setAddressID(1);
+ addr.setLine1("4567, 123 Street");
+ addr.setCity("Springfield");
+ addr.setState("IL");
+ addr.setCountry("USA");
+ addr.insert();
+
+ addr = addressStorage.prepare();
+ addr.setAddressID(2);
+ addr.setLine1("1111 Apt 1, 1st Ave");
+ addr.setCity("Somewhere");
+ addr.setState("AA");
+ addr.setCountry("USA");
+ addr.setNeighborAddressID(1);
+ addr.insert();
+
+ addr = addressStorage.prepare();
+ addr.setAddressID(3);
+ addr.setLine1("9999");
+ addr.setCity("Chicago");
+ addr.setState("IL");
+ addr.setCountry("USA");
+ addr.insert();
+
+ // Create some users
+ Storage<UserInfo> userStorage = mRepository.storageFor(UserInfo.class);
+ UserInfo user = userStorage.prepare();
+ user.setUserID(1);
+ user.setStateID(1);
+ user.setFirstName("Bob");
+ user.setLastName("Loblaw");
+ user.setAddressID(1);
+ user.insert();
+
+ user = userStorage.prepare();
+ user.setUserID(2);
+ user.setStateID(1);
+ user.setFirstName("Deb");
+ user.setLastName("Loblaw");
+ user.setAddressID(1);
+ user.insert();
+
+ user = userStorage.prepare();
+ user.setUserID(3);
+ user.setStateID(1);
+ user.setFirstName("No");
+ user.setLastName("Body");
+ user.setAddressID(2);
+ user.insert();
+
+ // Now do a basic join, finding everyone in IL.
+
+ FilterValues<UserInfo> values = Filter
+ .filterFor(UserInfo.class, "address.state = ?").initialFilterValues().with("IL");
+
+ Cursor<UserInfo> cursor = userExecutor.fetch(values);
+ assertTrue(cursor.hasNext());
+ assertEquals(1, cursor.next().getUserID());
+ assertEquals(2, cursor.next().getUserID());
+ assertFalse(cursor.hasNext());
+ cursor.close();
+
+ assertEquals(2L, userExecutor.count(values));
+
+ // Now do a multi join, finding everyone with an explicit neighbor in IL.
+
+ userExecutor = new JoinedQueryExecutor<UserAddress, UserInfo>
+ (mRepository, UserInfo.class, "address.neighbor", addressExecutor);
+
+ assertEquals("address.neighbor.state = ?", userExecutor.getFilter().toString());
+ assertEquals("address.neighbor.country", userExecutor.getOrdering().get(0).toString());
+
+ values = Filter
+ .filterFor(UserInfo.class, "address.neighbor.state = ?")
+ .initialFilterValues().with("IL");
+
+ cursor = userExecutor.fetch(values);
+ assertTrue(cursor.hasNext());
+ assertEquals(3, cursor.next().getUserID());
+ assertFalse(cursor.hasNext());
+ cursor.close();
+
+ assertEquals(1L, userExecutor.count(values));
+
+ }
+
+ protected QueryExecutor<UserAddress> addressExecutor() throws Exception {
+ Storage<UserAddress> addressStorage = mRepository.storageFor(UserAddress.class);
+
+ QueryExecutor<UserAddress> addressExecutor =
+ new ScanQueryExecutor<UserAddress>(addressStorage.query());
+
+ addressExecutor = new FilteredQueryExecutor<UserAddress>
+ (addressExecutor, Filter.filterFor(UserAddress.class, "state = ?"));
+
+ StorableProperty<UserAddress> prop = StorableIntrospector
+ .examine(UserAddress.class).getAllProperties().get("country");
+
+ List<OrderedProperty<UserAddress>> orderings =
+ new ArrayList<OrderedProperty<UserAddress>>();
+
+ orderings.add(OrderedProperty.get(prop, null));
+
+ addressExecutor = new ArraySortedQueryExecutor<UserAddress>
+ (addressExecutor, null, orderings);
+
+ return addressExecutor;
+ }
+
+ static class ScanQueryExecutor<S extends Storable> extends FullScanQueryExecutor<S> {
+ private final Query<S> mQuery;
+
+ ScanQueryExecutor(Query<S> query) {
+ super(query.getStorableType());
+ mQuery = query;
+ }
+
+ protected Cursor<S> fetch() throws FetchException {
+ return mQuery.fetch();
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestSortedQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestSortedQueryExecutor.java
index 0972e94..9e9906f 100644
--- a/src/test/java/com/amazon/carbonado/qe/TestSortedQueryExecutor.java
+++ b/src/test/java/com/amazon/carbonado/qe/TestSortedQueryExecutor.java
@@ -58,7 +58,7 @@ public class TestSortedQueryExecutor extends TestQueryExecutor {
assertEquals(ordered, executor.getOrdering());
- compareElements(executor.openCursor(values), 1, 2, 3, 4);
+ compareElements(executor.fetch(values), 1, 2, 3, 4);
}
public void testBasicFinisherSorting() throws Exception {
@@ -79,6 +79,6 @@ public class TestSortedQueryExecutor extends TestQueryExecutor {
assertEquals(handled.get(0), executor.getOrdering().get(0));
assertEquals(finisher.get(0), executor.getOrdering().get(1));
- compareElements(executor.openCursor(values), 1, 2, 3, 4);
+ compareElements(executor.fetch(values), 1, 2, 3, 4);
}
}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestUnionQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestUnionQueryExecutor.java
index 639176c..07716bb 100644
--- a/src/test/java/com/amazon/carbonado/qe/TestUnionQueryExecutor.java
+++ b/src/test/java/com/amazon/carbonado/qe/TestUnionQueryExecutor.java
@@ -70,6 +70,6 @@ public class TestUnionQueryExecutor extends TestQueryExecutor {
assertEquals(primary.getOrdering(), union.getOrdering());
- compareElements(union.openCursor(values), 1, 2, 3, 7, 8);
+ compareElements(union.fetch(values), 1, 2, 3, 7, 8);
}
}
diff --git a/src/test/java/com/amazon/carbonado/repo/toy/ToyRepository.java b/src/test/java/com/amazon/carbonado/repo/toy/ToyRepository.java
new file mode 100644
index 0000000..159f451
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/repo/toy/ToyRepository.java
@@ -0,0 +1,106 @@
+/*
+ * 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.repo.toy;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.amazon.carbonado.IsolationLevel;
+import com.amazon.carbonado.Repository;
+import com.amazon.carbonado.RepositoryException;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.Storage;
+import com.amazon.carbonado.SupportException;
+import com.amazon.carbonado.Transaction;
+
+import com.amazon.carbonado.capability.Capability;
+
+import com.amazon.carbonado.spi.SequenceValueGenerator;
+import com.amazon.carbonado.spi.SequenceValueProducer;
+
+/**
+ *
+ * @author Brian S O'Neill
+ */
+public class ToyRepository implements Repository {
+ private final String mName;
+ private final Map<Class, Storage> mStorages;
+ private final Map<String, SequenceValueProducer> mSequences;
+
+ public ToyRepository() {
+ this("toy");
+ }
+
+ public ToyRepository(String name) {
+ mName = name;
+ mStorages = new HashMap<Class, Storage>();
+ mSequences = new HashMap<String, SequenceValueProducer>();
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public <S extends Storable> Storage<S> storageFor(Class<S> type)
+ throws SupportException, RepositoryException
+ {
+ synchronized (mStorages) {
+ Storage<S> storage = (Storage<S>) mStorages.get(type);
+ if (storage == null) {
+ storage = new ToyStorage<S>(this, type);
+ mStorages.put(type, storage);
+ }
+ return storage;
+ }
+ }
+
+ public Transaction enterTransaction() {
+ return new ToyTransaction();
+ }
+
+ public Transaction enterTransaction(IsolationLevel level) {
+ return enterTransaction();
+ }
+
+ public Transaction enterTopTransaction(IsolationLevel level) {
+ return enterTransaction(level);
+ }
+
+ public IsolationLevel getTransactionIsolationLevel() {
+ return null;
+ }
+
+ public <C extends Capability> C getCapability(Class<C> capabilityType) {
+ return null;
+ }
+
+ public void close() {
+ }
+
+ SequenceValueProducer getSequenceValueProducer(String name) throws RepositoryException {
+ synchronized (mSequences) {
+ SequenceValueProducer producer = mSequences.get(name);
+ if (producer == null) {
+ producer = new SequenceValueGenerator(this, name);
+ mSequences.put(name, producer);
+ }
+ return producer;
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/repo/toy/ToyStorableGenerator.java b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorableGenerator.java
new file mode 100644
index 0000000..a72c7d5
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorableGenerator.java
@@ -0,0 +1,143 @@
+/*
+ * 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.repo.toy;
+
+import java.util.EnumSet;
+import java.util.Map;
+
+import org.cojen.classfile.ClassFile;
+import org.cojen.classfile.CodeBuilder;
+import org.cojen.classfile.MethodInfo;
+import org.cojen.classfile.Modifiers;
+import org.cojen.classfile.TypeDesc;
+
+import org.cojen.util.ClassInjector;
+import org.cojen.util.SoftValuedHashMap;
+
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.SupportException;
+import com.amazon.carbonado.Trigger;
+
+import com.amazon.carbonado.spi.MasterFeature;
+import com.amazon.carbonado.spi.MasterStorableGenerator;
+import com.amazon.carbonado.spi.MasterSupport;
+import com.amazon.carbonado.spi.StorableGenerator;
+import com.amazon.carbonado.spi.TriggerSupport;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class ToyStorableGenerator<S extends Storable> {
+ private static final Map<Class, Class> cCache;
+
+ static {
+ cCache = new SoftValuedHashMap();
+ }
+
+ /**
+ * Generated class has a constructor that accepts a ToyStorage instance.
+ */
+ public static <S extends Storable> Class<? extends S> getGeneratedClass(Class<S> type)
+ throws SupportException
+ {
+ synchronized (cCache) {
+ Class<? extends S> generatedClass = (Class<? extends S>) cCache.get(type);
+ if (generatedClass != null) {
+ return generatedClass;
+ }
+ generatedClass = new ToyStorableGenerator<S>(type).generateAndInjectClass();
+ cCache.put(type, generatedClass);
+ return generatedClass;
+ }
+ }
+
+ private final Class<S> mStorableType;
+
+ private final ClassInjector mClassInjector;
+ private final ClassFile mClassFile;
+
+ private ToyStorableGenerator(Class<S> type) throws SupportException {
+ mStorableType = type;
+
+ EnumSet<MasterFeature> features = EnumSet
+ .of(MasterFeature.VERSIONING, MasterFeature.INSERT_SEQUENCES);
+
+ final Class<? extends S> abstractClass =
+ MasterStorableGenerator.getAbstractClass(mStorableType, features);
+
+ mClassInjector = ClassInjector.create(mStorableType.getName(),
+ abstractClass.getClassLoader());
+
+ mClassFile = new ClassFile(mClassInjector.getClassName(), abstractClass);
+ mClassFile.markSynthetic();
+ mClassFile.setSourceFile(ToyStorableGenerator.class.getName());
+ mClassFile.setTarget("1.5");
+ }
+
+ private Class<? extends S> generateAndInjectClass() {
+ TypeDesc masterSupportType = TypeDesc.forClass(MasterSupport.class);
+ TypeDesc toyStorageType = TypeDesc.forClass(ToyStorage.class);
+
+ // Add constructor that accepts a ToyStorage.
+ {
+ TypeDesc[] params = {toyStorageType};
+ MethodInfo mi = mClassFile.addConstructor(Modifiers.PUBLIC, params);
+ CodeBuilder b = new CodeBuilder(mi);
+ b.loadThis();
+ b.loadLocal(b.getParameter(0));
+ b.invokeSuperConstructor(new TypeDesc[] {masterSupportType});
+ b.returnVoid();
+ }
+
+ // Implement abstract methods which all delegate to ToyStorage instance.
+
+ generateDelegatedMethod
+ (MasterStorableGenerator.DO_TRY_LOAD_MASTER_METHOD_NAME, "doTryLoad");
+ generateDelegatedMethod
+ (MasterStorableGenerator.DO_TRY_INSERT_MASTER_METHOD_NAME, "doTryInsert");
+ generateDelegatedMethod
+ (MasterStorableGenerator.DO_TRY_UPDATE_MASTER_METHOD_NAME, "doTryUpdate");
+ generateDelegatedMethod
+ (MasterStorableGenerator.DO_TRY_DELETE_MASTER_METHOD_NAME, "doTryDelete");
+
+ Class<? extends S> generatedClass = mClassInjector.defineClass(mClassFile);
+
+ return generatedClass;
+ }
+
+ private void generateDelegatedMethod(String masterMethodName, String supportMethodName) {
+ TypeDesc triggerSupportType = TypeDesc.forClass(TriggerSupport.class);
+ TypeDesc toyStorageType = TypeDesc.forClass(ToyStorage.class);
+
+ TypeDesc[] storableParam = {TypeDesc.forClass(Storable.class)};
+
+ MethodInfo mi = mClassFile.addMethod
+ (Modifiers.PROTECTED, masterMethodName, TypeDesc.BOOLEAN, null);
+ CodeBuilder b = new CodeBuilder(mi);
+
+ b.loadThis();
+ b.loadField(StorableGenerator.SUPPORT_FIELD_NAME, triggerSupportType);
+ b.checkCast(toyStorageType);
+ b.loadThis();
+ b.invokeVirtual(toyStorageType, supportMethodName, TypeDesc.BOOLEAN, storableParam);
+ b.returnValue(TypeDesc.BOOLEAN);
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java
new file mode 100644
index 0000000..dd29e4b
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java
@@ -0,0 +1,242 @@
+/*
+ * 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.repo.toy;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import com.amazon.carbonado.FetchException;
+import com.amazon.carbonado.IsolationLevel;
+import com.amazon.carbonado.PersistException;
+import com.amazon.carbonado.Query;
+import com.amazon.carbonado.Repository;
+import com.amazon.carbonado.RepositoryException;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.Storage;
+import com.amazon.carbonado.SupportException;
+import com.amazon.carbonado.Transaction;
+import com.amazon.carbonado.Trigger;
+
+import com.amazon.carbonado.spi.MasterSupport;
+import com.amazon.carbonado.spi.SequenceValueProducer;
+
+import com.amazon.carbonado.util.QuickConstructorGenerator;
+
+import com.amazon.carbonado.filter.Filter;
+import com.amazon.carbonado.filter.FilterValues;
+
+import com.amazon.carbonado.info.OrderedProperty;
+import com.amazon.carbonado.info.StorableIntrospector;
+
+import com.amazon.carbonado.qe.FilteredQueryExecutor;
+import com.amazon.carbonado.qe.IterableQueryExecutor;
+import com.amazon.carbonado.qe.QueryExecutor;
+import com.amazon.carbonado.qe.SortedQueryExecutor;
+import com.amazon.carbonado.qe.StandardQuery;
+
+/**
+ *
+ * @author Brian S O'Neill
+ */
+public class ToyStorage<S extends Storable> implements Storage<S>, MasterSupport<S> {
+ final ToyRepository mRepo;
+ final Class<S> mType;
+
+ final InstanceFactory mInstanceFactory;
+
+ final Collection<S> mData;
+ final Lock mDataLock;
+
+ public ToyStorage(ToyRepository repo, Class<S> type) throws SupportException {
+ StorableIntrospector.examine(type);
+ mRepo = repo;
+ mType = type;
+
+ Class<? extends S> generatedStorableClass = ToyStorableGenerator.getGeneratedClass(type);
+ mInstanceFactory = QuickConstructorGenerator
+ .getInstance(generatedStorableClass, InstanceFactory.class);
+
+ mData = new LinkedList<S>();
+ mDataLock = new ReentrantLock();
+ }
+
+ public Class<S> getStorableType() {
+ return mType;
+ }
+
+ public S prepare() {
+ return (S) mInstanceFactory.instantiate(this);
+ }
+
+ public Query<S> query() throws FetchException {
+ return new ToyQuery(null);
+ }
+
+ public Query<S> query(String filter) throws FetchException {
+ return query(Filter.filterFor(mType, filter));
+ }
+
+ public Query<S> query(Filter<S> filter) throws FetchException {
+ return new ToyQuery(filter.initialFilterValues());
+ }
+
+ public boolean addTrigger(Trigger<? super S> trigger) {
+ return false;
+ }
+
+ public boolean removeTrigger(Trigger<? super S> trigger) {
+ return false;
+ }
+
+ public boolean doTryLoad(S storable) {
+ mDataLock.lock();
+ try {
+ for (S existing : mData) {
+ if (existing.equalPrimaryKeys(storable)) {
+ storable.markAllPropertiesDirty();
+ existing.copyAllProperties(storable);
+ storable.markAllPropertiesClean();
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ mDataLock.unlock();
+ }
+ }
+
+ public boolean doTryInsert(S storable) {
+ mDataLock.lock();
+ try {
+ for (S existing : mData) {
+ if (existing.equalPrimaryKeys(storable)) {
+ return false;
+ }
+ }
+ storable.markAllPropertiesClean();
+ mData.add((S) storable.copy());
+ return true;
+ } finally {
+ mDataLock.unlock();
+ }
+ }
+
+ public boolean doTryUpdate(S storable) {
+ mDataLock.lock();
+ try {
+ for (S existing : mData) {
+ if (existing.equalPrimaryKeys(storable)) {
+ existing.markAllPropertiesDirty();
+ storable.copyAllProperties(existing);
+ existing.markAllPropertiesClean();
+ existing.copyAllProperties(storable);
+ storable.markAllPropertiesClean();
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ mDataLock.unlock();
+ }
+ }
+
+ public boolean doTryDelete(S storable) {
+ mDataLock.lock();
+ try {
+ Iterator<S> it = mData.iterator();
+ while (it.hasNext()) {
+ S existing = it.next();
+ if (existing.equalPrimaryKeys(storable)) {
+ it.remove();
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ mDataLock.unlock();
+ }
+ }
+
+ public Repository getRootRepository() {
+ return mRepo;
+ }
+
+ public boolean isPropertySupported(String propertyName) {
+ return StorableIntrospector.examine(mType)
+ .getAllProperties().containsKey(propertyName);
+ }
+
+ public Trigger<? super S> getInsertTrigger() {
+ return null;
+ }
+
+ public Trigger<? super S> getUpdateTrigger() {
+ return null;
+ }
+
+ public Trigger<? super S> getDeleteTrigger() {
+ return null;
+ }
+
+ public SequenceValueProducer getSequenceValueProducer(String name) throws PersistException {
+ try {
+ return mRepo.getSequenceValueProducer(name);
+ } catch (RepositoryException e) {
+ throw e.toPersistException();
+ }
+ }
+
+ public static interface InstanceFactory {
+ Storable instantiate(ToyStorage storage);
+ }
+
+ private class ToyQuery extends StandardQuery<S> {
+ ToyQuery(FilterValues<S> values, String... orderings) {
+ super(values, orderings);
+ }
+
+ protected Storage<S> getStorage() {
+ return ToyStorage.this;
+ }
+
+ protected Transaction enterTransactionForDelete(IsolationLevel level) {
+ return mRepo.enterTransaction(level);
+ }
+
+ protected QueryExecutor<S> getExecutor(FilterValues<S> values, String... orderings) {
+ QueryExecutor<S> executor = new IterableQueryExecutor<S>(mType, mData, mDataLock);
+
+ if (values != null) {
+ executor = new FilteredQueryExecutor<S>(executor, values.getFilter());
+ }
+
+ // FIXME: sorting
+
+ return executor;
+ }
+
+ protected StandardQuery<S> newInstance(FilterValues<S> values, String... orderings) {
+ return new ToyQuery(values, orderings);
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/repo/toy/ToyTransaction.java b/src/test/java/com/amazon/carbonado/repo/toy/ToyTransaction.java
new file mode 100644
index 0000000..c16d1f1
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/repo/toy/ToyTransaction.java
@@ -0,0 +1,51 @@
+/*
+ * 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.repo.toy;
+
+import java.util.concurrent.TimeUnit;
+
+import com.amazon.carbonado.IsolationLevel;
+import com.amazon.carbonado.PersistException;
+import com.amazon.carbonado.Transaction;
+
+/**
+ *
+ * @author Brian S O'Neill
+ */
+public class ToyTransaction implements Transaction {
+ public void commit() throws PersistException {
+ }
+
+ public void exit() throws PersistException {
+ }
+
+ public void setForUpdate(boolean forUpdate) {
+ }
+
+ public boolean isForUpdate() {
+ return false;
+ }
+
+ public void setDesiredLockTimeout(int timeout, TimeUnit unit) {
+ }
+
+ public IsolationLevel getIsolationLevel() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/repo/toy/package-info.java b/src/test/java/com/amazon/carbonado/repo/toy/package-info.java
new file mode 100644
index 0000000..769ea8b
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/repo/toy/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+/**
+ * Standalone toy repository implementation. This repository is only suitable
+ * for running tests that don't require anything sophisticated. It doesn't
+ * support transactions, nothing is actually persisted, and all queries do full
+ * scans. The repository is thread-safe, however.
+ */
+package com.amazon.carbonado.repo.toy;
diff --git a/src/test/java/com/amazon/carbonado/stored/Order.java b/src/test/java/com/amazon/carbonado/stored/Order.java
new file mode 100644
index 0000000..91804d9
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/Order.java
@@ -0,0 +1,68 @@
+/*
+ * 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.stored;
+
+import com.amazon.carbonado.Alias;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.FetchException;
+import com.amazon.carbonado.Join;
+import com.amazon.carbonado.Nullable;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Query;
+import com.amazon.carbonado.Sequence;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Alias("TEST_ORDER")
+@PrimaryKey("orderID")
+public interface Order extends Storable<Order> {
+ @Sequence("TEST_ORDER_ID_SEQ")
+ long getOrderID();
+ void setOrderID(long id);
+
+ String getOrderNumber();
+ void setOrderNumber(String value);
+
+ int getOrderTotal();
+ void setOrderTotal(int value);
+
+ @Nullable
+ String getOrderComments();
+ void setOrderComments(String value);
+
+ long getAddressID();
+ void setAddressID(long value);
+
+ @Join
+ @Nullable
+ Address getAddress() throws FetchException;
+ void setAddress(Address value);
+
+ @Join
+ Query<OrderItem> getOrderItems() throws FetchException;
+
+ @Join
+ Query<Shipment> getShipments() throws FetchException;
+
+ @Join
+ Query<Promotion> getPromotions() throws FetchException;
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/OrderItem.java b/src/test/java/com/amazon/carbonado/stored/OrderItem.java
new file mode 100644
index 0000000..dba2477
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/OrderItem.java
@@ -0,0 +1,68 @@
+/*
+ * 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.stored;
+
+import com.amazon.carbonado.Alias;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.FetchException;
+import com.amazon.carbonado.Index;
+import com.amazon.carbonado.Indexes;
+import com.amazon.carbonado.Join;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Sequence;
+import com.amazon.carbonado.constraint.IntegerConstraint;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Alias("TEST_ORDER_ITEM")
+@Indexes({@Index("+orderID"), @Index("+shipmentID")})
+@PrimaryKey("orderItemID")
+public interface OrderItem extends Storable<OrderItem> {
+ @Sequence("TEST_ORDER_ITEM_ID_SEQ")
+ long getOrderItemID();
+ void setOrderItemID(long id);
+
+ long getOrderID();
+ void setOrderID(long value);
+
+ @Join
+ Order getOrder() throws FetchException;
+ void setOrder(Order value);
+
+ String getItemDescription();
+ void setItemDescription(String value);
+
+ int getItemQuantity();
+ @IntegerConstraint(min=1, max=100)
+ void setItemQuantity(int value);
+
+ int getItemPrice();
+ void setItemPrice(int value);
+
+ long getShipmentID();
+ void setShipmentID(long value);
+
+ @Join
+ Shipment getShipment() throws FetchException;
+ void setShipment(Shipment value);
+}
+
diff --git a/src/test/java/com/amazon/carbonado/stored/Promotion.java b/src/test/java/com/amazon/carbonado/stored/Promotion.java
new file mode 100644
index 0000000..18f4a2e
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/Promotion.java
@@ -0,0 +1,55 @@
+/*
+ * 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.stored;
+
+import com.amazon.carbonado.Alias;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.FetchException;
+import com.amazon.carbonado.Join;
+import com.amazon.carbonado.PrimaryKey;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Alias("TEST_PROMOTION")
+@PrimaryKey({"orderID", "promotionID"})
+public interface Promotion extends Storable<Promotion> {
+ long getOrderID();
+ void setOrderID(long value);
+
+ String getPromotionID();
+ void setPromotionID(String value);
+
+ @Join
+ Order getOrder() throws FetchException;
+ void setOrder(Order value);
+
+ @Join
+ Promotion getPromotion() throws FetchException;
+ void setPromotion(Promotion value);
+
+ String getPromotionTitle();
+ void setPromotionTitle(String value);
+
+ String getPromotionDetails();
+ void setPromotionDetails(String value);
+}
+
diff --git a/src/test/java/com/amazon/carbonado/stored/Shipment.java b/src/test/java/com/amazon/carbonado/stored/Shipment.java
new file mode 100644
index 0000000..670806d
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/Shipment.java
@@ -0,0 +1,69 @@
+/*
+ * 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.stored;
+
+import org.joda.time.DateTime;
+
+import com.amazon.carbonado.Alias;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.FetchException;
+import com.amazon.carbonado.Index;
+import com.amazon.carbonado.Indexes;
+import com.amazon.carbonado.Join;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Query;
+import com.amazon.carbonado.Sequence;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Alias("TEST_SHIPMENT")
+@Indexes(@Index("+orderID"))
+@PrimaryKey("shipmentID")
+public interface Shipment extends Storable<Shipment> {
+ @Sequence("TEST_SHIPMENT_ID_SEQ")
+ long getShipmentID();
+ void setShipmentID(long id);
+
+ String getShipmentNotes();
+ void setShipmentNotes(String value);
+
+ DateTime getShipmentDate();
+ void setShipmentDate(DateTime value);
+
+ long getOrderID();
+ void setOrderID(long value);
+
+ @Join
+ Order getOrder() throws FetchException;
+ void setOrder(Order value);
+
+ long getShipperID();
+ void setShipperID(long value);
+
+ @Join
+ Shipper getShipper() throws FetchException;
+ void setShipper(Shipper value);
+
+ @Join
+ Query<OrderItem> getOrderItems() throws FetchException;
+}
+
diff --git a/src/test/java/com/amazon/carbonado/stored/Shipper.java b/src/test/java/com/amazon/carbonado/stored/Shipper.java
new file mode 100644
index 0000000..41e60f4
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/Shipper.java
@@ -0,0 +1,52 @@
+/*
+ * 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.stored;
+
+import com.amazon.carbonado.Alias;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.FetchException;
+import com.amazon.carbonado.Join;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Sequence;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Alias("TEST_SHIPPER")
+@PrimaryKey("shipperID")
+public interface Shipper extends Storable<Shipper> {
+ @Sequence("TEST_SHIPPER_ID_SEQ")
+ long getShipperID();
+ void setShipperID(long id);
+
+ String getShipperName();
+ void setShipperName(String value);
+
+ String getShipperDetails();
+ void setShipperDetails(String value);
+
+ long getAddressID();
+ void setAddressID(long value);
+
+ @Join
+ Address getAddress() throws FetchException;
+ void setAddress(Address value);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/UserAddress.java b/src/test/java/com/amazon/carbonado/stored/UserAddress.java
new file mode 100644
index 0000000..466f6eb
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/UserAddress.java
@@ -0,0 +1,66 @@
+/*
+ * 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.stored;
+
+import com.amazon.carbonado.*;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill (boneill)
+ */
+@Indexes({
+ @Index({"country", "state", "city"}),
+ @Index({"state", "city"}),
+ @Index("city")
+})
+@PrimaryKey("addressID")
+public abstract class UserAddress implements Storable<UserAddress> {
+ public abstract int getAddressID();
+ public abstract void setAddressID(int id);
+
+ public abstract String getLine1();
+ public abstract void setLine1(String value);
+
+ @Nullable
+ public abstract String getLine2();
+ public abstract void setLine2(String value);
+
+ public abstract String getCity();
+ public abstract void setCity(String value);
+
+ public abstract String getState();
+ public abstract void setState(String value);
+
+ public abstract String getCountry();
+ public abstract void setCountry(String value);
+
+ @Nullable
+ public abstract String getPostalCode();
+ public abstract void setPostalCode(String value);
+
+ @Nullable
+ public abstract Integer getNeighborAddressID();
+ public abstract void setNeighborAddressID(Integer id);
+
+ @Nullable
+ @Join(internal="neighborAddressID", external="addressID")
+ public abstract UserAddress getNeighbor() throws FetchException;
+ public abstract void setNeighbor(UserAddress address) throws FetchException;
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/UserInfo.java b/src/test/java/com/amazon/carbonado/stored/UserInfo.java
new file mode 100644
index 0000000..e0bffa4
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/UserInfo.java
@@ -0,0 +1,57 @@
+/*
+ * 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.stored;
+
+import com.amazon.carbonado.*;
+import com.amazon.carbonado.constraint.*;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill (boneill)
+ */
+@Indexes({
+ @Index("firstName"),
+ @Index("lastName"),
+ @Index("addressID")
+})
+@PrimaryKey("userID")
+public abstract class UserInfo implements Storable<UserInfo> {
+ public abstract int getUserID();
+ public abstract void setUserID(int id);
+
+ public abstract int getStateID();
+ @IntegerConstraint(allowed={1, 2, 3})
+ public abstract void setStateID(int state);
+
+ public abstract String getFirstName();
+ @LengthConstraint(min=1, max=50)
+ public abstract void setFirstName(String value);
+
+ public abstract String getLastName();
+ @LengthConstraint(min=1, max=50)
+ public abstract void setLastName(String value);
+
+ public abstract int getAddressID();
+ public abstract void setAddressID(int id);
+
+ @Join
+ public abstract UserAddress getAddress() throws FetchException;
+ public abstract void setAddress(UserAddress address);
+}