From 1d23e874281ba111d5cf37c7aef76d45d25a5170 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Sun, 21 Oct 2007 00:46:26 +0000 Subject: Allow open exists filter to operate on many-to-one join. --- .../carbonado/cursor/FilteredCursorGenerator.java | 15 +++-- .../java/com/amazon/carbonado/filter/Binder.java | 2 +- .../com/amazon/carbonado/filter/ExistsFilter.java | 75 +++++++++++++++------- .../java/com/amazon/carbonado/filter/Filter.java | 37 ++++++----- .../com/amazon/carbonado/filter/FilterParser.java | 22 ++----- .../amazon/carbonado/filter/PropertyFilter.java | 2 +- 6 files changed, 86 insertions(+), 67 deletions(-) (limited to 'src/main/java/com/amazon/carbonado') diff --git a/src/main/java/com/amazon/carbonado/cursor/FilteredCursorGenerator.java b/src/main/java/com/amazon/carbonado/cursor/FilteredCursorGenerator.java index 745ad7a..04ea2e8 100644 --- a/src/main/java/com/amazon/carbonado/cursor/FilteredCursorGenerator.java +++ b/src/main/java/com/amazon/carbonado/cursor/FilteredCursorGenerator.java @@ -349,6 +349,17 @@ class FilteredCursorGenerator { } public Object visit(ExistsFilter filter, Object param) { + // Load join property value to stack. + CodeBuilder b = mIsAllowedBuilder; + loadChainedProperty(b, filter.getChainedProperty()); + + if (!filter.getChainedProperty().getLastProperty().isQuery()) { + // Checking for existence or non-existence of many-to-one join. + // Implement by comparing against null. + getScope().successIfNullElseFail(b, filter.isNotExists()); + return null; + } + // Recursively gather all the properties to be passed to sub-filter. final List subPropFilters = new ArrayList(); @@ -364,10 +375,6 @@ class FilteredCursorGenerator { } }, null); - // Load join property value to stack. It is expected to be a Query. - CodeBuilder b = mIsAllowedBuilder; - loadChainedProperty(b, filter.getChainedProperty()); - final TypeDesc queryType = TypeDesc.forClass(Query.class); // Refine Query filter, if sub-filter isn't open. diff --git a/src/main/java/com/amazon/carbonado/filter/Binder.java b/src/main/java/com/amazon/carbonado/filter/Binder.java index 7b074c2..91a03e5 100644 --- a/src/main/java/com/amazon/carbonado/filter/Binder.java +++ b/src/main/java/com/amazon/carbonado/filter/Binder.java @@ -108,7 +108,7 @@ class Binder extends Visitor, Object> { // This should not happen. throw new IllegalStateException(nj.toString()); } - return ExistsFilter.getCanonical + return ExistsFilter.build (filter.getChainedProperty(), nj.getNotJoinedFilter(), filter.isNotExists()); } } diff --git a/src/main/java/com/amazon/carbonado/filter/ExistsFilter.java b/src/main/java/com/amazon/carbonado/filter/ExistsFilter.java index b75de28..0e5abf2 100644 --- a/src/main/java/com/amazon/carbonado/filter/ExistsFilter.java +++ b/src/main/java/com/amazon/carbonado/filter/ExistsFilter.java @@ -28,7 +28,7 @@ import com.amazon.carbonado.info.StorableProperty; /** * Filter tree node that performs an existence or non-existence test against a - * one-to-many join. + * join property. * * @author Brian S O'Neill * @since 1.2 @@ -38,32 +38,18 @@ public class ExistsFilter extends Filter { * Returns a canonical instance, creating a new one if there isn't one * already in the cache. */ - @SuppressWarnings("unchecked") - static ExistsFilter getCanonical(ChainedProperty property, - Filter subFilter, - boolean not) + static Filter build(ChainedProperty property, + Filter subFilter, + boolean not) { - return (ExistsFilter) cCanonical.put(new ExistsFilter(property, subFilter, not)); - } - - private final ChainedProperty mProperty; - private final Filter mSubFilter; - private final boolean mNot; - - private transient volatile Filter mJoinedSubFilter; - private transient volatile boolean mNoParameters; - - ExistsFilter(ChainedProperty property, Filter subFilter, boolean not) { - super(property == null ? null : property.getPrimeProperty().getEnclosingType()); + if (property == null) { + throw new IllegalArgumentException(); + } StorableProperty joinProperty = property.getLastProperty(); - if (!joinProperty.isQuery()) { - throw new IllegalArgumentException("Not a one-to-many join property: " + property); - } + if (subFilter == null) { subFilter = Filter.getOpenFilter(joinProperty.getJoinedType()); - } else if (subFilter.isClosed()) { - throw new IllegalArgumentException("Exists sub-filter cannot be closed: " + subFilter); } else if (joinProperty.getJoinedType() != subFilter.getStorableType()) { throw new IllegalArgumentException ("Filter not compatible with join property type: " + @@ -71,20 +57,61 @@ public class ExistsFilter extends Filter { ", but filter is for a " + subFilter.getStorableType().getName()); } + if (subFilter.isClosed()) { + // Exists filter reduces to a closed (or open) filter. + Filter f = Filter.getClosedFilter(property.getPrimeProperty().getEnclosingType()); + return not ? f.not() : f; + } else if (joinProperty.isQuery() || subFilter.isOpen()) { + return getCanonical(property, subFilter, not); + } else { + // Convert to normal join filter. + return subFilter.asJoinedFrom(property); + } + } + + /** + * Returns a canonical instance, creating a new one if there isn't one + * already in the cache. + */ + @SuppressWarnings("unchecked") + private static ExistsFilter getCanonical(ChainedProperty property, + Filter subFilter, + boolean not) + { + return (ExistsFilter) cCanonical.put(new ExistsFilter(property, subFilter, not)); + } + + private final ChainedProperty mProperty; + private final Filter mSubFilter; + private final boolean mNot; + + private transient volatile Filter mJoinedSubFilter; + private transient volatile boolean mNoParameters; + + private ExistsFilter(ChainedProperty property, Filter subFilter, boolean not) { + super(property.getPrimeProperty().getEnclosingType()); mProperty = property; mSubFilter = subFilter; mNot = not; } /** - * @return chained property whose last property is a one-to-many join + * Returns the join property that is being checked for existence or + * non-existence. The last property in the chain is a one-to-many or + * many-to-one join, but it is a many-to-one join only if the sub-filter is + * also open. + * + * @return chained property whose last property is a join */ public ChainedProperty getChainedProperty() { return mProperty; } /** - * @return filter which is applied to last property of chain, which might be open + * Returns the filter applied to the join, which might be open. For a + * many-to-one join, the sub-filter is always open. + * + * @return filter which is applied to last property of chain */ public Filter getSubFilter() { return mSubFilter; diff --git a/src/main/java/com/amazon/carbonado/filter/Filter.java b/src/main/java/com/amazon/carbonado/filter/Filter.java index 11f36d9..4f32179 100644 --- a/src/main/java/com/amazon/carbonado/filter/Filter.java +++ b/src/main/java/com/amazon/carbonado/filter/Filter.java @@ -282,34 +282,34 @@ public abstract class Filter implements Appender { /** * Returns a combined filter instance that accepts records which are only - * accepted by this filter and the "exists" test applied to a one-to-many join. + * accepted by this filter and the "exists" test applied to a join. * - * @param propertyName one-to-many join property name, which may be a chained property - * @param subFilter sub-filter to apply to one-to-many join, which may be - * null to test for any existing + * @param propertyName join property name, which may be a chained property + * @param subFilter sub-filter to apply to join, which may be null to test + * for any existing * @return canonical Filter instance * @throws IllegalArgumentException if property is not found * @since 1.2 */ public final Filter andExists(String propertyName, Filter subFilter) { ChainedProperty prop = new FilterParser(mType, propertyName).parseChainedProperty(); - return and(ExistsFilter.getCanonical(prop, subFilter, false)); + return and(ExistsFilter.build(prop, subFilter, false)); } /** * Returns a combined filter instance that accepts records which are only - * accepted by this filter and the "not exists" test applied to a one-to-many join. + * accepted by this filter and the "not exists" test applied to a join. * - * @param propertyName one-to-many join property name, which may be a chained property - * @param subFilter sub-filter to apply to one-to-many join, which may be - * null to test for any not existing + * @param propertyName join property name, which may be a chained property + * @param subFilter sub-filter to apply to join, which may be null to test + * for any not existing * @return canonical Filter instance * @throws IllegalArgumentException if property is not found * @since 1.2 */ public final Filter andNotExists(String propertyName, Filter subFilter) { ChainedProperty prop = new FilterParser(mType, propertyName).parseChainedProperty(); - return and(ExistsFilter.getCanonical(prop, subFilter, true)); + return and(ExistsFilter.build(prop, subFilter, true)); } /** @@ -372,36 +372,35 @@ public abstract class Filter implements Appender { /** * Returns a combined filter instance that accepts records which are - * accepted either by this filter or the "exists" test applied to a - * one-to-many join. + * accepted either by this filter or the "exists" test applied to a join. * * @param propertyName one-to-many join property name, which may be a chained property - * @param subFilter sub-filter to apply to one-to-many join, which may be - * null to test for any existing + * @param subFilter sub-filter to apply to join, which may be null to test + * for any existing * @return canonical Filter instance * @throws IllegalArgumentException if property is not found * @since 1.2 */ public final Filter orExists(String propertyName, Filter subFilter) { ChainedProperty prop = new FilterParser(mType, propertyName).parseChainedProperty(); - return or(ExistsFilter.getCanonical(prop, subFilter, false)); + return or(ExistsFilter.build(prop, subFilter, false)); } /** * Returns a combined filter instance that accepts records which are * accepted either by this filter or the "not exists" test applied to a - * one-to-many join. + * join. * * @param propertyName one-to-many join property name, which may be a chained property - * @param subFilter sub-filter to apply to one-to-many join, which may be - * null to test for any not existing + * @param subFilter sub-filter to apply to join, which may be null to test + * for any not existing * @return canonical Filter instance * @throws IllegalArgumentException if property is not found * @since 1.2 */ public final Filter orNotExists(String propertyName, Filter subFilter) { ChainedProperty prop = new FilterParser(mType, propertyName).parseChainedProperty(); - return or(ExistsFilter.getCanonical(prop, subFilter, true)); + return or(ExistsFilter.build(prop, subFilter, true)); } /** diff --git a/src/main/java/com/amazon/carbonado/filter/FilterParser.java b/src/main/java/com/amazon/carbonado/filter/FilterParser.java index bb7dac8..5d3762e 100644 --- a/src/main/java/com/amazon/carbonado/filter/FilterParser.java +++ b/src/main/java/com/amazon/carbonado/filter/FilterParser.java @@ -132,27 +132,13 @@ class FilterParser { return parsePropertyFilter(chained); } - boolean isExistsFilter = chained.getLastProperty().isQuery(); - - Filter chainedFilter; + Filter subFilter; c = nextCharIgnoreWhitespace(); if (c == ')') { - if (isExistsFilter) { - chainedFilter = ExistsFilter.getCanonical(chained, null, false); - } else { - // FIXME: support exists filter for this case - mPos--; - throw error("Property \"" + chained + - "\" is a many-to-one join and requires property filters"); - } + subFilter = null; } else { mPos--; - Filter cf = parseChainedFilter(chained); - if (isExistsFilter) { - chainedFilter = ExistsFilter.getCanonical(chained, cf, false); - } else { - chainedFilter = cf.asJoinedFrom(chained); - } + subFilter = parseChainedFilter(chained); c = nextCharIgnoreWhitespace(); if (c != ')') { mPos--; @@ -160,7 +146,7 @@ class FilterParser { } } - return chainedFilter; + return ExistsFilter.build(chained, subFilter, false); } } diff --git a/src/main/java/com/amazon/carbonado/filter/PropertyFilter.java b/src/main/java/com/amazon/carbonado/filter/PropertyFilter.java index 97d508a..c4a585d 100644 --- a/src/main/java/com/amazon/carbonado/filter/PropertyFilter.java +++ b/src/main/java/com/amazon/carbonado/filter/PropertyFilter.java @@ -97,7 +97,7 @@ public class PropertyFilter extends Filter { */ private PropertyFilter(ChainedProperty property, RelOp op, int bindID, Object constant) { super(property == null ? null : property.getPrimeProperty().getEnclosingType()); - if (op == null) { + if (property == null || op == null) { throw new IllegalArgumentException(); } mProperty = property; -- cgit v1.2.3