cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ble...@apache.org
Subject [2/2] cassandra git commit: Merge branch cassandra-3.11 into trunk
Date Thu, 23 Feb 2017 13:38:11 GMT
Merge branch cassandra-3.11 into trunk


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/cd29d44d
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/cd29d44d
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/cd29d44d

Branch: refs/heads/trunk
Commit: cd29d44dfeb2a9a4a4bf2b63bedb71503bda8760
Parents: 6e17d65 6487876
Author: Benjamin Lerer <b.lerer@gmail.com>
Authored: Thu Feb 23 14:26:29 2017 +0100
Committer: Benjamin Lerer <b.lerer@gmail.com>
Committed: Thu Feb 23 14:36:36 2017 +0100

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../org/apache/cassandra/cql3/Operator.java     |   9 +
 .../cassandra/cql3/SingleColumnRelation.java    |  10 +-
 .../cql3/conditions/ColumnCondition.java        |  14 ++
 .../cql3/statements/CreateIndexStatement.java   |  11 +
 .../cassandra/db/marshal/DurationType.java      |   3 +-
 .../validation/entities/SecondaryIndexTest.java |  26 ++
 .../cql3/validation/operations/CreateTest.java  |   4 +-
 .../operations/InsertUpdateIfConditionTest.java | 250 +++++++++++++++++++
 .../cql3/validation/operations/SelectTest.java  | 165 ++++++++++++
 10 files changed, 488 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/cd29d44d/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 28ca6d9,233898f..0641011
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,41 -1,6 +1,42 @@@
 +4.0
 + * Adds the ability to use uncompressed chunks in compressed files (CASSANDRA-10520)
 + * Don't flush sstables when streaming for incremental repair (CASSANDRA-13226)
 + * Remove unused method (CASSANDRA-13227)
 + * Fix minor bugs related to #9143 (CASSANDRA-13217)
 + * Output warning if user increases RF (CASSANDRA-13079)
 + * Remove pre-3.0 streaming compatibility code for 4.0 (CASSANDRA-13081)
 + * Add support for + and - operations on dates (CASSANDRA-11936)
 + * Fix consistency of incrementally repaired data (CASSANDRA-9143)
 + * Increase commitlog version (CASSANDRA-13161)
 + * Make TableMetadata immutable, optimize Schema (CASSANDRA-9425)
 + * Refactor ColumnCondition (CASSANDRA-12981)
 + * Parallelize streaming of different keyspaces (CASSANDRA-4663)
 + * Improved compactions metrics (CASSANDRA-13015)
 + * Speed-up start-up sequence by avoiding un-needed flushes (CASSANDRA-13031)
 + * Use Caffeine (W-TinyLFU) for on-heap caches (CASSANDRA-10855)
 + * Thrift removal (CASSANDRA-11115)
 + * Remove pre-3.0 compatibility code for 4.0 (CASSANDRA-12716)
 + * Add column definition kind to dropped columns in schema (CASSANDRA-12705)
 + * Add (automate) Nodetool Documentation (CASSANDRA-12672)
 + * Update bundled cqlsh python driver to 3.7.0 (CASSANDRA-12736)
 + * Reject invalid replication settings when creating or altering a keyspace (CASSANDRA-12681)
 + * Clean up the SSTableReader#getScanner API wrt removal of RateLimiter (CASSANDRA-12422)
 + * Use new token allocation for non bootstrap case as well (CASSANDRA-13080)
 + * Avoid byte-array copy when key cache is disabled (CASSANDRA-13084)
 + * Require forceful decommission if number of nodes is less than replication factor (CASSANDRA-12510)
 + * Allow IN restrictions on column families with collections (CASSANDRA-12654)
 + * Log message size in trace message in OutboundTcpConnection (CASSANDRA-13028)
 + * Add timeUnit Days for cassandra-stress (CASSANDRA-13029)
 + * Add mutation size and batch metrics (CASSANDRA-12649)
 + * Add method to get size of endpoints to TokenMetadata (CASSANDRA-12999)
 + * Expose time spent waiting in thread pool queue (CASSANDRA-8398)
 + * Conditionally update index built status to avoid unnecessary flushes (CASSANDRA-12969)
 + * cqlsh auto completion: refactor definition of compaction strategy options (CASSANDRA-12946)
 + * Add support for arithmetic operators (CASSANDRA-11935)
 +
 +
  3.11.0
+  * Fix equality comparisons of columns using the duration type (CASSANDRA-13174)
 - * Obfuscate password in stress-graphs (CASSANDRA-12233)
   * Move to FastThreadLocalThread and FastThreadLocal (CASSANDRA-13034)
   * nodetool stopdaemon errors out (CASSANDRA-13030)
   * Tables in system_distributed should not use gcgs of 0 (CASSANDRA-12954)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cd29d44d/src/java/org/apache/cassandra/cql3/Operator.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cd29d44d/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/SingleColumnRelation.java
index 412eb16,ae07f56..bbeb560
--- a/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java
+++ b/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java
@@@ -190,15 -191,22 +189,22 @@@ public final class SingleColumnRelatio
      }
  
      @Override
 -    protected Restriction newSliceRestriction(CFMetaData cfm,
 +    protected Restriction newSliceRestriction(TableMetadata table,
                                                VariableSpecifications boundNames,
                                                Bound bound,
 -                                              boolean inclusive) throws InvalidRequestException
 +                                              boolean inclusive)
      {
 -        ColumnDefinition columnDef = entity.prepare(cfm);
 +        ColumnMetadata columnDef = entity.prepare(table);
-         checkFalse(columnDef.type instanceof DurationType, "Slice restriction are not supported on duration columns");
+ 
+         if (columnDef.type.referencesDuration())
+         {
+             checkFalse(columnDef.type.isCollection(), "Slice restrictions are not supported on collections containing durations");
+             checkFalse(columnDef.type.isTuple(), "Slice restrictions are not supported on tuples containing durations");
+             checkFalse(columnDef.type.isUDT(), "Slice restrictions are not supported on UDTs containing durations");
+             throw invalidRequest("Slice restrictions are not supported on duration columns");
+         }
  
 -        Term term = toTerm(toReceivers(columnDef), value, cfm.ksName, boundNames);
 +        Term term = toTerm(toReceivers(columnDef), value, table.keyspace, boundNames);
          return new SingleColumnRestriction.SliceRestriction(columnDef, bound, inclusive, term);
      }
  

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cd29d44d/src/java/org/apache/cassandra/cql3/conditions/ColumnCondition.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/conditions/ColumnCondition.java
index 31b7185,0000000..24a8674
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/conditions/ColumnCondition.java
+++ b/src/java/org/apache/cassandra/cql3/conditions/ColumnCondition.java
@@@ -1,825 -1,0 +1,839 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you 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 org.apache.cassandra.cql3.conditions;
 +
 +import java.nio.ByteBuffer;
 +import java.util.*;
 +
 +import com.google.common.collect.Iterators;
 +
 +import org.apache.cassandra.cql3.*;
 +import org.apache.cassandra.cql3.Term.Terminal;
 +import org.apache.cassandra.cql3.functions.Function;
 +import org.apache.cassandra.db.rows.*;
 +import org.apache.cassandra.db.marshal.*;
 +import org.apache.cassandra.schema.ColumnMetadata;
 +import org.apache.cassandra.schema.TableMetadata;
 +import org.apache.cassandra.transport.ProtocolVersion;
 +import org.apache.cassandra.utils.ByteBufferUtil;
 +
 +import static org.apache.cassandra.cql3.statements.RequestValidations.*;
 +
 +/**
 + * A CQL3 condition on the value of a column or collection element.  For example, "UPDATE .. IF a = 0".
 + */
 +public abstract class ColumnCondition
 +{
 +    public final ColumnMetadata column;
 +    public final Operator operator;
 +    private final Terms terms;
 +
 +    private ColumnCondition(ColumnMetadata column, Operator op, Terms terms)
 +    {
 +        this.column = column;
 +        this.operator = op;
 +        this.terms = terms;
 +    }
 +
 +    /**
 +     * Adds functions for the bind variables of this operation.
 +     *
 +     * @param functions the list of functions to get add
 +     */
 +    public void addFunctionsTo(List<Function> functions)
 +    {
 +        terms.addFunctionsTo(functions);
 +    }
 +
 +    /**
 +     * Collects the column specification for the bind variables of this operation.
 +     *
 +     * @param boundNames the list of column specification where to collect the
 +     * bind variables of this term in.
 +     */
 +    public void collectMarkerSpecification(VariableSpecifications boundNames)
 +    {
 +        terms.collectMarkerSpecification(boundNames);
 +    }
 +
 +    public abstract ColumnCondition.Bound bind(QueryOptions options);
 +
 +    protected final List<ByteBuffer> bindAndGetTerms(QueryOptions options)
 +    {
 +        return filterUnsetValuesIfNeeded(checkValues(terms.bindAndGet(options)));
 +    }
 +
 +    protected final List<Terminal> bindTerms(QueryOptions options)
 +    {
 +        return filterUnsetValuesIfNeeded(checkValues(terms.bind(options)));
 +    }
 +
 +    /**
 +     * Checks that the output of a bind operations on {@code Terms} is a valid one.
 +     * @param values the list to check
 +     * @return the input list
 +     */
 +    private <T> List<T> checkValues(List<T> values)
 +    {
 +        checkFalse(values == null && operator.isIN(), "Invalid null list in IN condition");
 +        checkFalse(values == Terms.UNSET_LIST, "Invalid 'unset' value in condition");
 +        return values;
 +    }
 +
 +    private <T> List<T> filterUnsetValuesIfNeeded(List<T> values)
 +    {
 +        if (!operator.isIN())
 +            return values;
 +
 +        List<T> filtered = new ArrayList<>(values.size());
 +        for (int i = 0, m = values.size(); i < m; i++)
 +        {
 +            T value = values.get(i);
 +            // The value can be ByteBuffer or Constants.Value so we need to check the 2 type of UNSET
 +            if (value != ByteBufferUtil.UNSET_BYTE_BUFFER && value != Constants.UNSET_VALUE)
 +                filtered.add(value);
 +        }
 +        return filtered;
 +    }
 +
 +    /**
 +     * Simple condition (e.g. <pre>IF v = 1</pre>).
 +     */
 +    private static final class SimpleColumnCondition extends ColumnCondition
 +    {
 +        public SimpleColumnCondition(ColumnMetadata column, Operator op, Terms values)
 +        {
 +            super(column, op, values);
 +        }
 +
 +        public Bound bind(QueryOptions options)
 +        {
 +            if (column.type.isCollection() && column.type.isMultiCell())
 +                return new MultiCellCollectionBound(column, operator, bindTerms(options));
 +
 +            if (column.type.isUDT() && column.type.isMultiCell())
 +                return new MultiCellUdtBound(column, operator, bindAndGetTerms(options), options.getProtocolVersion());
 +
 +            return new SimpleBound(column, operator, bindAndGetTerms(options));
 +        }
 +    }
 +
 +    /**
 +     * A condition on a collection element (e.g. <pre>IF l[1] = 1</pre>).
 +     */
 +    private static class CollectionElementCondition extends ColumnCondition
 +    {
 +        private final Term collectionElement;
 +
 +        public CollectionElementCondition(ColumnMetadata column, Term collectionElement, Operator op, Terms values)
 +        {
 +            super(column, op, values);
 +            this.collectionElement = collectionElement;
 +        }
 +
 +        public void addFunctionsTo(List<Function> functions)
 +        {
 +            collectionElement.addFunctionsTo(functions);
 +            super.addFunctionsTo(functions);
 +        }
 +
 +        public void collectMarkerSpecification(VariableSpecifications boundNames)
 +        {
 +            collectionElement.collectMarkerSpecification(boundNames);
 +            super.collectMarkerSpecification(boundNames);
 +        }
 +
 +        public Bound bind(QueryOptions options)
 +        {
 +            return new ElementAccessBound(column, collectionElement.bindAndGet(options), operator, bindAndGetTerms(options));
 +        }
 +    }
 +
 +    /**
 +     *  A condition on a UDT field (e.g. <pre>IF v.a = 1</pre>).
 +     */
 +    private final static class UDTFieldCondition extends ColumnCondition
 +    {
 +        private final FieldIdentifier udtField;
 +
 +        public UDTFieldCondition(ColumnMetadata column, FieldIdentifier udtField, Operator op, Terms values)
 +        {
 +            super(column, op, values);
 +            assert udtField != null;
 +            this.udtField = udtField;
 +        }
 +
 +        public Bound bind(QueryOptions options)
 +        {
 +            return new UDTFieldAccessBound(column, udtField, operator, bindAndGetTerms(options));
 +        }
 +    }
 +
 +    /**
 +     *  A regular column, simple condition.
 +     */
 +    public static ColumnCondition condition(ColumnMetadata column, Operator op, Terms terms)
 +    {
 +        return new SimpleColumnCondition(column, op, terms);
 +    }
 +
 +    /**
 +     * A collection column, simple condition.
 +     */
 +    public static ColumnCondition condition(ColumnMetadata column, Term collectionElement, Operator op, Terms terms)
 +    {
 +        return new CollectionElementCondition(column, collectionElement, op, terms);
 +    }
 +
 +    /**
 +     * A UDT column, simple condition.
 +     */
 +    public static ColumnCondition condition(ColumnMetadata column, FieldIdentifier udtField, Operator op, Terms terms)
 +    {
 +        return new UDTFieldCondition(column, udtField, op, terms);
 +    }
 +
 +    public static abstract class Bound
 +    {
 +        public final ColumnMetadata column;
 +        public final Operator comparisonOperator;
 +
 +        protected Bound(ColumnMetadata column, Operator operator)
 +        {
 +            this.column = column;
 +            // If the operator is an IN we want to compare the value using an EQ.
 +            this.comparisonOperator = operator.isIN() ? Operator.EQ : operator;
 +        }
 +
 +        /**
 +         * Validates whether this condition applies to {@code current}.
 +         */
 +        public abstract boolean appliesTo(Row row);
 +
 +        public ByteBuffer getCollectionElementValue()
 +        {
 +            return null;
 +        }
 +
 +        /** Returns true if the operator is satisfied (i.e. "otherValue operator value == true"), false otherwise. */
 +        protected static boolean compareWithOperator(Operator operator, AbstractType<?> type, ByteBuffer value, ByteBuffer otherValue)
 +        {
 +            if (value == ByteBufferUtil.UNSET_BYTE_BUFFER)
 +                throw invalidRequest("Invalid 'unset' value in condition");
 +
 +            if (value == null)
 +            {
 +                switch (operator)
 +                {
 +                    case EQ:
 +                        return otherValue == null;
 +                    case NEQ:
 +                        return otherValue != null;
 +                    default:
 +                        throw invalidRequest("Invalid comparison with null for operator \"%s\"", operator);
 +                }
 +            }
 +            else if (otherValue == null)
 +            {
 +                // the condition value is not null, so only NEQ can return true
 +                return operator == Operator.NEQ;
 +            }
 +            return operator.isSatisfiedBy(type, otherValue, value);
 +        }
 +    }
 +
 +    protected static final Cell getCell(Row row, ColumnMetadata column)
 +    {
 +        // If we're asking for a given cell, and we didn't got any row from our read, it's
 +        // the same as not having said cell.
 +        return row == null ? null : row.getCell(column);
 +    }
 +
 +    protected static final Cell getCell(Row row, ColumnMetadata column, CellPath path)
 +    {
 +        // If we're asking for a given cell, and we didn't got any row from our read, it's
 +        // the same as not having said cell.
 +        return row == null ? null : row.getCell(column, path);
 +    }
 +
 +    protected static final Iterator<Cell> getCells(Row row, ColumnMetadata column)
 +    {
 +        // If we're asking for a complex cells, and we didn't got any row from our read, it's
 +        // the same as not having any cells for that column.
 +        if (row == null)
 +            return Collections.<Cell>emptyIterator();
 +
 +        ComplexColumnData complexData = row.getComplexColumnData(column);
 +        return complexData == null ? Collections.<Cell>emptyIterator() : complexData.iterator();
 +    }
 +
 +    protected static final boolean evaluateComparisonWithOperator(int comparison, Operator operator)
 +    {
 +        // called when comparison != 0
 +        switch (operator)
 +        {
 +            case EQ:
 +                return false;
 +            case LT:
 +            case LTE:
 +                return comparison < 0;
 +            case GT:
 +            case GTE:
 +                return comparison > 0;
 +            case NEQ:
 +                return true;
 +            default:
 +                throw new AssertionError();
 +        }
 +    }
 +
 +    /**
 +     * A condition on a single non-collection column.
 +     */
 +    private static final class SimpleBound extends Bound
 +    {
 +        /**
 +         * The condition values
 +         */
 +        private final List<ByteBuffer> values;
 +
 +        private SimpleBound(ColumnMetadata column, Operator operator, List<ByteBuffer> values)
 +        {
 +            super(column, operator);
 +            this.values = values;
 +        }
 +
 +        @Override
 +        public boolean appliesTo(Row row)
 +        {
 +            return isSatisfiedBy(rowValue(row));
 +        }
 +
 +        private ByteBuffer rowValue(Row row)
 +        {
 +            Cell c = getCell(row, column);
 +            return c == null ? null : c.value();
 +        }
 +
 +        private boolean isSatisfiedBy(ByteBuffer rowValue)
 +        {
 +            for (ByteBuffer value : values)
 +            {
 +                if (compareWithOperator(comparisonOperator, column.type, value, rowValue))
 +                    return true;
 +            }
 +            return false;
 +        }
 +    }
 +
 +    /**
 +     * A condition on an element of a collection column.
 +     */
 +    private static final class ElementAccessBound extends Bound
 +    {
 +        /**
 +         * The collection element
 +         */
 +        private final ByteBuffer collectionElement;
 +
 +        /**
 +         * The conditions values.
 +         */
 +        private final List<ByteBuffer> values;
 +
 +        private ElementAccessBound(ColumnMetadata column,
 +                                   ByteBuffer collectionElement,
 +                                   Operator operator,
 +                                   List<ByteBuffer> values)
 +        {
 +            super(column, operator);
 +
 +            this.collectionElement = collectionElement;
 +            this.values = values;
 +        }
 +
 +        @Override
 +        public boolean appliesTo(Row row)
 +        {
 +            boolean isMap = column.type instanceof MapType;
 +
 +            if (collectionElement == null)
 +                throw invalidRequest("Invalid null value for %s element access", isMap ? "map" : "list");
 +
 +            if (isMap)
 +            {
 +                MapType<?, ?> mapType = (MapType<?, ?>) column.type;
 +                ByteBuffer rowValue = rowMapValue(mapType, row);
 +                return isSatisfiedBy(mapType.getKeysType(), rowValue);
 +            }
 +
 +            ListType<?> listType = (ListType<?>) column.type;
 +            ByteBuffer rowValue = rowListValue(listType, row);
 +            return isSatisfiedBy(listType.getElementsType(), rowValue);
 +        }
 +
 +        private ByteBuffer rowMapValue(MapType<?, ?> type, Row row)
 +        {
 +            if (column.type.isMultiCell())
 +            {
 +                Cell cell = getCell(row, column, CellPath.create(collectionElement));
 +                return cell == null ? null : cell.value();
 +            }
 +
 +            Cell cell = getCell(row, column);
 +            return cell == null
 +                    ? null
 +                    : type.getSerializer().getSerializedValue(cell.value(), collectionElement, type.getKeysType());
 +        }
 +
 +        private ByteBuffer rowListValue(ListType<?> type, Row row)
 +        {
 +            if (column.type.isMultiCell())
 +                return cellValueAtIndex(getCells(row, column), getListIndex(collectionElement));
 +
 +            Cell cell = getCell(row, column);
 +            return cell == null
 +                    ? null
 +                    : type.getSerializer().getElement(cell.value(), getListIndex(collectionElement));
 +        }
 +
 +        private static ByteBuffer cellValueAtIndex(Iterator<Cell> iter, int index)
 +        {
 +            int adv = Iterators.advance(iter, index);
 +            if (adv == index && iter.hasNext())
 +                return iter.next().value();
 +
 +            return null;
 +        }
 +
 +        private boolean isSatisfiedBy(AbstractType<?> valueType, ByteBuffer rowValue)
 +        {
 +            for (ByteBuffer value : values)
 +            {
 +                if (compareWithOperator(comparisonOperator, valueType, value, rowValue))
 +                    return true;
 +            }
 +            return false;
 +        }
 +
 +        @Override
 +        public ByteBuffer getCollectionElementValue()
 +        {
 +            return collectionElement;
 +        }
 +
 +        private static int getListIndex(ByteBuffer collectionElement)
 +        {
 +            int idx = ByteBufferUtil.toInt(collectionElement);
 +            checkFalse(idx < 0, "Invalid negative list index %d", idx);
 +            return idx;
 +        }
 +    }
 +
 +    /**
 +     * A condition on an entire collection column.
 +     */
 +    private static final class MultiCellCollectionBound extends Bound
 +    {
 +        private final List<Term.Terminal> values;
 +
 +        public MultiCellCollectionBound(ColumnMetadata column, Operator operator, List<Term.Terminal> values)
 +        {
 +            super(column, operator);
 +            assert column.type.isMultiCell();
 +            this.values = values;
 +        }
 +
 +        public boolean appliesTo(Row row)
 +        {
 +            CollectionType<?> type = (CollectionType<?>)column.type;
 +
 +            // copy iterator contents so that we can properly reuse them for each comparison with an IN value
 +            for (Term.Terminal value : values)
 +            {
 +                Iterator<Cell> iter = getCells(row, column);
 +                if (value == null)
 +                {
 +                    if (comparisonOperator == Operator.EQ)
 +                    {
 +                        if (!iter.hasNext())
 +                            return true;
 +                        continue;
 +                    }
 +
 +                    if (comparisonOperator == Operator.NEQ)
 +                        return iter.hasNext();
 +
 +                    throw invalidRequest("Invalid comparison with null for operator \"%s\"", comparisonOperator);
 +                }
 +
 +                if (valueAppliesTo(type, iter, value, comparisonOperator))
 +                    return true;
 +            }
 +            return false;
 +        }
 +
 +        private static boolean valueAppliesTo(CollectionType<?> type, Iterator<Cell> iter, Term.Terminal value, Operator operator)
 +        {
 +            if (value == null)
 +                return !iter.hasNext();
 +
 +            switch (type.kind)
 +            {
 +                case LIST:
 +                    List<ByteBuffer> valueList = ((Lists.Value) value).elements;
 +                    return listAppliesTo((ListType<?>)type, iter, valueList, operator);
 +                case SET:
 +                    Set<ByteBuffer> valueSet = ((Sets.Value) value).elements;
 +                    return setAppliesTo((SetType<?>)type, iter, valueSet, operator);
 +                case MAP:
 +                    Map<ByteBuffer, ByteBuffer> valueMap = ((Maps.Value) value).map;
 +                    return mapAppliesTo((MapType<?, ?>)type, iter, valueMap, operator);
 +            }
 +            throw new AssertionError();
 +        }
 +
 +        private static boolean setOrListAppliesTo(AbstractType<?> type, Iterator<Cell> iter, Iterator<ByteBuffer> conditionIter, Operator operator, boolean isSet)
 +        {
 +            while(iter.hasNext())
 +            {
 +                if (!conditionIter.hasNext())
 +                    return (operator == Operator.GT) || (operator == Operator.GTE) || (operator == Operator.NEQ);
 +
 +                // for lists we use the cell value; for sets we use the cell name
 +                ByteBuffer cellValue = isSet ? iter.next().path().get(0) : iter.next().value();
 +                int comparison = type.compare(cellValue, conditionIter.next());
 +                if (comparison != 0)
 +                    return evaluateComparisonWithOperator(comparison, operator);
 +            }
 +
 +            if (conditionIter.hasNext())
 +                return (operator == Operator.LT) || (operator == Operator.LTE) || (operator == Operator.NEQ);
 +
 +            // they're equal
 +            return operator == Operator.EQ || operator == Operator.LTE || operator == Operator.GTE;
 +        }
 +
 +        private static boolean listAppliesTo(ListType<?> type, Iterator<Cell> iter, List<ByteBuffer> elements, Operator operator)
 +        {
 +            return setOrListAppliesTo(type.getElementsType(), iter, elements.iterator(), operator, false);
 +        }
 +
 +        private static boolean setAppliesTo(SetType<?> type, Iterator<Cell> iter, Set<ByteBuffer> elements, Operator operator)
 +        {
 +            ArrayList<ByteBuffer> sortedElements = new ArrayList<>(elements);
 +            Collections.sort(sortedElements, type.getElementsType());
 +            return setOrListAppliesTo(type.getElementsType(), iter, sortedElements.iterator(), operator, true);
 +        }
 +
 +        private static boolean mapAppliesTo(MapType<?, ?> type, Iterator<Cell> iter, Map<ByteBuffer, ByteBuffer> elements, Operator operator)
 +        {
 +            Iterator<Map.Entry<ByteBuffer, ByteBuffer>> conditionIter = elements.entrySet().iterator();
 +            while(iter.hasNext())
 +            {
 +                if (!conditionIter.hasNext())
 +                    return (operator == Operator.GT) || (operator == Operator.GTE) || (operator == Operator.NEQ);
 +
 +                Map.Entry<ByteBuffer, ByteBuffer> conditionEntry = conditionIter.next();
 +                Cell c = iter.next();
 +
 +                // compare the keys
 +                int comparison = type.getKeysType().compare(c.path().get(0), conditionEntry.getKey());
 +                if (comparison != 0)
 +                    return evaluateComparisonWithOperator(comparison, operator);
 +
 +                // compare the values
 +                comparison = type.getValuesType().compare(c.value(), conditionEntry.getValue());
 +                if (comparison != 0)
 +                    return evaluateComparisonWithOperator(comparison, operator);
 +            }
 +
 +            if (conditionIter.hasNext())
 +                return (operator == Operator.LT) || (operator == Operator.LTE) || (operator == Operator.NEQ);
 +
 +            // they're equal
 +            return operator == Operator.EQ || operator == Operator.LTE || operator == Operator.GTE;
 +        }
 +    }
 +
 +    /**
 +     * A condition on a UDT field
 +     */
 +    private static final class UDTFieldAccessBound extends Bound
 +    {
 +        /**
 +         * The UDT field.
 +         */
 +        private final FieldIdentifier field;
 +
 +        /**
 +         * The conditions values.
 +         */
 +        private final List<ByteBuffer> values;
 +
 +        private UDTFieldAccessBound(ColumnMetadata column, FieldIdentifier field, Operator operator, List<ByteBuffer> values)
 +        {
 +            super(column, operator);
 +            assert column.type.isUDT() && field != null;
 +            this.field = field;
 +            this.values = values;
 +        }
 +
 +        @Override
 +        public boolean appliesTo(Row row)
 +        {
 +            return isSatisfiedBy(rowValue(row));
 +        }
 +
 +        private ByteBuffer rowValue(Row row)
 +        {
 +            UserType userType = (UserType) column.type;
 +
 +            if (column.type.isMultiCell())
 +            {
 +                Cell cell = getCell(row, column, userType.cellPathForField(field));
 +                return cell == null ? null : cell.value();
 +            }
 +
 +            Cell cell = getCell(row, column);
 +            return cell == null
 +                      ? null
 +                      : userType.split(cell.value())[userType.fieldPosition(field)];
 +        }
 +
 +        private boolean isSatisfiedBy(ByteBuffer rowValue)
 +        {
 +            UserType userType = (UserType) column.type;
 +            int fieldPosition = userType.fieldPosition(field);
 +            AbstractType<?> valueType = userType.fieldType(fieldPosition);
 +            for (ByteBuffer value : values)
 +            {
 +                if (compareWithOperator(comparisonOperator, valueType, value, rowValue))
 +                    return true;
 +            }
 +            return false;
 +        }
 +    }
 +
 +    /**
 +     * A condition on an entire UDT.
 +     */
 +    private static final class MultiCellUdtBound extends Bound
 +    {
 +        /**
 +         * The conditions values.
 +         */
 +        private final List<ByteBuffer> values;
 +
 +        /**
 +         * The protocol version
 +         */
 +        private final ProtocolVersion protocolVersion;
 +
 +        private MultiCellUdtBound(ColumnMetadata column, Operator op, List<ByteBuffer> values, ProtocolVersion protocolVersion)
 +        {
 +            super(column, op);
 +            assert column.type.isMultiCell();
 +            this.values = values;
 +            this.protocolVersion = protocolVersion;
 +        }
 +
 +        @Override
 +        public boolean appliesTo(Row row)
 +        {
 +            return isSatisfiedBy(rowValue(row));
 +        }
 +
 +        private final ByteBuffer rowValue(Row row)
 +        {
 +            UserType userType = (UserType) column.type;
 +            Iterator<Cell> iter = getCells(row, column);
 +            return iter.hasNext() ? userType.serializeForNativeProtocol(iter, protocolVersion) : null;
 +        }
 +
 +        private boolean isSatisfiedBy(ByteBuffer rowValue)
 +        {
 +            for (ByteBuffer value : values)
 +            {
 +                if (compareWithOperator(comparisonOperator, column.type, value, rowValue))
 +                    return true;
 +            }
 +            return false;
 +        }
 +    }
 +
 +    public static class Raw
 +    {
 +        private final Term.Raw value;
 +        private final List<Term.Raw> inValues;
 +        private final AbstractMarker.INRaw inMarker;
 +
 +        // Can be null, only used with the syntax "IF m[e] = ..." (in which case it's 'e')
 +        private final Term.Raw collectionElement;
 +
 +        // Can be null, only used with the syntax "IF udt.field = ..." (in which case it's 'field')
 +        private final FieldIdentifier udtField;
 +
 +        private final Operator operator;
 +
 +        private Raw(Term.Raw value, List<Term.Raw> inValues, AbstractMarker.INRaw inMarker, Term.Raw collectionElement,
 +                    FieldIdentifier udtField, Operator op)
 +        {
 +            this.value = value;
 +            this.inValues = inValues;
 +            this.inMarker = inMarker;
 +            this.collectionElement = collectionElement;
 +            this.udtField = udtField;
 +            this.operator = op;
 +        }
 +
 +        /** A condition on a column. For example: "IF col = 'foo'" */
 +        public static Raw simpleCondition(Term.Raw value, Operator op)
 +        {
 +            return new Raw(value, null, null, null, null, op);
 +        }
 +
 +        /** An IN condition on a column. For example: "IF col IN ('foo', 'bar', ...)" */
 +        public static Raw simpleInCondition(List<Term.Raw> inValues)
 +        {
 +            return new Raw(null, inValues, null, null, null, Operator.IN);
 +        }
 +
 +        /** An IN condition on a column with a single marker. For example: "IF col IN ?" */
 +        public static Raw simpleInCondition(AbstractMarker.INRaw inMarker)
 +        {
 +            return new Raw(null, null, inMarker, null, null, Operator.IN);
 +        }
 +
 +        /** A condition on a collection element. For example: "IF col['key'] = 'foo'" */
 +        public static Raw collectionCondition(Term.Raw value, Term.Raw collectionElement, Operator op)
 +        {
 +            return new Raw(value, null, null, collectionElement, null, op);
 +        }
 +
 +        /** An IN condition on a collection element. For example: "IF col['key'] IN ('foo', 'bar', ...)" */
 +        public static Raw collectionInCondition(Term.Raw collectionElement, List<Term.Raw> inValues)
 +        {
 +            return new Raw(null, inValues, null, collectionElement, null, Operator.IN);
 +        }
 +
 +        /** An IN condition on a collection element with a single marker. For example: "IF col['key'] IN ?" */
 +        public static Raw collectionInCondition(Term.Raw collectionElement, AbstractMarker.INRaw inMarker)
 +        {
 +            return new Raw(null, null, inMarker, collectionElement, null, Operator.IN);
 +        }
 +
 +        /** A condition on a UDT field. For example: "IF col.field = 'foo'" */
 +        public static Raw udtFieldCondition(Term.Raw value, FieldIdentifier udtField, Operator op)
 +        {
 +            return new Raw(value, null, null, null, udtField, op);
 +        }
 +
 +        /** An IN condition on a collection element. For example: "IF col.field IN ('foo', 'bar', ...)" */
 +        public static Raw udtFieldInCondition(FieldIdentifier udtField, List<Term.Raw> inValues)
 +        {
 +            return new Raw(null, inValues, null, null, udtField, Operator.IN);
 +        }
 +
 +        /** An IN condition on a collection element with a single marker. For example: "IF col.field IN ?" */
 +        public static Raw udtFieldInCondition(FieldIdentifier udtField, AbstractMarker.INRaw inMarker)
 +        {
 +            return new Raw(null, null, inMarker, null, udtField, Operator.IN);
 +        }
 +
 +        public ColumnCondition prepare(String keyspace, ColumnMetadata receiver, TableMetadata cfm)
 +        {
 +            if (receiver.type instanceof CounterColumnType)
 +                throw invalidRequest("Conditions on counters are not supported");
 +
 +            if (collectionElement != null)
 +            {
 +                if (!(receiver.type.isCollection()))
 +                    throw invalidRequest("Invalid element access syntax for non-collection column %s", receiver.name);
 +
 +                ColumnSpecification elementSpec, valueSpec;
 +                switch ((((CollectionType<?>) receiver.type).kind))
 +                {
 +                    case LIST:
 +                        elementSpec = Lists.indexSpecOf(receiver);
 +                        valueSpec = Lists.valueSpecOf(receiver);
 +                        break;
 +                    case MAP:
 +                        elementSpec = Maps.keySpecOf(receiver);
 +                        valueSpec = Maps.valueSpecOf(receiver);
 +                        break;
 +                    case SET:
 +                        throw invalidRequest("Invalid element access syntax for set column %s", receiver.name);
 +                    default:
 +                        throw new AssertionError();
 +                }
 +
++                validateOperationOnDurations(valueSpec.type);
 +                return condition(receiver, collectionElement.prepare(keyspace, elementSpec), operator, prepareTerms(keyspace, valueSpec));
 +            }
 +
 +            if (udtField != null)
 +            {
 +                UserType userType = (UserType) receiver.type;
 +                int fieldPosition = userType.fieldPosition(udtField);
 +                if (fieldPosition == -1)
 +                    throw invalidRequest("Unknown field %s for column %s", udtField, receiver.name);
 +
 +                ColumnSpecification fieldReceiver = UserTypes.fieldSpecOf(receiver, fieldPosition);
++                validateOperationOnDurations(fieldReceiver.type);
 +                return condition(receiver, udtField, operator, prepareTerms(keyspace, fieldReceiver));
 +            }
 +
++            validateOperationOnDurations(receiver.type);
 +            return condition(receiver, operator, prepareTerms(keyspace, receiver));
 +        }
 +
 +        private Terms prepareTerms(String keyspace, ColumnSpecification receiver)
 +        {
 +            if (operator.isIN())
 +            {
 +                return inValues == null ? Terms.ofListMarker(inMarker.prepare(keyspace, receiver), receiver.type)
 +                                        : Terms.of(prepareTerms(keyspace, receiver, inValues));
 +            }
 +
 +            return Terms.of(value.prepare(keyspace, receiver));
 +        }
 +
 +        private static List<Term> prepareTerms(String keyspace, ColumnSpecification receiver, List<Term.Raw> raws)
 +        {
 +            List<Term> terms = new ArrayList<>(raws.size());
 +            for (int i = 0, m = raws.size(); i < m; i++)
 +            {
 +                Term.Raw raw = raws.get(i);
 +                terms.add(raw.prepare(keyspace, receiver));
 +            }
 +            return terms;
 +        }
++
++        private void validateOperationOnDurations(AbstractType<?> type)
++        {
++            if (type.referencesDuration() && operator.isSlice())
++            {
++                checkFalse(type.isCollection(), "Slice conditions are not supported on collections containing durations");
++                checkFalse(type.isTuple(), "Slice conditions are not supported on tuples containing durations");
++                checkFalse(type.isUDT(), "Slice conditions are not supported on UDTs containing durations");
++                throw invalidRequest("Slice conditions are not supported on durations", operator);
++            }
++        }
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cd29d44d/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
index 7560e2f,ed4658f..a98193b
--- a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
@@@ -34,16 -38,17 +34,19 @@@ import org.apache.cassandra.db.marshal.
  import org.apache.cassandra.exceptions.InvalidRequestException;
  import org.apache.cassandra.exceptions.RequestValidationException;
  import org.apache.cassandra.exceptions.UnauthorizedException;
 +import org.apache.cassandra.schema.ColumnMetadata;
  import org.apache.cassandra.schema.IndexMetadata;
  import org.apache.cassandra.schema.Indexes;
 +import org.apache.cassandra.schema.MigrationManager;
 +import org.apache.cassandra.schema.Schema;
 +import org.apache.cassandra.schema.TableMetadata;
  import org.apache.cassandra.service.ClientState;
 -import org.apache.cassandra.service.MigrationManager;
  import org.apache.cassandra.service.QueryState;
 -import org.apache.cassandra.thrift.ThriftValidation;
  import org.apache.cassandra.transport.Event;
  
+ import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
+ import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
+ 
  /** A <code>CREATE INDEX</code> statement parsed from a CQL query. */
  public class CreateIndexStatement extends SchemaAlteringStatement
  {
@@@ -102,11 -107,19 +105,19 @@@
              if (cd == null)
                  throw new InvalidRequestException("No column definition found for column " + target.column);
  
+             if (cd.type.referencesDuration())
+             {
+                 checkFalse(cd.type.isCollection(), "Secondary indexes are not supported on collections containing durations");
+                 checkFalse(cd.type.isTuple(), "Secondary indexes are not supported on tuples containing durations");
+                 checkFalse(cd.type.isUDT(), "Secondary indexes are not supported on UDTs containing durations");
+                 throw invalidRequest("Secondary indexes are not supported on duration columns");
+             }
+ 
              // TODO: we could lift that limitation
 -            if (cfm.isCompactTable() && cd.isPrimaryKeyColumn())
 +            if (table.isCompactTable() && cd.isPrimaryKeyColumn())
                  throw new InvalidRequestException("Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables");
  
 -            if (cd.kind == ColumnDefinition.Kind.PARTITION_KEY && cfm.getKeyValidatorAsClusteringComparator().size() == 1)
 +            if (cd.kind == ColumnMetadata.Kind.PARTITION_KEY && table.partitionKeyColumns().size() == 1)
                  throw new InvalidRequestException(String.format("Cannot create secondary index on partition key column %s", target.column));
  
              boolean isMap = cd.type instanceof MapType;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cd29d44d/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
index f098126,c03b0cc..c00688d
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
@@@ -1330,13 -1344,40 +1330,39 @@@ public class SecondaryIndexTest extend
                     row(1, 1, 9, 1));
      }
  
+     @Test
+     public void testIndexOnDurationColumn() throws Throwable
+     {
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, d duration)");
+         assertInvalidMessage("Secondary indexes are not supported on duration columns",
+                              "CREATE INDEX ON %s (d)");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, l list<duration>)");
+         assertInvalidMessage("Secondary indexes are not supported on collections containing durations",
+                              "CREATE INDEX ON %s (l)");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, m map<int, duration>)");
+         assertInvalidMessage("Secondary indexes are not supported on collections containing durations",
+                              "CREATE INDEX ON %s (m)");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, t tuple<int, duration>)");
+         assertInvalidMessage("Secondary indexes are not supported on tuples containing durations",
+                              "CREATE INDEX ON %s (t)");
+ 
+         String udt = createType("CREATE TYPE %s (i int, d duration)");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, t " + udt + ")");
+         assertInvalidMessage("Secondary indexes are not supported on UDTs containing durations",
+                              "CREATE INDEX ON %s (t)");
+     }
+ 
 -    private ResultMessage.Prepared prepareStatement(String cql, boolean forThrift)
 +    private ResultMessage.Prepared prepareStatement(String cql)
      {
          return QueryProcessor.prepare(String.format(cql, KEYSPACE, currentTable()),
 -                                      ClientState.forInternalCalls(),
 -                                      forThrift);
 +                                      ClientState.forInternalCalls());
      }
  
 -    private void validateCell(Cell cell, ColumnDefinition def, ByteBuffer val, long timestamp)
 +    private void validateCell(Cell cell, ColumnMetadata def, ByteBuffer val, long timestamp)
      {
          assertNotNull(cell);
          assertEquals(0, def.type.compare(cell.value(), val));

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cd29d44d/test/unit/org/apache/cassandra/cql3/validation/operations/CreateTest.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cd29d44d/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java
index 7cbe085,596ef62..da5c3bd
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java
@@@ -23,8 -23,9 +23,9 @@@ import java.util.List
  
  import org.junit.Test;
  
 -import org.apache.cassandra.config.SchemaConstants;
 +import org.apache.cassandra.schema.SchemaConstants;
  import org.apache.cassandra.cql3.CQLTester;
+ import org.apache.cassandra.cql3.Duration;
  import org.apache.cassandra.exceptions.InvalidRequestException;
  import org.apache.cassandra.exceptions.SyntaxException;
  import org.apache.cassandra.schema.SchemaKeyspace;
@@@ -2033,51 -2061,144 +2034,144 @@@ public class InsertUpdateIfConditionTes
      }
  
      @Test
+     public void testConditionalOnDurationColumns() throws Throwable
+     {
+         createTable(" CREATE TABLE %s (k int PRIMARY KEY, v int, d duration)");
+ 
+         assertInvalidMessage("Slice conditions are not supported on durations",
+                              "UPDATE %s SET v = 3 WHERE k = 0 IF d > 1s");
+         assertInvalidMessage("Slice conditions are not supported on durations",
+                              "UPDATE %s SET v = 3 WHERE k = 0 IF d >= 1s");
+         assertInvalidMessage("Slice conditions are not supported on durations",
+                              "UPDATE %s SET v = 3 WHERE k = 0 IF d <= 1s");
+         assertInvalidMessage("Slice conditions are not supported on durations",
+                              "UPDATE %s SET v = 3 WHERE k = 0 IF d < 1s");
+ 
+         execute("INSERT INTO %s (k, v, d) VALUES (1, 1, 2s)");
+ 
+         assertRows(execute("UPDATE %s SET v = 4 WHERE k = 1 IF d = 1s"), row(false, Duration.from("2s")));
+         assertRows(execute("UPDATE %s SET v = 3 WHERE k = 1 IF d = 2s"), row(true));
+ 
+         assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, Duration.from("2s"), 3));
+ 
+         assertRows(execute("UPDATE %s SET d = 10s WHERE k = 1 IF d != 2s"), row(false, Duration.from("2s")));
+         assertRows(execute("UPDATE %s SET v = 6 WHERE k = 1 IF d != 1s"), row(true));
+ 
+         assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, Duration.from("2s"), 6));
+ 
+         assertRows(execute("UPDATE %s SET v = 5 WHERE k = 1 IF d IN (1s, 5s)"), row(false, Duration.from("2s")));
+         assertRows(execute("UPDATE %s SET d = 10s WHERE k = 1 IF d IN (1s, 2s)"), row(true));
+ 
+         assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, Duration.from("10s"), 6));
+     }
+ 
+     @Test
+     public void testConditionalOnDurationWithinLists() throws Throwable
+     {
+         for (Boolean frozen : new Boolean[]{Boolean.FALSE, Boolean.TRUE})
+         {
+             String listType = String.format(frozen ? "frozen<%s>" : "%s", "list<duration>");
+ 
+             createTable("CREATE TABLE %s (k int PRIMARY KEY, v int, l " + listType + " )");
+ 
+             assertInvalidMessage("Slice conditions are not supported on collections containing durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF l > [1s, 2s]");
+             assertInvalidMessage("Slice conditions are not supported on collections containing durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF l >= [1s, 2s]");
+             assertInvalidMessage("Slice conditions are not supported on collections containing durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF l <= [1s, 2s]");
+             assertInvalidMessage("Slice conditions are not supported on collections containing durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF l < [1s, 2s]");
+ 
+             execute("INSERT INTO %s (k, v, l) VALUES (1, 1, [1s, 2s])");
+ 
+             assertRows(execute("UPDATE %s SET v = 4 WHERE k = 1 IF l = [2s]"), row(false, list(Duration.from("1000ms"), Duration.from("2s"))));
+             assertRows(execute("UPDATE %s SET v = 3 WHERE k = 1 IF l = [1s, 2s]"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, list(Duration.from("1000ms"), Duration.from("2s")), 3));
+ 
+             assertRows(execute("UPDATE %s SET l = [10s] WHERE k = 1 IF l != [1s, 2s]"), row(false, list(Duration.from("1000ms"), Duration.from("2s"))));
+             assertRows(execute("UPDATE %s SET v = 6 WHERE k = 1 IF l != [1s]"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, list(Duration.from("1000ms"), Duration.from("2s")), 6));
+ 
+             assertRows(execute("UPDATE %s SET v = 5 WHERE k = 1 IF l IN ([1s], [1s, 5s])"), row(false, list(Duration.from("1000ms"), Duration.from("2s"))));
+             assertRows(execute("UPDATE %s SET l = [5s, 10s] WHERE k = 1 IF l IN ([1s], [1s, 2s])"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, list(Duration.from("5s"), Duration.from("10s")), 6));
+ 
+             assertInvalidMessage("Slice conditions are not supported on durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF l[0] > 1s");
+             assertInvalidMessage("Slice conditions are not supported on durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF l[0] >= 1s");
+             assertInvalidMessage("Slice conditions are not supported on durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF l[0] <= 1s");
+             assertInvalidMessage("Slice conditions are not supported on durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF l[0] < 1s");
+ 
+             assertRows(execute("UPDATE %s SET v = 4 WHERE k = 1 IF l[0] = 2s"), row(false, list(Duration.from("5s"), Duration.from("10s"))));
+             assertRows(execute("UPDATE %s SET v = 3 WHERE k = 1 IF l[0] = 5s"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, list(Duration.from("5s"), Duration.from("10s")), 3));
+ 
+             assertRows(execute("UPDATE %s SET l = [10s] WHERE k = 1 IF l[1] != 10s"), row(false, list(Duration.from("5s"), Duration.from("10s"))));
+             assertRows(execute("UPDATE %s SET v = 6 WHERE k = 1 IF l[1] != 1s"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, list(Duration.from("5s"), Duration.from("10s")), 6));
+ 
+             assertRows(execute("UPDATE %s SET v = 5 WHERE k = 1 IF l[0] IN (2s, 10s)"), row(false, list(Duration.from("5s"), Duration.from("10s"))));
+             assertRows(execute("UPDATE %s SET l = [6s, 12s] WHERE k = 1 IF l[0] IN (5s, 10s)"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, list(Duration.from("6s"), Duration.from("12s")), 6));
+         }
+     }
+ 
+     @Test
 -    public void testInMarkerWithMaps() throws Throwable
 +    public void testInMarkerWithLists() throws Throwable
      {
 -        for (boolean frozen : new boolean[] {false, true})
 +        for (boolean frozen : new boolean[]{false, true})
          {
 -            createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, m %s)",
 +            createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, l %s)",
                                        frozen
 -                                      ? "frozen<map<text, text>>"
 -                                      : "map<text, text>"));
 +                                      ? "frozen<list<text>>"
 +                                      : "list<text>"));
  
 -            execute("INSERT INTO %s (k, m) VALUES (0, {'foo' : 'bar'})");
 +            execute("INSERT INTO %s(k, l) VALUES (0, ['foo', 'bar', 'foobar'])");
  
              // Does not apply
 -            assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m IN (?, ?)", map("foo", "foobar"), map("bar", "foobar")),
 -                       row(false, map("foo", "bar")));
 -            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m IN (?, ?)", map("foo", "foobar"), null),
 -                       row(false, map("foo", "bar")));
 -            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m IN (?, ?)", map("foo", "foobar"), unset()),
 -                       row(false, map("foo", "bar")));
 -            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m IN (?, ?)", null, null),
 -                       row(false, map("foo", "bar")));
 -            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m IN ?", list(map("foo", "foobar"), map("bar", "foobar"))),
 -                       row(false, map("foo", "bar")));
 -            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN ?", "foo", list("foo", "foobar")),
 -                       row(false, map("foo", "bar")));
 -            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN (?, ?)", "foo", "foo", "foobar"),
 -                       row(false, map("foo", "bar")));
 -            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN (?, ?)", "foo", "foo", null),
 -                       row(false, map("foo", "bar")));
 -            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN (?, ?)", "foo", "foo", unset()),
 -                       row(false, map("foo", "bar")));
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN (?, ?)", list("foo", "foobar"), list("bar", "foobar")),
 +                       row(false, list("foo", "bar", "foobar")));
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN (?, ?)", list("foo", "foobar"), null),
 +                       row(false, list("foo", "bar", "foobar")));
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN (?, ?)", list("foo", "foobar"), unset()),
 +                       row(false, list("foo", "bar", "foobar")));
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN (?, ?)", null, null),
 +                       row(false, list("foo", "bar", "foobar")));
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN ?", list(list("foo", "foobar"), list("bar", "foobar"))),
 +                       row(false, list("foo", "bar", "foobar")));
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l[?] IN ?", 1, list("foo", "foobar")),
 +                       row(false, list("foo", "bar", "foobar")));
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l[?] IN (?, ?)", 1, "foo", "foobar"),
 +                       row(false, list("foo", "bar", "foobar")));
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l[?] IN (?, ?)", 1, "foo", null),
 +                       row(false, list("foo", "bar", "foobar")));
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l[?] IN (?, ?)", 1, "foo", unset()),
 +                       row(false, list("foo", "bar", "foobar")));
  
              // Does apply
 -            assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m IN (?, ?)", map("foo", "foobar"), map("foo", "bar")),
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN (?, ?)", list("foo", "bar", "foobar"), list("bar", "foobar")),
                         row(true));
 -            assertRows(execute("UPDATE %s SET m = {'foo' : 'bar'} WHERE k = 0 IF m IN (?, ?, ?)", map("bar", "foobar"), null, map("foo", "foobar")),
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'foobar'] WHERE k = 0 IF l IN (?, ?, ?)", list("foo", "bar", "foobar"), null, list("foo", "bar")),
                         row(true));
 -            assertRows(execute("UPDATE %s SET m = {'foo' : 'bar'} WHERE k = 0 IF m IN (?, ?, ?)", map("bar", "foobar"), unset(), map("foo", "bar")),
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN (?, ?, ?)", list("foo", "bar", "foobar"), unset(), list("foo", "foobar")),
                         row(true));
 -            assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m IN ?", list(map("foo", "bar"), map("bar", "foobar"))),
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'foobar'] WHERE k = 0 IF l IN (?, ?)", list("bar", "foobar"), list("foo", "bar")),
                         row(true));
 -            assertRows(execute("UPDATE %s SET m = {'foo' : 'bar'} WHERE k = 0 IF m[?] IN ?", "foo", list("bar", "foobar")),
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l[?] IN ?", 1, list("bar", "foobar")),
                         row(true));
 -            assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN (?, ?, ?)", "foo", "bar", null, "foobar"),
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'foobar'] WHERE k = 0 IF l[?] IN (?, ?, ?)", 1, "bar", null, "foobar"),
                         row(true));
 -            assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN (?, ?, ?)", "foo", "bar", unset(), "foobar"),
 +            assertRows(execute("UPDATE %s SET l = ['foo', 'foobar'] WHERE k = 0 IF l[?] IN (?, ?, ?)", 1, "bar", unset(), "foobar"),
                         row(true));
  
              assertInvalidMessage("Invalid null list in IN condition",
@@@ -2090,59 -2211,158 +2184,215 @@@
      }
  
      @Test
+     public void testConditionalOnDurationWithinMaps() throws Throwable
+     {
+         for (Boolean frozen : new Boolean[]{Boolean.FALSE, Boolean.TRUE})
+         {
+             String mapType = String.format(frozen ? "frozen<%s>" : "%s", "map<int, duration>");
+ 
+             createTable("CREATE TABLE %s (k int PRIMARY KEY, v int, m " + mapType + " )");
+ 
+             assertInvalidMessage("Slice conditions are not supported on collections containing durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF m > {1: 1s, 2: 2s}");
+             assertInvalidMessage("Slice conditions are not supported on collections containing durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF m >= {1: 1s, 2: 2s}");
+             assertInvalidMessage("Slice conditions are not supported on collections containing durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF m <= {1: 1s, 2: 2s}");
+             assertInvalidMessage("Slice conditions are not supported on collections containing durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF m < {1: 1s, 2: 2s}");
+ 
+             execute("INSERT INTO %s (k, v, m) VALUES (1, 1, {1: 1s, 2: 2s})");
+ 
+             assertRows(execute("UPDATE %s SET v = 4 WHERE k = 1 IF m = {2: 2s}"), row(false, map(1, Duration.from("1000ms"), 2, Duration.from("2s"))));
+             assertRows(execute("UPDATE %s SET v = 3 WHERE k = 1 IF m = {1: 1s, 2: 2s}"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, map(1, Duration.from("1000ms"), 2, Duration.from("2s")), 3));
+ 
+             assertRows(execute("UPDATE %s SET m = {1 :10s} WHERE k = 1 IF m != {1: 1s, 2: 2s}"), row(false, map(1, Duration.from("1000ms"), 2, Duration.from("2s"))));
+             assertRows(execute("UPDATE %s SET v = 6 WHERE k = 1 IF m != {1: 1s}"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, map(1, Duration.from("1000ms"), 2, Duration.from("2s")), 6));
+ 
+             assertRows(execute("UPDATE %s SET v = 5 WHERE k = 1 IF m IN ({1: 1s}, {1: 5s})"), row(false, map(1, Duration.from("1000ms"), 2, Duration.from("2s"))));
+             assertRows(execute("UPDATE %s SET m = {1: 5s, 2: 10s} WHERE k = 1 IF m IN ({1: 1s}, {1: 1s, 2: 2s})"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, map(1, Duration.from("5s"), 2, Duration.from("10s")), 6));
+ 
+             assertInvalidMessage("Slice conditions are not supported on durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF m[1] > 1s");
+             assertInvalidMessage("Slice conditions are not supported on durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF m[1] >= 1s");
+             assertInvalidMessage("Slice conditions are not supported on durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF m[1] <= 1s");
+             assertInvalidMessage("Slice conditions are not supported on durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF m[1] < 1s");
+ 
+             assertRows(execute("UPDATE %s SET v = 4 WHERE k = 1 IF m[1] = 2s"), row(false, map(1, Duration.from("5s"), 2, Duration.from("10s"))));
+             assertRows(execute("UPDATE %s SET v = 3 WHERE k = 1 IF m[1] = 5s"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, map(1, Duration.from("5s"), 2, Duration.from("10s")), 3));
+ 
+             assertRows(execute("UPDATE %s SET m = {1: 10s} WHERE k = 1 IF m[2] != 10s"), row(false, map(1, Duration.from("5s"), 2, Duration.from("10s"))));
+             assertRows(execute("UPDATE %s SET v = 6 WHERE k = 1 IF m[2] != 1s"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, map(1, Duration.from("5s"), 2, Duration.from("10s")), 6));
+ 
+             assertRows(execute("UPDATE %s SET v = 5 WHERE k = 1 IF m[1] IN (2s, 10s)"), row(false, map(1, Duration.from("5s"), 2, Duration.from("10s"))));
+             assertRows(execute("UPDATE %s SET m = {1: 6s, 2: 12s} WHERE k = 1 IF m[1] IN (5s, 10s)"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, map(1, Duration.from("6s"), 2, Duration.from("12s")), 6));
+         }
+     }
+ 
+     @Test
 +    public void testInMarkerWithMaps() throws Throwable
 +    {
 +        for (boolean frozen : new boolean[] {false, true})
 +        {
 +            createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, m %s)",
 +                                      frozen
 +                                      ? "frozen<map<text, text>>"
 +                                      : "map<text, text>"));
 +
 +            execute("INSERT INTO %s (k, m) VALUES (0, {'foo' : 'bar'})");
 +
 +            // Does not apply
 +            assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m IN (?, ?)", map("foo", "foobar"), map("bar", "foobar")),
 +                       row(false, map("foo", "bar")));
 +            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m IN (?, ?)", map("foo", "foobar"), null),
 +                       row(false, map("foo", "bar")));
 +            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m IN (?, ?)", map("foo", "foobar"), unset()),
 +                       row(false, map("foo", "bar")));
 +            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m IN (?, ?)", null, null),
 +                       row(false, map("foo", "bar")));
 +            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m IN ?", list(map("foo", "foobar"), map("bar", "foobar"))),
 +                       row(false, map("foo", "bar")));
 +            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN ?", "foo", list("foo", "foobar")),
 +                       row(false, map("foo", "bar")));
 +            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN (?, ?)", "foo", "foo", "foobar"),
 +                       row(false, map("foo", "bar")));
 +            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN (?, ?)", "foo", "foo", null),
 +                       row(false, map("foo", "bar")));
 +            assertRows(execute("UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN (?, ?)", "foo", "foo", unset()),
 +                       row(false, map("foo", "bar")));
 +
 +            // Does apply
 +            assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m IN (?, ?)", map("foo", "foobar"), map("foo", "bar")),
 +                       row(true));
 +            assertRows(execute("UPDATE %s SET m = {'foo' : 'bar'} WHERE k = 0 IF m IN (?, ?, ?)", map("bar", "foobar"), null, map("foo", "foobar")),
 +                       row(true));
 +            assertRows(execute("UPDATE %s SET m = {'foo' : 'bar'} WHERE k = 0 IF m IN (?, ?, ?)", map("bar", "foobar"), unset(), map("foo", "bar")),
 +                       row(true));
 +            assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m IN ?", list(map("foo", "bar"), map("bar", "foobar"))),
 +                       row(true));
 +            assertRows(execute("UPDATE %s SET m = {'foo' : 'bar'} WHERE k = 0 IF m[?] IN ?", "foo", list("bar", "foobar")),
 +                       row(true));
 +            assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN (?, ?, ?)", "foo", "bar", null, "foobar"),
 +                       row(true));
 +            assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN (?, ?, ?)", "foo", "bar", unset(), "foobar"),
 +                       row(true));
 +
 +            assertInvalidMessage("Invalid null list in IN condition",
 +                                 "UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m IN ?", (List<ByteBuffer>) null);
 +            assertInvalidMessage("Invalid 'unset' value in condition",
 +                                 "UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m IN ?", unset());
 +            assertInvalidMessage("Invalid 'unset' value in condition",
 +                                 "UPDATE %s SET  m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN ?", "foo", unset());
 +        }
 +    }
++
++    @Test
+     public void testConditionalOnDurationWithinUdts() throws Throwable
+     {
+         String udt = createType("CREATE TYPE %s (i int, d duration)");
+ 
+         for (Boolean frozen : new Boolean[]{Boolean.FALSE, Boolean.TRUE})
+         {
+             udt = String.format(frozen ? "frozen<%s>" : "%s", udt);
+ 
+             createTable("CREATE TABLE %s (k int PRIMARY KEY, v int, u " + udt + " )");
+ 
+             assertInvalidMessage("Slice conditions are not supported on UDTs containing durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF u > {i: 1, d: 2s}");
+             assertInvalidMessage("Slice conditions are not supported on UDTs containing durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF u >= {i: 1, d: 2s}");
+             assertInvalidMessage("Slice conditions are not supported on UDTs containing durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF u <= {i: 1, d: 2s}");
+             assertInvalidMessage("Slice conditions are not supported on UDTs containing durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF u < {i: 1, d: 2s}");
+ 
+             execute("INSERT INTO %s (k, v, u) VALUES (1, 1, {i:1, d:2s})");
+ 
+             assertRows(execute("UPDATE %s SET v = 4 WHERE k = 1 IF u = {i: 2, d: 2s}"), row(false, userType("i", 1, "d", Duration.from("2s"))));
+             assertRows(execute("UPDATE %s SET v = 3 WHERE k = 1 IF u = {i: 1, d: 2s}"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, userType("i", 1, "d", Duration.from("2s")), 3));
+ 
+             assertRows(execute("UPDATE %s SET u = {i: 1, d: 10s} WHERE k = 1 IF u != {i: 1, d: 2s}"), row(false, userType("i", 1, "d", Duration.from("2s"))));
+             assertRows(execute("UPDATE %s SET v = 6 WHERE k = 1 IF u != {i: 1, d: 1s}"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, userType("i", 1, "d", Duration.from("2s")), 6));
+ 
+             assertRows(execute("UPDATE %s SET v = 5 WHERE k = 1 IF u IN ({i: 1, d: 1s}, {i: 1, d: 5s})"), row(false, userType("i", 1, "d", Duration.from("2s"))));
+             assertRows(execute("UPDATE %s SET u = {i: 1, d: 10s} WHERE k = 1 IF u IN ({i: 1, d: 1s}, {i: 1, d: 2s})"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, userType("i", 1, "d", Duration.from("10s")), 6));
+ 
+             assertInvalidMessage("Slice conditions are not supported on durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF u.d > 1s");
+             assertInvalidMessage("Slice conditions are not supported on durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF u.d >= 1s");
+             assertInvalidMessage("Slice conditions are not supported on durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF u.d <= 1s");
+             assertInvalidMessage("Slice conditions are not supported on durations",
+                                  "UPDATE %s SET v = 3 WHERE k = 0 IF u.d < 1s");
+ 
+             assertRows(execute("UPDATE %s SET v = 4 WHERE k = 1 IF u.d = 2s"), row(false, userType("i", 1, "d", Duration.from("10s"))));
+             assertRows(execute("UPDATE %s SET v = 3 WHERE k = 1 IF u.d = 10s"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, userType("i", 1, "d", Duration.from("10s")), 3));
+ 
+             assertRows(execute("UPDATE %s SET u = {i: 1, d: 10s} WHERE k = 1 IF u.d != 10s"), row(false, userType("i", 1, "d", Duration.from("10s"))));
+             assertRows(execute("UPDATE %s SET v = 6 WHERE k = 1 IF u.d != 1s"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, userType("i", 1, "d", Duration.from("10s")), 6));
+ 
+             assertRows(execute("UPDATE %s SET v = 5 WHERE k = 1 IF u.d IN (2s, 5s)"), row(false, userType("i", 1, "d", Duration.from("10s"))));
+             assertRows(execute("UPDATE %s SET u = {i: 6, d: 12s} WHERE k = 1 IF u.d IN (5s, 10s)"), row(true));
+ 
+             assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, userType("i", 6, "d", Duration.from("12s")), 6));
+         }
+     }
+ 
+     @Test
+     public void testConditionalOnDurationWithinTuples() throws Throwable
+     {
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, v int, u tuple<int, duration> )");
+ 
+         assertInvalidMessage("Slice conditions are not supported on tuples containing durations",
+                              "UPDATE %s SET v = 3 WHERE k = 0 IF u > (1, 2s)");
+         assertInvalidMessage("Slice conditions are not supported on tuples containing durations",
+                              "UPDATE %s SET v = 3 WHERE k = 0 IF u >= (1, 2s)");
+         assertInvalidMessage("Slice conditions are not supported on tuples containing durations",
+                              "UPDATE %s SET v = 3 WHERE k = 0 IF u <= (1, 2s)");
+         assertInvalidMessage("Slice conditions are not supported on tuples containing durations",
+                              "UPDATE %s SET v = 3 WHERE k = 0 IF u < (1, 2s)");
+ 
+         execute("INSERT INTO %s (k, v, u) VALUES (1, 1, (1, 2s))");
+ 
+         assertRows(execute("UPDATE %s SET v = 4 WHERE k = 1 IF u = (2, 2s)"), row(false, tuple(1, Duration.from("2s"))));
+         assertRows(execute("UPDATE %s SET v = 3 WHERE k = 1 IF u = (1, 2s)"), row(true));
+ 
+         assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, tuple(1, Duration.from("2s")), 3));
+ 
+         assertRows(execute("UPDATE %s SET u = (1, 10s) WHERE k = 1 IF u != (1, 2s)"), row(false, tuple(1, Duration.from("2s"))));
+         assertRows(execute("UPDATE %s SET v = 6 WHERE k = 1 IF u != (1, 1s)"), row(true));
+ 
+         assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, tuple(1, Duration.from("2s")), 6));
+ 
+         assertRows(execute("UPDATE %s SET v = 5 WHERE k = 1 IF u IN ((1, 1s), (1, 5s))"), row(false, tuple(1, Duration.from("2s"))));
+         assertRows(execute("UPDATE %s SET u = (1, 10s) WHERE k = 1 IF u IN ((1, 1s), (1, 2s))"), row(true));
+ 
+         assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, tuple(1, Duration.from("10s")), 6));
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cd29d44d/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
index 23a5a45,f1fcfc3..cb34d86
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
@@@ -4480,12 -4481,166 +4481,176 @@@ public class SelectTest extends CQLTest
      }
  
      @Test
 +    public void testWithDistinctAndJsonAsColumnName() throws Throwable
 +    {
 +        createTable("CREATE TABLE %s (distinct int, json int, value int, PRIMARY KEY(distinct, json))");
 +        execute("INSERT INTO %s (distinct, json, value) VALUES (0, 0, 0)");
 +
 +        assertRows(execute("SELECT distinct, json FROM %s"), row(0, 0));
 +        assertRows(execute("SELECT distinct distinct FROM %s"), row(0));
 +    }
++
++    @Test
+     public void testFilteringOnDurationColumn() throws Throwable
+     {
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, d duration)");
+         execute("INSERT INTO %s (k, d) VALUES (0, 1s)");
+         execute("INSERT INTO %s (k, d) VALUES (1, 2s)");
+         execute("INSERT INTO %s (k, d) VALUES (2, 1s)");
+ 
+         assertRows(execute("SELECT * FROM %s WHERE d=1s ALLOW FILTERING"),
+                    row(0, Duration.from("1s")),
+                    row(2, Duration.from("1s")));
+ 
+         assertInvalidMessage("IN predicates on non-primary-key columns (d) is not yet supported",
+                              "SELECT * FROM %s WHERE d IN (1s, 2s) ALLOW FILTERING");
+ 
+         assertInvalidMessage("Slice restrictions are not supported on duration columns",
+                              "SELECT * FROM %s WHERE d > 1s ALLOW FILTERING");
+ 
+         assertInvalidMessage("Slice restrictions are not supported on duration columns",
+                              "SELECT * FROM %s WHERE d >= 1s ALLOW FILTERING");
+ 
+         assertInvalidMessage("Slice restrictions are not supported on duration columns",
+                              "SELECT * FROM %s WHERE d <= 1s ALLOW FILTERING");
+ 
+         assertInvalidMessage("Slice restrictions are not supported on duration columns",
+                              "SELECT * FROM %s WHERE d < 1s ALLOW FILTERING");
+     }
+ 
+     @Test
+     public void testFilteringOnListContainingDurations() throws Throwable
+     {
+         for (Boolean frozen : new Boolean[]{Boolean.FALSE, Boolean.TRUE})
+         {
+             String listType = String.format(frozen ? "frozen<%s>" : "%s", "list<duration>");
+ 
+             createTable("CREATE TABLE %s (k int PRIMARY KEY, l " + listType + ")");
+             execute("INSERT INTO %s (k, l) VALUES (0, [1s, 2s])");
+             execute("INSERT INTO %s (k, l) VALUES (1, [2s, 3s])");
+             execute("INSERT INTO %s (k, l) VALUES (2, [1s, 3s])");
+ 
+             if (frozen)
+                 assertRows(execute("SELECT * FROM %s WHERE l = [1s, 2s] ALLOW FILTERING"),
+                            row(0, list(Duration.from("1s"), Duration.from("2s"))));
+ 
+             assertInvalidMessage("IN predicates on non-primary-key columns (l) is not yet supported",
+                                  "SELECT * FROM %s WHERE l IN ([1s, 2s], [2s, 3s]) ALLOW FILTERING");
+ 
+             assertInvalidMessage("Slice restrictions are not supported on collections containing durations",
+                                  "SELECT * FROM %s WHERE l > [2s, 3s] ALLOW FILTERING");
+ 
+             assertInvalidMessage("Slice restrictions are not supported on collections containing durations",
+                                  "SELECT * FROM %s WHERE l >= [2s, 3s] ALLOW FILTERING");
+ 
+             assertInvalidMessage("Slice restrictions are not supported on collections containing durations",
+                                  "SELECT * FROM %s WHERE l <= [2s, 3s] ALLOW FILTERING");
+ 
+             assertInvalidMessage("Slice restrictions are not supported on collections containing durations",
+                                  "SELECT * FROM %s WHERE l < [2s, 3s] ALLOW FILTERING");
+ 
+             assertRows(execute("SELECT * FROM %s WHERE l CONTAINS 1s ALLOW FILTERING"),
+                        row(0, list(Duration.from("1s"), Duration.from("2s"))),
+                        row(2, list(Duration.from("1s"), Duration.from("3s"))));
+         }
+     }
+ 
+     @Test
+     public void testFilteringOnMapContainingDurations() throws Throwable
+     {
+         for (Boolean frozen : new Boolean[]{Boolean.FALSE, Boolean.TRUE})
+         {
+             String mapType = String.format(frozen ? "frozen<%s>" : "%s", "map<int, duration>");
+ 
+             createTable("CREATE TABLE %s (k int PRIMARY KEY, m " + mapType + ")");
+             execute("INSERT INTO %s (k, m) VALUES (0, {1:1s, 2:2s})");
+             execute("INSERT INTO %s (k, m) VALUES (1, {2:2s, 3:3s})");
+             execute("INSERT INTO %s (k, m) VALUES (2, {1:1s, 3:3s})");
+ 
+             if (frozen)
+                 assertRows(execute("SELECT * FROM %s WHERE m = {1:1s, 2:2s} ALLOW FILTERING"),
+                            row(0, map(1, Duration.from("1s"), 2, Duration.from("2s"))));
+ 
+             assertInvalidMessage("IN predicates on non-primary-key columns (m) is not yet supported",
+                     "SELECT * FROM %s WHERE m IN ({1:1s, 2:2s}, {1:1s, 3:3s}) ALLOW FILTERING");
+ 
+             assertInvalidMessage("Slice restrictions are not supported on collections containing durations",
+                     "SELECT * FROM %s WHERE m > {1:1s, 3:3s} ALLOW FILTERING");
+ 
+             assertInvalidMessage("Slice restrictions are not supported on collections containing durations",
+                     "SELECT * FROM %s WHERE m >= {1:1s, 3:3s} ALLOW FILTERING");
+ 
+             assertInvalidMessage("Slice restrictions are not supported on collections containing durations",
+                     "SELECT * FROM %s WHERE m <= {1:1s, 3:3s} ALLOW FILTERING");
+ 
+             assertInvalidMessage("Slice restrictions are not supported on collections containing durations",
+                     "SELECT * FROM %s WHERE m < {1:1s, 3:3s} ALLOW FILTERING");
+ 
+             assertRows(execute("SELECT * FROM %s WHERE m CONTAINS 1s ALLOW FILTERING"),
+                        row(0, map(1, Duration.from("1s"), 2, Duration.from("2s"))),
+                        row(2, map(1, Duration.from("1s"), 3, Duration.from("3s"))));
+         }
+     }
+ 
+     @Test
+     public void testFilteringOnTupleContainingDurations() throws Throwable
+     {
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, t tuple<int, duration>)");
+         execute("INSERT INTO %s (k, t) VALUES (0, (1, 2s))");
+         execute("INSERT INTO %s (k, t) VALUES (1, (2, 3s))");
+         execute("INSERT INTO %s (k, t) VALUES (2, (1, 3s))");
+ 
+         assertRows(execute("SELECT * FROM %s WHERE t = (1, 2s) ALLOW FILTERING"),
+                    row(0, tuple(1, Duration.from("2s"))));
+ 
+         assertInvalidMessage("IN predicates on non-primary-key columns (t) is not yet supported",
+                 "SELECT * FROM %s WHERE t IN ((1, 2s), (1, 3s)) ALLOW FILTERING");
+ 
+         assertInvalidMessage("Slice restrictions are not supported on tuples containing durations",
+                 "SELECT * FROM %s WHERE t > (1, 2s) ALLOW FILTERING");
+ 
+         assertInvalidMessage("Slice restrictions are not supported on tuples containing durations",
+                 "SELECT * FROM %s WHERE t >= (1, 2s) ALLOW FILTERING");
+ 
+         assertInvalidMessage("Slice restrictions are not supported on tuples containing durations",
+                 "SELECT * FROM %s WHERE t <= (1, 2s) ALLOW FILTERING");
+ 
+         assertInvalidMessage("Slice restrictions are not supported on tuples containing durations",
+                 "SELECT * FROM %s WHERE t < (1, 2s) ALLOW FILTERING");
+     }
+ 
+     @Test
+     public void testFilteringOnUdtContainingDurations() throws Throwable
+     {
+         String udt = createType("CREATE TYPE %s (i int, d duration)");
+ 
+         for (Boolean frozen : new Boolean[]{Boolean.FALSE, Boolean.TRUE})
+         {
+             udt = String.format(frozen ? "frozen<%s>" : "%s", udt);
+ 
+             createTable("CREATE TABLE %s (k int PRIMARY KEY, u " + udt + ")");
+             execute("INSERT INTO %s (k, u) VALUES (0, {i: 1, d:2s})");
+             execute("INSERT INTO %s (k, u) VALUES (1, {i: 2, d:3s})");
+             execute("INSERT INTO %s (k, u) VALUES (2, {i: 1, d:3s})");
+ 
+             if (frozen)
+                 assertRows(execute("SELECT * FROM %s WHERE u = {i: 1, d:2s} ALLOW FILTERING"),
+                            row(0, userType("i", 1, "d", Duration.from("2s"))));
+ 
+             assertInvalidMessage("IN predicates on non-primary-key columns (u) is not yet supported",
+                     "SELECT * FROM %s WHERE u IN ({i: 2, d:3s}, {i: 1, d:3s}) ALLOW FILTERING");
+ 
+             assertInvalidMessage("Slice restrictions are not supported on UDTs containing durations",
+                     "SELECT * FROM %s WHERE u > {i: 1, d:3s} ALLOW FILTERING");
+ 
+             assertInvalidMessage("Slice restrictions are not supported on UDTs containing durations",
+                     "SELECT * FROM %s WHERE u >= {i: 1, d:3s} ALLOW FILTERING");
+ 
+             assertInvalidMessage("Slice restrictions are not supported on UDTs containing durations",
+                     "SELECT * FROM %s WHERE u <= {i: 1, d:3s} ALLOW FILTERING");
+ 
+             assertInvalidMessage("Slice restrictions are not supported on UDTs containing durations",
+                     "SELECT * FROM %s WHERE u < {i: 1, d:3s} ALLOW FILTERING");
+         }
+     }
  }


Mime
View raw message