diff options
| author | Brian S. O'Neill <bronee@gmail.com> | 2006-09-03 06:14:41 +0000 | 
|---|---|---|
| committer | Brian S. O'Neill <bronee@gmail.com> | 2006-09-03 06:14:41 +0000 | 
| commit | 39fce59a840b723eb013bc79285687986592b2da (patch) | |
| tree | 6ae5e1602fca5bedfe63643286588a1d35575ccc /src | |
| parent | 967e17697f343849a4c0e420c813d382b11eda44 (diff) | |
More work on query engine.
Diffstat (limited to 'src')
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);
 +}
  | 
