You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ms...@apache.org on 2016/08/16 22:42:02 UTC
[01/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0'
into cassandra-3.9
Repository: cassandra
Updated Branches:
refs/heads/cassandra-3.8 b188e0826 -> c1e9ad8cd
Merge branch 'cassandra-3.0' into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/02ebf875
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/02ebf875
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/02ebf875
Branch: refs/heads/cassandra-3.8
Commit: 02ebf875b1a07c79e18bc1f21878c51ed67cb9c4
Parents: ae816cb 676b6a8
Author: Aleksey Yeschenko <al...@apache.org>
Authored: Mon Aug 8 18:41:23 2016 +0100
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Mon Aug 8 18:41:23 2016 +0100
----------------------------------------------------------------------
----------------------------------------------------------------------
[12/50] [abbrv] cassandra git commit: Make sure compaction stats are
updated when compaction is interrupted
Posted by ms...@apache.org.
Make sure compaction stats are updated when compaction is interrupted
patch by Stefania Alborghetti; reviewed by Marcus Eriksson for CASSANDRA-12100
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/05483a96
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/05483a96
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/05483a96
Branch: refs/heads/cassandra-3.8
Commit: 05483a962c64c350315fc738c697980b22361cc3
Parents: 2691c9e
Author: Stefania Alborghetti <st...@datastax.com>
Authored: Thu Aug 4 14:20:38 2016 +0800
Committer: Stefania Alborghetti <st...@datastax.com>
Committed: Thu Aug 11 09:09:57 2016 +0800
----------------------------------------------------------------------
CHANGES.txt | 1 +
src/java/org/apache/cassandra/db/compaction/CompactionTask.java | 5 +++--
2 files changed, 4 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/05483a96/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index e1bc9f3..bd3bb75 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
3.0.9
+ * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
* Fix potential bad messaging service message for paged range reads
within mixed-version 3.x clusters (CASSANDRA-12249)
* Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/05483a96/src/java/org/apache/cassandra/db/compaction/CompactionTask.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/compaction/CompactionTask.java b/src/java/org/apache/cassandra/db/compaction/CompactionTask.java
index 9a7aa98..7e4ed41 100644
--- a/src/java/org/apache/cassandra/db/compaction/CompactionTask.java
+++ b/src/java/org/apache/cassandra/db/compaction/CompactionTask.java
@@ -164,13 +164,14 @@ public class CompactionTask extends AbstractCompactionTask
AbstractCompactionStrategy.ScannerList scanners = strategy.getScanners(actuallyCompact);
CompactionIterator ci = new CompactionIterator(compactionType, scanners.scanners, controller, nowInSec, taskId))
{
- if (collector != null)
- collector.beginCompaction(ci);
long lastCheckObsoletion = start;
if (!controller.cfs.getCompactionStrategyManager().isActive)
throw new CompactionInterruptedException(ci.getCompactionInfo());
+ if (collector != null)
+ collector.beginCompaction(ci);
+
try (CompactionAwareWriter writer = getCompactionAwareWriter(cfs, getDirectories(), transaction, actuallyCompact))
{
estimatedKeys = writer.estimatedKeys();
[48/50] [abbrv] cassandra git commit: Merge branch cassandra-2.2 into
cassandra-3.0
Posted by ms...@apache.org.
Merge branch cassandra-2.2 into cassandra-3.0
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/bb56193a
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/bb56193a
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/bb56193a
Branch: refs/heads/cassandra-3.8
Commit: bb56193a3f2ac865efb18c9d6944f2927f667771
Parents: 6528fbf 9583b6b
Author: Benjamin Lerer <b....@gmail.com>
Authored: Tue Aug 16 15:29:42 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Tue Aug 16 15:34:35 2016 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
NEWS.txt | 10 +
.../cassandra/db/compaction/Scrubber.java | 169 ++++-
.../cassandra/db/marshal/ReversedType.java | 10 -
.../db/partitions/AbstractBTreePartition.java | 7 +-
.../db/partitions/ImmutableBTreePartition.java | 34 +-
.../db/rows/UnfilteredRowIterators.java | 28 +
.../org/apache/cassandra/cql3/CQLTester.java | 18 +
.../validation/entities/SecondaryIndexTest.java | 131 ++++
.../cql3/validation/operations/DeleteTest.java | 113 +++
.../operations/SelectOrderByTest.java | 719 ++++++++++---------
.../cql3/validation/operations/SelectTest.java | 357 ++++++++-
.../cassandra/db/marshal/ReversedTypeTest.java | 4 +-
13 files changed, 1210 insertions(+), 391 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index a202755,c421398..7605952
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -49,9 -14,12 +49,10 @@@ Merged from 2.2
* Fixed cqlshlib.test.remove_test_db (CASSANDRA-12214)
* Synchronize ThriftServer::stop() (CASSANDRA-12105)
* Use dedicated thread for JMX notifications (CASSANDRA-12146)
- * NPE when trying to remove purgable tombstones from result (CASSANDRA-12143)
* Improve streaming synchronization and fault tolerance (CASSANDRA-11414)
* MemoryUtil.getShort() should return an unsigned short also for architectures not supporting unaligned memory accesses (CASSANDRA-11973)
- * Don't write shadowed range tombstone (CASSANDRA-12030)
Merged from 2.1:
+ * Fix queries with empty ByteBuffer values in clustering column restrictions (CASSANDRA-12127)
* Disable passing control to post-flush after flush failure to prevent data loss (CASSANDRA-11828)
* Allow STCS-in-L0 compactions to reduce scope with LCS (CASSANDRA-12040)
* cannot use cql since upgrading python to 2.7.11+ (CASSANDRA-11850)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/NEWS.txt
----------------------------------------------------------------------
diff --cc NEWS.txt
index 6b8dfd3,0a3ab36..0bd3920
--- a/NEWS.txt
+++ b/NEWS.txt
@@@ -13,55 -13,17 +13,65 @@@ restore snapshots created with the prev
'sstableloader' tool. You can upgrade the file format of your snapshots
using the provided 'sstableupgrade' tool.
-2.2.8
++3.0.9
+ =====
+
+ Upgrading
+ ---------
- - The ReversedType behaviour has been corrected for clustering columns of
- BYTES type containing empty value. Scrub should be run on the existing
- SSTables containing a descending clustering column of BYTES type to correct
- their ordering. See CASSANDRA-12127 for more details.
++ - The ReversedType behaviour has been corrected for clustering columns of
++ BYTES type containing empty value. Scrub should be run on the existing
++ SSTables containing a descending clustering column of BYTES type to correct
++ their ordering. See CASSANDRA-12127 for more details.
+
-2.2.7
+3.0.8
+=====
+
+Upgrading
+---------
+ - Ec2MultiRegionSnitch will no longer automatically set broadcast_rpc_address
+ to the public instance IP if this property is defined on cassandra.yaml.
+
+3.0.7
+=====
+
+Upgrading
+---------
+ - A maximum size for SSTables values has been introduced, to prevent out of memory
+ exceptions when reading corrupt SSTables. This maximum size can be set via
+ max_value_size_in_mb in cassandra.yaml. The default is 256MB, which matches the default
+ value of native_transport_max_frame_size_in_mb. SSTables will be considered corrupt if
+ they contain values whose size exceeds this limit. See CASSANDRA-9530 for more details.
+
+Deprecation
+-----------
+ - DateTieredCompactionStrategy has been deprecated - new tables should use
+ TimeWindowCompactionStrategy. Note that migrating an existing DTCS-table to TWCS might
+ cause increased compaction load for a while after the migration so make sure you run
+ tests before migrating. Read CASSANDRA-9666 for background on this.
+
+New features
+------------
+ - TimeWindowCompactionStrategy has been added. This has proven to be a better approach
+ to time series compaction and new tables should use this instead of DTCS. See
+ CASSANDRA-9666 for details.
+
+3.0.6
+=====
+
+New features
+------------
+ - JSON timestamps are now in UTC and contain the timezone information, see
+ CASSANDRA-11137 for more details.
+
+3.0.5
+=====
+
+Upgrading
+---------
+ - Nothing specific to this release, but please see previous versions upgrading section,
+ especially if you are upgrading from 2.2.
+
+3.0.4
=====
New features
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/src/java/org/apache/cassandra/db/compaction/Scrubber.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/compaction/Scrubber.java
index 539c4c7,99ee62e..c010891
--- a/src/java/org/apache/cassandra/db/compaction/Scrubber.java
+++ b/src/java/org/apache/cassandra/db/compaction/Scrubber.java
@@@ -23,11 -23,11 +23,12 @@@ import java.util.*
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
-import com.google.common.collect.AbstractIterator;
++import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.lifecycle.LifecycleTransaction;
-import org.apache.cassandra.db.columniterator.OnDiskAtomIterator;
+import org.apache.cassandra.db.rows.*;
+import org.apache.cassandra.db.partitions.*;
import org.apache.cassandra.io.sstable.*;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.io.sstable.format.SSTableWriter;
@@@ -327,6 -312,32 +302,38 @@@ public class Scrubber implements Closea
}
}
+ @SuppressWarnings("resource")
+ private boolean tryAppend(DecoratedKey prevKey, DecoratedKey key, SSTableRewriter writer)
+ {
- // OrderCheckerIterator will check, at iteration time, that the cells are in the proper order. If it detects
- // that one cell is out of order, it will stop returning them. The remaining cells will be sorted and added
- // to the outOfOrderRows that will be later written to a new SSTable.
- OrderCheckerIterator atoms = new OrderCheckerIterator(new SSTableIdentityIterator(sstable, dataFile, key, checkData),
- cfs.metadata.comparator.onDiskAtomComparator());
- if (prevKey != null && prevKey.compareTo(key) > 0)
++ // OrderCheckerIterator will check, at iteration time, that the rows are in the proper order. If it detects
++ // that one row is out of order, it will stop returning them. The remaining rows will be sorted and added
++ // to the outOfOrder set that will be later written to a new SSTable.
++ OrderCheckerIterator sstableIterator = new OrderCheckerIterator(new RowMergingSSTableIterator(sstable, dataFile, key),
++ cfs.metadata.comparator);
++
++ try (UnfilteredRowIterator iterator = withValidation(sstableIterator, dataFile.getPath()))
+ {
- saveOutOfOrderRow(prevKey, key, atoms);
- return false;
- }
++ if (prevKey != null && prevKey.compareTo(key) > 0)
++ {
++ saveOutOfOrderRow(prevKey, key, iterator);
++ return false;
++ }
+
- AbstractCompactedRow compactedRow = new LazilyCompactedRow(controller, Collections.singletonList(atoms));
- if (writer.tryAppend(compactedRow) == null)
- emptyRows++;
- else
- goodRows++;
++ if (writer.tryAppend(iterator) == null)
++ emptyRows++;
++ else
++ goodRows++;
++ }
+
- if (atoms.hasOutOfOrderCells())
- saveOutOfOrderRow(key, atoms);
++ if (sstableIterator.hasRowsOutOfOrder())
++ {
++ outputHandler.warn(String.format("Out of order rows found in partition: %s", key));
++ outOfOrder.add(sstableIterator.getRowsOutOfOrder());
++ }
+
+ return true;
+ }
+
private void updateIndexKey()
{
currentIndexKey = nextIndexKey;
@@@ -469,44 -523,88 +476,146 @@@
}
/**
+ * During 2.x migration, under some circumstances rows might have gotten duplicated.
+ * Merging iterator merges rows with same clustering.
+ *
+ * For more details, refer to CASSANDRA-12144.
+ */
+ private static class RowMergingSSTableIterator extends SSTableIdentityIterator
+ {
+ RowMergingSSTableIterator(SSTableReader sstable, RandomAccessReader file, DecoratedKey key)
+ {
+ super(sstable, file, key);
+ }
+
+ @Override
+ protected Unfiltered doCompute()
+ {
+ if (!iterator.hasNext())
+ return endOfData();
+
+ Unfiltered next = iterator.next();
+ if (!next.isRow())
+ return next;
+
+ while (iterator.hasNext())
+ {
+ Unfiltered peek = iterator.peek();
+ // If there was a duplicate row, merge it.
+ if (next.clustering().equals(peek.clustering()) && peek.isRow())
+ {
+ iterator.next(); // Make sure that the peeked item was consumed.
+ next = Rows.merge((Row) next, (Row) peek, FBUtilities.nowInSeconds());
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return next;
+ }
+ }
++
++ /**
+ * In some case like CASSANDRA-12127 the cells might have been stored in the wrong order. This decorator check the
+ * cells order and collect the out of order cells to correct the problem.
+ */
- private static final class OrderCheckerIterator extends AbstractIterator<OnDiskAtom> implements OnDiskAtomIterator
++ private static final class OrderCheckerIterator extends AbstractIterator<Unfiltered> implements UnfilteredRowIterator
+ {
+ /**
+ * The decorated iterator.
+ */
- private final OnDiskAtomIterator iterator;
++ private final UnfilteredRowIterator iterator;
+
- /**
- * The atom comparator.
- */
- private final Comparator<OnDiskAtom> comparator;
++ private final ClusteringComparator comparator;
+
- /**
- * The Column family containing the cells which are out of order.
- */
- private ColumnFamily outOfOrderCells;
++ private Unfiltered previous;
+
+ /**
- * The previous atom returned
++ * The partition containing the rows which are out of order.
+ */
- private OnDiskAtom previous;
++ private Partition rowsOutOfOrder;
+
- public OrderCheckerIterator(OnDiskAtomIterator iterator, Comparator<OnDiskAtom> comparator)
++ public OrderCheckerIterator(UnfilteredRowIterator iterator, ClusteringComparator comparator)
+ {
+ this.iterator = iterator;
+ this.comparator = comparator;
+ }
+
- public ColumnFamily getColumnFamily()
++ public CFMetaData metadata()
+ {
- return iterator.getColumnFamily();
++ return iterator.metadata();
+ }
+
- public DecoratedKey getKey()
++ public boolean isReverseOrder()
+ {
- return iterator.getKey();
++ return iterator.isReverseOrder();
+ }
+
- public void close() throws IOException
++ public PartitionColumns columns()
+ {
- iterator.close();
++ return iterator.columns();
++ }
++
++ public DecoratedKey partitionKey()
++ {
++ return iterator.partitionKey();
++ }
++
++ public Row staticRow()
++ {
++ return iterator.staticRow();
+ }
+
+ @Override
- protected OnDiskAtom computeNext()
++ public boolean isEmpty()
++ {
++ return iterator.isEmpty();
++ }
++
++ public void close()
++ {
++ iterator.close();
++ }
++
++ public DeletionTime partitionLevelDeletion()
++ {
++ return iterator.partitionLevelDeletion();
++ }
++
++ public EncodingStats stats()
++ {
++ return iterator.stats();
++ }
++
++ public boolean hasRowsOutOfOrder()
++ {
++ return rowsOutOfOrder != null;
++ }
++
++ public Partition getRowsOutOfOrder()
++ {
++ return rowsOutOfOrder;
++ }
++
++ protected Unfiltered computeNext()
+ {
+ if (!iterator.hasNext())
+ return endOfData();
+
- OnDiskAtom next = iterator.next();
++ Unfiltered next = iterator.next();
+
- // If we detect that some cells are out of order we will store and sort the remaining once to insert them
++ // If we detect that some rows are out of order we will store and sort the remaining ones to insert them
+ // in a separate SSTable.
+ if (previous != null && comparator.compare(next, previous) < 0)
+ {
- outOfOrderCells = collectOutOfOrderCells(next, iterator);
++ rowsOutOfOrder = ImmutableBTreePartition.create(UnfilteredRowIterators.concat(next, iterator), false);
+ return endOfData();
+ }
+ previous = next;
+ return next;
+ }
+
- public boolean hasOutOfOrderCells()
- {
- return outOfOrderCells != null;
- }
-
- public ColumnFamily getOutOfOrderCells()
- {
- return outOfOrderCells;
- }
-
- private static ColumnFamily collectOutOfOrderCells(OnDiskAtom atom, OnDiskAtomIterator iterator)
- {
- ColumnFamily cf = iterator.getColumnFamily().cloneMeShallow(ArrayBackedSortedColumns.factory, false);
- cf.addAtom(atom);
- while (iterator.hasNext())
- cf.addAtom(iterator.next());
- return cf;
- }
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/src/java/org/apache/cassandra/db/marshal/ReversedType.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/marshal/ReversedType.java
index 02320c7,19bee5f..82a1895
--- a/src/java/org/apache/cassandra/db/marshal/ReversedType.java
+++ b/src/java/org/apache/cassandra/db/marshal/ReversedType.java
@@@ -66,18 -65,8 +66,8 @@@ public class ReversedType<T> extends Ab
return baseType.isEmptyValueMeaningless();
}
- public int compare(ByteBuffer o1, ByteBuffer o2)
+ public int compareCustom(ByteBuffer o1, ByteBuffer o2)
{
- // An empty byte buffer is always smaller
- if (o1.remaining() == 0)
- {
- return o2.remaining() == 0 ? 0 : -1;
- }
- if (o2.remaining() == 0)
- {
- return 1;
- }
-
return baseType.compare(o2, o1);
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
index 1fa3324,0000000..c63acc2
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
+++ b/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
@@@ -1,408 -1,0 +1,413 @@@
+/*
+* 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.db.partitions;
+
+import java.util.Iterator;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.filter.ColumnFilter;
+import org.apache.cassandra.db.rows.*;
+import org.apache.cassandra.utils.SearchIterator;
+import org.apache.cassandra.utils.btree.BTree;
+import org.apache.cassandra.utils.btree.BTreeSearchIterator;
+
+import static org.apache.cassandra.utils.btree.BTree.Dir.desc;
+
+public abstract class AbstractBTreePartition implements Partition, Iterable<Row>
+{
+ protected static final Holder EMPTY = new Holder(PartitionColumns.NONE, BTree.empty(), DeletionInfo.LIVE, Rows.EMPTY_STATIC_ROW, EncodingStats.NO_STATS);
+
+ protected final CFMetaData metadata;
+ protected final DecoratedKey partitionKey;
+
+ protected abstract Holder holder();
+ protected abstract boolean canHaveShadowedData();
+
+ protected AbstractBTreePartition(CFMetaData metadata, DecoratedKey partitionKey)
+ {
+ this.metadata = metadata;
+ this.partitionKey = partitionKey;
+ }
+
+ protected static final class Holder
+ {
+ final PartitionColumns columns;
+ final DeletionInfo deletionInfo;
+ // the btree of rows
+ final Object[] tree;
+ final Row staticRow;
+ final EncodingStats stats;
+
+ Holder(PartitionColumns columns, Object[] tree, DeletionInfo deletionInfo, Row staticRow, EncodingStats stats)
+ {
+ this.columns = columns;
+ this.tree = tree;
+ this.deletionInfo = deletionInfo;
+ this.staticRow = staticRow == null ? Rows.EMPTY_STATIC_ROW : staticRow;
+ this.stats = stats;
+ }
+ }
+
+ public DeletionInfo deletionInfo()
+ {
+ return holder().deletionInfo;
+ }
+
+ public Row staticRow()
+ {
+ return holder().staticRow;
+ }
+
+ public boolean isEmpty()
+ {
+ Holder holder = holder();
+ return holder.deletionInfo.isLive() && BTree.isEmpty(holder.tree) && holder.staticRow.isEmpty();
+ }
+
+ public boolean hasRows()
+ {
+ Holder holder = holder();
+ return !BTree.isEmpty(holder.tree);
+ }
+
+ public CFMetaData metadata()
+ {
+ return metadata;
+ }
+
+ public DecoratedKey partitionKey()
+ {
+ return partitionKey;
+ }
+
+ public DeletionTime partitionLevelDeletion()
+ {
+ return holder().deletionInfo.getPartitionDeletion();
+ }
+
+ public PartitionColumns columns()
+ {
+ return holder().columns;
+ }
+
+ public EncodingStats stats()
+ {
+ return holder().stats;
+ }
+
+ public Row getRow(Clustering clustering)
+ {
+ Row row = searchIterator(ColumnFilter.selection(columns()), false).next(clustering);
+ // Note that for statics, this will never return null, this will return an empty row. However,
+ // it's more consistent for this method to return null if we don't really have a static row.
+ return row == null || (clustering == Clustering.STATIC_CLUSTERING && row.isEmpty()) ? null : row;
+ }
+
+ private Row staticRow(Holder current, ColumnFilter columns, boolean setActiveDeletionToRow)
+ {
+ DeletionTime partitionDeletion = current.deletionInfo.getPartitionDeletion();
+ if (columns.fetchedColumns().statics.isEmpty() || (current.staticRow.isEmpty() && partitionDeletion.isLive()))
+ return Rows.EMPTY_STATIC_ROW;
+
+ Row row = current.staticRow.filter(columns, partitionDeletion, setActiveDeletionToRow, metadata);
+ return row == null ? Rows.EMPTY_STATIC_ROW : row;
+ }
+
+ public SearchIterator<Clustering, Row> searchIterator(final ColumnFilter columns, final boolean reversed)
+ {
+ // TODO: we could optimize comparison for "NativeRow" � la #6755
+ final Holder current = holder();
+ return new SearchIterator<Clustering, Row>()
+ {
+ private final SearchIterator<Clustering, Row> rawIter = new BTreeSearchIterator<>(current.tree, metadata.comparator, desc(reversed));
+ private final DeletionTime partitionDeletion = current.deletionInfo.getPartitionDeletion();
+
+ public boolean hasNext()
+ {
+ return rawIter.hasNext();
+ }
+
+ public Row next(Clustering clustering)
+ {
+ if (clustering == Clustering.STATIC_CLUSTERING)
+ return staticRow(current, columns, true);
+
+ Row row = rawIter.next(clustering);
+ RangeTombstone rt = current.deletionInfo.rangeCovering(clustering);
+
+ // A search iterator only return a row, so it doesn't allow to directly account for deletion that should apply to to row
+ // (the partition deletion or the deletion of a range tombstone that covers it). So if needs be, reuse the row deletion
+ // to carry the proper deletion on the row.
+ DeletionTime activeDeletion = partitionDeletion;
+ if (rt != null && rt.deletionTime().supersedes(activeDeletion))
+ activeDeletion = rt.deletionTime();
+
+ if (row == null)
+ return activeDeletion.isLive() ? null : BTreeRow.emptyDeletedRow(clustering, Row.Deletion.regular(activeDeletion));
+
+ return row.filter(columns, activeDeletion, true, metadata);
+ }
+ };
+ }
+
+ public UnfilteredRowIterator unfilteredIterator()
+ {
+ return unfilteredIterator(ColumnFilter.all(metadata()), Slices.ALL, false);
+ }
+
+ public UnfilteredRowIterator unfilteredIterator(ColumnFilter selection, Slices slices, boolean reversed)
+ {
+ return unfilteredIterator(holder(), selection, slices, reversed);
+ }
+
+ public UnfilteredRowIterator unfilteredIterator(Holder current, ColumnFilter selection, Slices slices, boolean reversed)
+ {
+ Row staticRow = staticRow(current, selection, false);
+ if (slices.size() == 0)
+ {
+ DeletionTime partitionDeletion = current.deletionInfo.getPartitionDeletion();
+ return UnfilteredRowIterators.noRowsIterator(metadata, partitionKey, staticRow, partitionDeletion, reversed);
+ }
+
+ return slices.size() == 1
+ ? sliceIterator(selection, slices.get(0), reversed, current, staticRow)
+ : new SlicesIterator(selection, slices, reversed, current, staticRow);
+ }
+
+ private UnfilteredRowIterator sliceIterator(ColumnFilter selection, Slice slice, boolean reversed, Holder current, Row staticRow)
+ {
+ Slice.Bound start = slice.start() == Slice.Bound.BOTTOM ? null : slice.start();
+ Slice.Bound end = slice.end() == Slice.Bound.TOP ? null : slice.end();
+ Iterator<Row> rowIter = BTree.slice(current.tree, metadata.comparator, start, true, end, true, desc(reversed));
+ Iterator<RangeTombstone> deleteIter = current.deletionInfo.rangeIterator(slice, reversed);
+
+ return merge(rowIter, deleteIter, selection, reversed, current, staticRow);
+ }
+
+ private RowAndDeletionMergeIterator merge(Iterator<Row> rowIter, Iterator<RangeTombstone> deleteIter,
+ ColumnFilter selection, boolean reversed, Holder current, Row staticRow)
+ {
+ return new RowAndDeletionMergeIterator(metadata, partitionKey, current.deletionInfo.getPartitionDeletion(),
+ selection, staticRow, reversed, current.stats,
+ rowIter, deleteIter,
+ canHaveShadowedData());
+ }
+
+ private abstract class AbstractIterator extends AbstractUnfilteredRowIterator
+ {
+ final Holder current;
+ final ColumnFilter selection;
+
+ private AbstractIterator(ColumnFilter selection, boolean isReversed)
+ {
+ this(AbstractBTreePartition.this.holder(), selection, isReversed);
+ }
+
+ private AbstractIterator(Holder current, ColumnFilter selection, boolean isReversed)
+ {
+ this(current,
+ AbstractBTreePartition.this.staticRow(current, selection, false),
+ selection, isReversed);
+ }
+
+ private AbstractIterator(Holder current, Row staticRow, ColumnFilter selection, boolean isReversed)
+ {
+ super(AbstractBTreePartition.this.metadata,
+ AbstractBTreePartition.this.partitionKey,
+ current.deletionInfo.getPartitionDeletion(),
+ selection.fetchedColumns(), // non-selected columns will be filtered in subclasses by RowAndDeletionMergeIterator
+ // it would also be more precise to return the intersection of the selection and current.columns,
+ // but its probably not worth spending time on computing that.
+ staticRow,
+ isReversed,
+ current.stats);
+ this.current = current;
+ this.selection = selection;
+ }
+ }
+
+ public class SlicesIterator extends AbstractIterator
+ {
+ private final Slices slices;
+
+ private int idx;
+ private Iterator<Unfiltered> currentSlice;
+
+ private SlicesIterator(ColumnFilter selection,
+ Slices slices,
+ boolean isReversed,
+ Holder current,
+ Row staticRow)
+ {
+ super(current, staticRow, selection, isReversed);
+ this.slices = slices;
+ }
+
+ protected Unfiltered computeNext()
+ {
+ while (true)
+ {
+ if (currentSlice == null)
+ {
+ if (idx >= slices.size())
+ return endOfData();
+
+ int sliceIdx = isReverseOrder ? slices.size() - idx - 1 : idx;
+ currentSlice = sliceIterator(selection, slices.get(sliceIdx), isReverseOrder, current, Rows.EMPTY_STATIC_ROW);
+ idx++;
+ }
+
+ if (currentSlice.hasNext())
+ return currentSlice.next();
+
+ currentSlice = null;
+ }
+ }
+ }
+
+ public class SliceableIterator extends AbstractIterator implements SliceableUnfilteredRowIterator
+ {
+ private Iterator<Unfiltered> iterator;
+
+ protected SliceableIterator(ColumnFilter selection, boolean isReversed)
+ {
+ super(selection, isReversed);
+ }
+
+ protected Unfiltered computeNext()
+ {
+ if (iterator == null)
+ iterator = unfilteredIterator(selection, Slices.ALL, isReverseOrder);
+ if (!iterator.hasNext())
+ return endOfData();
+ return iterator.next();
+ }
+
+ public Iterator<Unfiltered> slice(Slice slice)
+ {
+ return sliceIterator(selection, slice, isReverseOrder, current, staticRow);
+ }
+ }
+
+ public SliceableUnfilteredRowIterator sliceableUnfilteredIterator(ColumnFilter columns, boolean reversed)
+ {
+ return new SliceableIterator(columns, reversed);
+ }
+
+ protected SliceableUnfilteredRowIterator sliceableUnfilteredIterator()
+ {
+ return sliceableUnfilteredIterator(ColumnFilter.all(metadata), false);
+ }
+
+ protected static Holder build(UnfilteredRowIterator iterator, int initialRowCapacity)
+ {
++ return build(iterator, initialRowCapacity, true);
++ }
++
++ protected static Holder build(UnfilteredRowIterator iterator, int initialRowCapacity, boolean ordered)
++ {
+ CFMetaData metadata = iterator.metadata();
+ PartitionColumns columns = iterator.columns();
+ boolean reversed = iterator.isReverseOrder();
+
+ BTree.Builder<Row> builder = BTree.builder(metadata.comparator, initialRowCapacity);
- builder.auto(false);
++ builder.auto(!ordered);
+ MutableDeletionInfo.Builder deletionBuilder = MutableDeletionInfo.builder(iterator.partitionLevelDeletion(), metadata.comparator, reversed);
+
+ while (iterator.hasNext())
+ {
+ Unfiltered unfiltered = iterator.next();
+ if (unfiltered.kind() == Unfiltered.Kind.ROW)
+ builder.add((Row)unfiltered);
+ else
+ deletionBuilder.add((RangeTombstoneMarker)unfiltered);
+ }
+
+ if (reversed)
+ builder.reverse();
+
+ return new Holder(columns, builder.build(), deletionBuilder.build(), iterator.staticRow(), iterator.stats());
+ }
+
+ // Note that when building with a RowIterator, deletion will generally be LIVE, but we allow to pass it nonetheless because PartitionUpdate
+ // passes a MutableDeletionInfo that it mutates later.
+ protected static Holder build(RowIterator rows, DeletionInfo deletion, boolean buildEncodingStats, int initialRowCapacity)
+ {
+ CFMetaData metadata = rows.metadata();
+ PartitionColumns columns = rows.columns();
+ boolean reversed = rows.isReverseOrder();
+
+ BTree.Builder<Row> builder = BTree.builder(metadata.comparator, initialRowCapacity);
+ builder.auto(false);
+ while (rows.hasNext())
+ {
+ Row row = rows.next();
+ builder.add(row);
+ }
+
+ if (reversed)
+ builder.reverse();
+
+ Row staticRow = rows.staticRow();
+ Object[] tree = builder.build();
+ EncodingStats stats = buildEncodingStats ? EncodingStats.Collector.collect(staticRow, BTree.iterator(tree), deletion)
+ : EncodingStats.NO_STATS;
+ return new Holder(columns, tree, deletion, staticRow, stats);
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(String.format("[%s.%s] key=%s columns=%s",
+ metadata.ksName,
+ metadata.cfName,
+ metadata.getKeyValidator().getString(partitionKey().getKey()),
+ columns()));
+
+ if (staticRow() != Rows.EMPTY_STATIC_ROW)
+ sb.append("\n ").append(staticRow().toString(metadata));
+
+ for (Row row : this)
+ sb.append("\n ").append(row.toString(metadata));
+
+ return sb.toString();
+ }
+
+ public int rowCount()
+ {
+ return BTree.size(holder().tree);
+ }
+
+ public Iterator<Row> iterator()
+ {
+ return BTree.<Row>iterator(holder().tree);
+ }
+
+ public Row lastRow()
+ {
+ Object[] tree = holder().tree;
+ if (BTree.isEmpty(tree))
+ return null;
+
+ return BTree.findByIndex(tree, BTree.size(tree) - 1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/src/java/org/apache/cassandra/db/partitions/ImmutableBTreePartition.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/partitions/ImmutableBTreePartition.java
index 9af4bad,0000000..8d96f1e
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/db/partitions/ImmutableBTreePartition.java
+++ b/src/java/org/apache/cassandra/db/partitions/ImmutableBTreePartition.java
@@@ -1,91 -1,0 +1,123 @@@
+/*
+* 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.db.partitions;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.db.DecoratedKey;
+import org.apache.cassandra.db.DeletionInfo;
+import org.apache.cassandra.db.PartitionColumns;
+import org.apache.cassandra.db.rows.*;
+
+public class ImmutableBTreePartition extends AbstractBTreePartition
+{
+
+ protected final Holder holder;
+
+ public ImmutableBTreePartition(CFMetaData metadata,
+ DecoratedKey partitionKey,
+ PartitionColumns columns,
+ Row staticRow,
+ Object[] tree,
+ DeletionInfo deletionInfo,
+ EncodingStats stats)
+ {
+ super(metadata, partitionKey);
+ this.holder = new Holder(columns, tree, deletionInfo, staticRow, stats);
+ }
+
+ protected ImmutableBTreePartition(CFMetaData metadata,
+ DecoratedKey partitionKey,
+ Holder holder)
+ {
+ super(metadata, partitionKey);
+ this.holder = holder;
+ }
+
+ /**
+ * Creates an {@code ImmutableBTreePartition} holding all the data of the provided iterator.
+ *
+ * Warning: Note that this method does not close the provided iterator and it is
+ * up to the caller to do so.
+ *
+ * @param iterator the iterator to gather in memory.
+ * @return the created partition.
+ */
+ public static ImmutableBTreePartition create(UnfilteredRowIterator iterator)
+ {
+ return create(iterator, 16);
+ }
+
+ /**
+ * Creates an {@code ImmutableBTreePartition} holding all the data of the provided iterator.
+ *
+ * Warning: Note that this method does not close the provided iterator and it is
+ * up to the caller to do so.
+ *
+ * @param iterator the iterator to gather in memory.
++ * @param ordered {@code true} if the iterator will return the rows in order, {@code false} otherwise.
++ * @return the created partition.
++ */
++ public static ImmutableBTreePartition create(UnfilteredRowIterator iterator, boolean ordered)
++ {
++ return create(iterator, 16, ordered);
++ }
++
++ /**
++ * Creates an {@code ImmutableBTreePartition} holding all the data of the provided iterator.
++ *
++ * Warning: Note that this method does not close the provided iterator and it is
++ * up to the caller to do so.
++ *
++ * @param iterator the iterator to gather in memory.
+ * @param initialRowCapacity sizing hint (in rows) to use for the created partition. It should ideally
+ * correspond or be a good estimation of the number or rows in {@code iterator}.
+ * @return the created partition.
+ */
+ public static ImmutableBTreePartition create(UnfilteredRowIterator iterator, int initialRowCapacity)
+ {
- return new ImmutableBTreePartition(iterator.metadata(), iterator.partitionKey(), build(iterator, initialRowCapacity));
++ return create(iterator, initialRowCapacity, true);
++ }
++
++ /**
++ * Creates an {@code ImmutableBTreePartition} holding all the data of the provided iterator.
++ *
++ * Warning: Note that this method does not close the provided iterator and it is
++ * up to the caller to do so.
++ *
++ * @param iterator the iterator to gather in memory.
++ * @param initialRowCapacity sizing hint (in rows) to use for the created partition. It should ideally
++ * correspond or be a good estimation of the number or rows in {@code iterator}.
++ * @param ordered {@code true} if the iterator will return the rows in order, {@code false} otherwise.
++ * @return the created partition.
++ */
++ public static ImmutableBTreePartition create(UnfilteredRowIterator iterator, int initialRowCapacity, boolean ordered)
++ {
++ return new ImmutableBTreePartition(iterator.metadata(), iterator.partitionKey(), build(iterator, initialRowCapacity, ordered));
+ }
+
+ protected Holder holder()
+ {
+ return holder;
+ }
+
+ protected boolean canHaveShadowedData()
+ {
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
index 3218ff2,0000000..43653a9
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
+++ b/src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
@@@ -1,558 -1,0 +1,586 @@@
+/*
+ * 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.db.rows;
+
+import java.util.*;
+import java.security.MessageDigest;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.transform.FilteredRows;
+import org.apache.cassandra.db.transform.MoreRows;
+import org.apache.cassandra.db.transform.Transformation;
+import org.apache.cassandra.io.util.FileUtils;
+import org.apache.cassandra.io.sstable.CorruptSSTableException;
+import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.net.MessagingService;
+import org.apache.cassandra.utils.FBUtilities;
+import org.apache.cassandra.utils.IMergeIterator;
+import org.apache.cassandra.utils.MergeIterator;
+import org.apache.cassandra.utils.memory.AbstractAllocator;
+
+/**
+ * Static methods to work with atom iterators.
+ */
+public abstract class UnfilteredRowIterators
+{
+ private static final Logger logger = LoggerFactory.getLogger(UnfilteredRowIterators.class);
+
+ private UnfilteredRowIterators() {}
+
+ /**
+ * Interface for a listener interested in the result of merging multiple versions of a given row.
+ * <p>
+ * Implementors of this interface are given enough information that they can easily reconstruct the difference
+ * between the merged result and each individual input. This is used when reconciling results on replias for
+ * instance to figure out what to send as read-repair to each source.
+ */
+ public interface MergeListener
+ {
+ /**
+ * Called once for the merged partition.
+ *
+ * @param mergedDeletion the partition level deletion for the merged partition. Implementors can test if the
+ * merged partition actually has a partition level deletion or not by calling {@code mergedDeletion.isLive()}.
+ * @param versions the partition level deletion for the sources of the merge. Elements of the array will never
+ * be null, but be "live".
+ **/
+ public void onMergedPartitionLevelDeletion(DeletionTime mergedDeletion, DeletionTime[] versions);
+
+ /**
+ * Called once for every row participating in the merge.
+ * <p>
+ * Note that this is called for every clustering where at least one of the source merged has a row. In
+ * particular, this may be called in cases where there is no row in the merged output (if a source has a row
+ * that is shadowed by another source range tombstone or partition level deletion).
+ *
+ * @param merged the result of the merge. This cannot be {@code null} but can be empty, in which case this is a
+ * placeholder for when at least one source has a row, but that row is shadowed in the merged output.
+ * @param versions for each source, the row in that source corresponding to {@code merged}. This can be
+ * {@code null} for some sources if the source has not such row.
+ */
+ public void onMergedRows(Row merged, Row[] versions);
+
+ /**
+ * Called once for every range tombstone marker participating in the merge.
+ * <p>
+ * Note that this is called for every "clustering position" where at least one of the source merged has a range
+ * tombstone marker.
+ *
+ * @param merged the marker in the merged output. This can be {@code null} if there is no such marker, which
+ * means that at least one source has a marker in {@code versions} but the merged out has nothing corresponding
+ * (this basically mean the merged output has a currently open deletion that shadows whatever marker the source
+ * had).
+ * @param versions the marker for each source merged. This can be {@code null} for some source if that source
+ * has not such marker.
+ */
+ public void onMergedRangeTombstoneMarkers(RangeTombstoneMarker merged, RangeTombstoneMarker[] versions);
+
+ public void close();
+ }
+
+ /**
+ * Returns a iterator that only returns rows with only live content.
+ *
+ * This is mainly used in the CQL layer when we know we don't care about deletion
+ * infos (and since an UnfilteredRowIterator cannot shadow it's own data, we know everyting
+ * returned isn't shadowed by a tombstone).
+ */
+ public static RowIterator filter(UnfilteredRowIterator iter, int nowInSec)
+ {
+ return FilteredRows.filter(iter, nowInSec);
+ }
+
+ /**
+ * Returns an iterator that is the result of merging other iterators.
+ */
+ public static UnfilteredRowIterator merge(List<UnfilteredRowIterator> iterators, int nowInSec)
+ {
+ assert !iterators.isEmpty();
+ if (iterators.size() == 1)
+ return iterators.get(0);
+
+ return UnfilteredRowMergeIterator.create(iterators, nowInSec, null);
+ }
+
+ /**
+ * Returns an iterator that is the result of merging other iterators, and (optionally) using
+ * specific MergeListener.
+ *
+ * Note that this method assumes that there is at least 2 iterators to merge.
+ */
+ public static UnfilteredRowIterator merge(List<UnfilteredRowIterator> iterators, int nowInSec, MergeListener mergeListener)
+ {
+ return UnfilteredRowMergeIterator.create(iterators, nowInSec, mergeListener);
+ }
+
+ /**
+ * Returns an empty unfiltered iterator for a given partition.
+ */
+ public static UnfilteredRowIterator noRowsIterator(final CFMetaData cfm, final DecoratedKey partitionKey, final Row staticRow, final DeletionTime partitionDeletion, final boolean isReverseOrder)
+ {
+ return EmptyIterators.unfilteredRow(cfm, partitionKey, isReverseOrder, staticRow, partitionDeletion);
+ }
+
+ /**
+ * Digests the partition represented by the provided iterator.
+ *
+ * @param command the command that has yield {@code iterator}. This can be null if {@code version >= MessagingService.VERSION_30}
+ * as this is only used when producing digest to be sent to legacy nodes.
+ * @param iterator the iterator to digest.
+ * @param digest the {@code MessageDigest} to use for the digest.
+ * @param version the messaging protocol to use when producing the digest.
+ */
+ public static void digest(ReadCommand command, UnfilteredRowIterator iterator, MessageDigest digest, int version)
+ {
+ if (version < MessagingService.VERSION_30)
+ {
+ LegacyLayout.fromUnfilteredRowIterator(command, iterator).digest(iterator.metadata(), digest);
+ return;
+ }
+
+ digest.update(iterator.partitionKey().getKey().duplicate());
+ iterator.partitionLevelDeletion().digest(digest);
+ iterator.columns().regulars.digest(digest);
+ // When serializing an iterator, we skip the static columns if the iterator has not static row, even if the
+ // columns() object itself has some (the columns() is a superset of what the iterator actually contains, and
+ // will correspond to the queried columns pre-serialization). So we must avoid taking the satic column names
+ // into account if there is no static row or we'd have a digest mismatch between depending on whether the digest
+ // is computed on an iterator that has been serialized or not (see CASSANDRA-12090)
+ // TODO: in practice we could completely skip digesting the columns since they are more informative of what the
+ // iterator may contain, and digesting the actual content is enough. And in fact, that would be more correct
+ // (since again, the columns could be different without the information represented by the iterator being
+ // different), but removing them entirely is stricly speaking a breaking change (it would create mismatches on
+ // upgrade) so we can only do on the next protocol version bump.
+ if (iterator.staticRow() != Rows.EMPTY_STATIC_ROW)
+ iterator.columns().statics.digest(digest);
+ FBUtilities.updateWithBoolean(digest, iterator.isReverseOrder());
+ iterator.staticRow().digest(digest);
+
+ while (iterator.hasNext())
+ {
+ Unfiltered unfiltered = iterator.next();
+ unfiltered.digest(digest);
+ }
+ }
+
+ /**
+ * Returns an iterator that concatenate two atom iterators.
+ * This method assumes that both iterator are from the same partition and that the atom from
+ * {@code iter2} come after the ones of {@code iter1} (that is, that concatenating the iterator
+ * make sense).
+ */
+ public static UnfilteredRowIterator concat(final UnfilteredRowIterator iter1, final UnfilteredRowIterator iter2)
+ {
+ assert iter1.metadata().cfId.equals(iter2.metadata().cfId)
+ && iter1.partitionKey().equals(iter2.partitionKey())
+ && iter1.partitionLevelDeletion().equals(iter2.partitionLevelDeletion())
+ && iter1.isReverseOrder() == iter2.isReverseOrder()
+ && iter1.columns().equals(iter2.columns())
+ && iter1.staticRow().equals(iter2.staticRow());
+
+ class Extend implements MoreRows<UnfilteredRowIterator>
+ {
+ boolean returned = false;
+ public UnfilteredRowIterator moreContents()
+ {
+ if (returned)
+ return null;
+ returned = true;
+ return iter2;
+ }
+ }
+
+ return MoreRows.extend(iter1, new Extend());
+ }
+
++ /**
++ * Returns an iterator that concatenate the specified atom with the iterator.
++ */
++ public static UnfilteredRowIterator concat(final Unfiltered first, final UnfilteredRowIterator rest)
++ {
++ return new WrappingUnfilteredRowIterator(rest)
++ {
++ private boolean hasReturnedFirst;
++
++ @Override
++ public boolean hasNext()
++ {
++ return hasReturnedFirst ? super.hasNext() : true;
++ }
++
++ @Override
++ public Unfiltered next()
++ {
++ if (!hasReturnedFirst)
++ {
++ hasReturnedFirst = true;
++ return first;
++ }
++ return super.next();
++ }
++ };
++ }
++
+ public static UnfilteredRowIterator cloningIterator(UnfilteredRowIterator iterator, final AbstractAllocator allocator)
+ {
+ class Cloner extends Transformation
+ {
+ private final Row.Builder builder = allocator.cloningBTreeRowBuilder();
+
+ public Row applyToStatic(Row row)
+ {
+ return Rows.copy(row, builder).build();
+ }
+
+ @Override
+ public Row applyToRow(Row row)
+ {
+ return Rows.copy(row, builder).build();
+ }
+
+ @Override
+ public RangeTombstoneMarker applyToMarker(RangeTombstoneMarker marker)
+ {
+ return marker.copy(allocator);
+ }
+ }
+ return Transformation.apply(iterator, new Cloner());
+ }
+
+ /**
+ * Validate that the data of the provided iterator is valid, that is that the values
+ * it contains are valid for the type they represent, and more generally that the
+ * infos stored are sensible.
+ *
+ * This is mainly used by scrubber to detect problems in sstables.
+ *
+ * @param iterator the partition to check.
+ * @param filename the name of the file the data is comming from.
+ * @return an iterator that returns the same data than {@code iterator} but that
+ * checks said data and throws a {@code CorruptedSSTableException} if it detects
+ * invalid data.
+ */
+ public static UnfilteredRowIterator withValidation(UnfilteredRowIterator iterator, final String filename)
+ {
+ class Validator extends Transformation
+ {
+ @Override
+ public Row applyToStatic(Row row)
+ {
+ validate(row);
+ return row;
+ }
+
+ @Override
+ public Row applyToRow(Row row)
+ {
+ validate(row);
+ return row;
+ }
+
+ @Override
+ public RangeTombstoneMarker applyToMarker(RangeTombstoneMarker marker)
+ {
+ validate(marker);
+ return marker;
+ }
+
+ private void validate(Unfiltered unfiltered)
+ {
+ try
+ {
+ unfiltered.validateData(iterator.metadata());
+ }
+ catch (MarshalException me)
+ {
+ throw new CorruptSSTableException(me, filename);
+ }
+ }
+ }
+ return Transformation.apply(iterator, new Validator());
+ }
+
+ /**
+ * Wraps the provided iterator so it logs the returned atoms for debugging purposes.
+ * <p>
+ * Note that this is only meant for debugging as this can log a very large amount of
+ * logging at INFO.
+ */
+ public static UnfilteredRowIterator loggingIterator(UnfilteredRowIterator iterator, final String id, final boolean fullDetails)
+ {
+ CFMetaData metadata = iterator.metadata();
+ logger.info("[{}] Logging iterator on {}.{}, partition key={}, reversed={}, deletion={}",
+ id,
+ metadata.ksName,
+ metadata.cfName,
+ metadata.getKeyValidator().getString(iterator.partitionKey().getKey()),
+ iterator.isReverseOrder(),
+ iterator.partitionLevelDeletion().markedForDeleteAt());
+
+ class Logger extends Transformation
+ {
+ @Override
+ public Row applyToStatic(Row row)
+ {
+ if (!row.isEmpty())
+ logger.info("[{}] {}", id, row.toString(metadata, fullDetails));
+ return row;
+ }
+
+ @Override
+ public Row applyToRow(Row row)
+ {
+ logger.info("[{}] {}", id, row.toString(metadata, fullDetails));
+ return row;
+ }
+
+ @Override
+ public RangeTombstoneMarker applyToMarker(RangeTombstoneMarker marker)
+ {
+ logger.info("[{}] {}", id, marker.toString(metadata));
+ return marker;
+ }
+ }
+ return Transformation.apply(iterator, new Logger());
+ }
+
+ /**
+ * A wrapper over MergeIterator to implement the UnfilteredRowIterator interface.
+ */
+ private static class UnfilteredRowMergeIterator extends AbstractUnfilteredRowIterator
+ {
+ private final IMergeIterator<Unfiltered, Unfiltered> mergeIterator;
+ private final MergeListener listener;
+
+ private UnfilteredRowMergeIterator(CFMetaData metadata,
+ List<UnfilteredRowIterator> iterators,
+ PartitionColumns columns,
+ DeletionTime partitionDeletion,
+ int nowInSec,
+ boolean reversed,
+ MergeListener listener)
+ {
+ super(metadata,
+ iterators.get(0).partitionKey(),
+ partitionDeletion,
+ columns,
+ mergeStaticRows(iterators, columns.statics, nowInSec, listener, partitionDeletion),
+ reversed,
+ mergeStats(iterators));
+
+ this.mergeIterator = MergeIterator.get(iterators,
+ reversed ? metadata.comparator.reversed() : metadata.comparator,
+ new MergeReducer(iterators.size(), reversed, nowInSec, listener));
+ this.listener = listener;
+ }
+
+ private static UnfilteredRowMergeIterator create(List<UnfilteredRowIterator> iterators, int nowInSec, MergeListener listener)
+ {
+ try
+ {
+ checkForInvalidInput(iterators);
+ return new UnfilteredRowMergeIterator(iterators.get(0).metadata(),
+ iterators,
+ collectColumns(iterators),
+ collectPartitionLevelDeletion(iterators, listener),
+ nowInSec,
+ iterators.get(0).isReverseOrder(),
+ listener);
+ }
+ catch (RuntimeException | Error e)
+ {
+ try
+ {
+ FBUtilities.closeAll(iterators);
+ }
+ catch (Exception suppressed)
+ {
+ e.addSuppressed(suppressed);
+ }
+ throw e;
+ }
+ }
+
+ @SuppressWarnings("resource") // We're not really creating any resource here
+ private static void checkForInvalidInput(List<UnfilteredRowIterator> iterators)
+ {
+ if (iterators.isEmpty())
+ return;
+
+ UnfilteredRowIterator first = iterators.get(0);
+ for (int i = 1; i < iterators.size(); i++)
+ {
+ UnfilteredRowIterator iter = iterators.get(i);
+ assert first.metadata().cfId.equals(iter.metadata().cfId);
+ assert first.partitionKey().equals(iter.partitionKey());
+ assert first.isReverseOrder() == iter.isReverseOrder();
+ }
+ }
+
+ @SuppressWarnings("resource") // We're not really creating any resource here
+ private static DeletionTime collectPartitionLevelDeletion(List<UnfilteredRowIterator> iterators, MergeListener listener)
+ {
+ DeletionTime[] versions = listener == null ? null : new DeletionTime[iterators.size()];
+
+ DeletionTime delTime = DeletionTime.LIVE;
+ for (int i = 0; i < iterators.size(); i++)
+ {
+ UnfilteredRowIterator iter = iterators.get(i);
+ DeletionTime iterDeletion = iter.partitionLevelDeletion();
+ if (listener != null)
+ versions[i] = iterDeletion;
+ if (!delTime.supersedes(iterDeletion))
+ delTime = iterDeletion;
+ }
+ if (listener != null)
+ listener.onMergedPartitionLevelDeletion(delTime, versions);
+ return delTime;
+ }
+
+ private static Row mergeStaticRows(List<UnfilteredRowIterator> iterators,
+ Columns columns,
+ int nowInSec,
+ MergeListener listener,
+ DeletionTime partitionDeletion)
+ {
+ if (columns.isEmpty())
+ return Rows.EMPTY_STATIC_ROW;
+
+ if (iterators.stream().allMatch(iter -> iter.staticRow().isEmpty()))
+ return Rows.EMPTY_STATIC_ROW;
+
+ Row.Merger merger = new Row.Merger(iterators.size(), nowInSec, columns.hasComplex());
+ for (int i = 0; i < iterators.size(); i++)
+ merger.add(i, iterators.get(i).staticRow());
+
+ Row merged = merger.merge(partitionDeletion);
+ if (merged == null)
+ merged = Rows.EMPTY_STATIC_ROW;
+ if (listener != null)
+ listener.onMergedRows(merged, merger.mergedRows());
+ return merged;
+ }
+
+ private static PartitionColumns collectColumns(List<UnfilteredRowIterator> iterators)
+ {
+ PartitionColumns first = iterators.get(0).columns();
+ Columns statics = first.statics;
+ Columns regulars = first.regulars;
+ for (int i = 1; i < iterators.size(); i++)
+ {
+ PartitionColumns cols = iterators.get(i).columns();
+ statics = statics.mergeTo(cols.statics);
+ regulars = regulars.mergeTo(cols.regulars);
+ }
+ return statics == first.statics && regulars == first.regulars
+ ? first
+ : new PartitionColumns(statics, regulars);
+ }
+
+ private static EncodingStats mergeStats(List<UnfilteredRowIterator> iterators)
+ {
+ EncodingStats stats = EncodingStats.NO_STATS;
+ for (UnfilteredRowIterator iter : iterators)
+ stats = stats.mergeWith(iter.stats());
+ return stats;
+ }
+
+ protected Unfiltered computeNext()
+ {
+ while (mergeIterator.hasNext())
+ {
+ Unfiltered merged = mergeIterator.next();
+ if (merged != null)
+ return merged;
+ }
+ return endOfData();
+ }
+
+ public void close()
+ {
+ // This will close the input iterators
+ FileUtils.closeQuietly(mergeIterator);
+
+ if (listener != null)
+ listener.close();
+ }
+
+ private class MergeReducer extends MergeIterator.Reducer<Unfiltered, Unfiltered>
+ {
+ private final MergeListener listener;
+
+ private Unfiltered.Kind nextKind;
+
+ private final Row.Merger rowMerger;
+ private final RangeTombstoneMarker.Merger markerMerger;
+
+ private MergeReducer(int size, boolean reversed, int nowInSec, MergeListener listener)
+ {
+ this.rowMerger = new Row.Merger(size, nowInSec, columns().regulars.hasComplex());
+ this.markerMerger = new RangeTombstoneMarker.Merger(size, partitionLevelDeletion(), reversed);
+ this.listener = listener;
+ }
+
+ @Override
+ public boolean trivialReduceIsTrivial()
+ {
+ // If we have a listener, we must signal it even when we have a single version
+ return listener == null;
+ }
+
+ public void reduce(int idx, Unfiltered current)
+ {
+ nextKind = current.kind();
+ if (nextKind == Unfiltered.Kind.ROW)
+ rowMerger.add(idx, (Row)current);
+ else
+ markerMerger.add(idx, (RangeTombstoneMarker)current);
+ }
+
+ protected Unfiltered getReduced()
+ {
+ if (nextKind == Unfiltered.Kind.ROW)
+ {
+ Row merged = rowMerger.merge(markerMerger.activeDeletion());
+ if (listener != null)
+ listener.onMergedRows(merged == null ? BTreeRow.emptyRow(rowMerger.mergedClustering()) : merged, rowMerger.mergedRows());
+ return merged;
+ }
+ else
+ {
+ RangeTombstoneMarker merged = markerMerger.merge();
+ if (listener != null)
+ listener.onMergedRangeTombstoneMarkers(merged, markerMerger.mergedMarkers());
+ return merged;
+ }
+ }
+
+ protected void onKeyChange()
+ {
+ if (nextKind == Unfiltered.Kind.ROW)
+ rowMerger.clear();
+ else
+ markerMerger.clear();
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/test/unit/org/apache/cassandra/cql3/CQLTester.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/CQLTester.java
index e3dc220,98b8e23..c9c4631
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@@ -366,21 -278,20 +366,39 @@@ public abstract class CQLTeste
return list.isEmpty() ? Collections.<String>emptyList() : new ArrayList<>(list);
}
+ public ColumnFamilyStore getCurrentColumnFamilyStore()
+ {
+ String currentTable = currentTable();
+ return currentTable == null
+ ? null
+ : Keyspace.open(KEYSPACE).getColumnFamilyStore(currentTable);
+ }
+
public void flush()
{
- try
- {
- String currentTable = currentTable();
- if (currentTable != null)
- Keyspace.open(KEYSPACE).getColumnFamilyStore(currentTable).forceFlush().get();
- }
- catch (InterruptedException | ExecutionException e)
- {
- throw new RuntimeException(e);
- }
+ ColumnFamilyStore store = getCurrentColumnFamilyStore();
+ if (store != null)
+ store.forceBlockingFlush();
+ }
+
++ @FunctionalInterface
++ public interface CheckedFunction {
++ void apply() throws Throwable;
++ }
++
++ /**
++ * Runs the given function before and after a flush of sstables. This is useful for checking that behavior is
++ * the same whether data is in memtables or sstables.
++ * @param runnable
++ * @throws Throwable
++ */
++ public void beforeAndAfterFlush(CheckedFunction runnable) throws Throwable
++ {
++ runnable.apply();
++ flush();
++ runnable.apply();
+ }
+
public void compact()
{
try
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
index f9802d7,11d2462..0cf13bd
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
@@@ -17,47 -17,38 +17,49 @@@
*/
package org.apache.cassandra.cql3.validation.entities;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
import java.nio.ByteBuffer;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
+import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.Test;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLTester;
-import org.apache.cassandra.db.ColumnFamily;
+import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.cql3.QueryProcessor;
+import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
+import org.apache.cassandra.cql3.statements.IndexTarget;
import org.apache.cassandra.db.ColumnFamilyStore;
-import org.apache.cassandra.db.DecoratedKey;
-import org.apache.cassandra.db.IndexExpression;
-import org.apache.cassandra.db.composites.CellName;
-import org.apache.cassandra.db.index.IndexNotAvailableException;
-import org.apache.cassandra.db.index.PerRowSecondaryIndex;
-import org.apache.cassandra.db.index.SecondaryIndexSearcher;
-import org.apache.cassandra.db.index.composites.CompositesSearcher;
+import org.apache.cassandra.db.DeletionTime;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.rows.Cell;
+import org.apache.cassandra.db.rows.Row;
import org.apache.cassandra.exceptions.ConfigurationException;
-import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.exceptions.SyntaxException;
-import org.apache.cassandra.utils.FBUtilities;
-import org.apache.cassandra.utils.concurrent.OpOrder.Group;
-import org.apache.commons.lang3.StringUtils;
-import org.junit.Test;
-
+import org.apache.cassandra.index.IndexNotAvailableException;
+import org.apache.cassandra.index.SecondaryIndexManager;
+import org.apache.cassandra.index.StubIndex;
+import org.apache.cassandra.index.internal.CustomCassandraIndex;
+import org.apache.cassandra.schema.IndexMetadata;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.messages.ResultMessage;
+import org.apache.cassandra.utils.ByteBufferUtil;
+import org.apache.cassandra.utils.MD5Digest;
+import org.apache.cassandra.utils.Pair;
+
+import static org.apache.cassandra.Util.throwAssert;
+ import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
+ import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
public class SecondaryIndexTest extends CQLTester
{
@@@ -949,77 -684,190 +951,206 @@@
}
@Test
+ public void droppingIndexInvalidatesPreparedStatements() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c int, PRIMARY KEY ((a), b))");
+ createIndex("CREATE INDEX c_idx ON %s(c)");
+ MD5Digest cqlId = prepareStatement("SELECT * FROM %s.%s WHERE c=?", false).statementId;
+ Integer thriftId = prepareStatement("SELECT * FROM %s.%s WHERE c=?", true).toThriftPreparedResult().getItemId();
+
+ assertNotNull(QueryProcessor.instance.getPrepared(cqlId));
+ assertNotNull(QueryProcessor.instance.getPreparedForThrift(thriftId));
+
+ dropIndex("DROP INDEX %s.c_idx");
+
+ assertNull(QueryProcessor.instance.getPrepared(cqlId));
+ assertNull(QueryProcessor.instance.getPreparedForThrift(thriftId));
+ }
+
+ // See CASSANDRA-11021
+ @Test
+ public void testIndexesOnNonStaticColumnsWhereSchemaIncludesStaticColumns() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c int static, d int, PRIMARY KEY (a, b))");
+ createIndex("CREATE INDEX b_idx on %s(b)");
+ createIndex("CREATE INDEX d_idx on %s(d)");
+
+ execute("INSERT INTO %s (a, b, c ,d) VALUES (0, 0, 0, 0)");
+ execute("INSERT INTO %s (a, b, c, d) VALUES (1, 1, 1, 1)");
+ assertRows(execute("SELECT * FROM %s WHERE b = 0"), row(0, 0, 0, 0));
+ assertRows(execute("SELECT * FROM %s WHERE d = 1"), row(1, 1, 1, 1));
+
+ execute("UPDATE %s SET c = 2 WHERE a = 0");
+ execute("UPDATE %s SET c = 3, d = 4 WHERE a = 1 AND b = 1");
+ assertRows(execute("SELECT * FROM %s WHERE b = 0"), row(0, 0, 2, 0));
+ assertRows(execute("SELECT * FROM %s WHERE d = 4"), row(1, 1, 3, 4));
+
+ execute("DELETE FROM %s WHERE a = 0");
+ execute("DELETE FROM %s WHERE a = 1 AND b = 1");
+ assertEmpty(execute("SELECT * FROM %s WHERE b = 0"));
+ assertEmpty(execute("SELECT * FROM %s WHERE d = 3"));
+ }
+
++ @Test
+ public void testWithEmptyRestrictionValueAndSecondaryIndex() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))");
+ createIndex("CREATE INDEX on %s(c)");
+ createIndex("CREATE INDEX on %s(v)");
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("1"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ // Test clustering columns restrictions
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob('')) AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
- }
++ });
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ // Test restrictions on non-primary key value
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"));
- }
++ });
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER);
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"),
+ row(bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER));
- }
++ });
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithSecondaryIndexAndCompactTables() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c)) WITH COMPACT STORAGE");
- assertInvalidMessage("Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables",
++ assertInvalidMessage("Secondary indexes are not supported on COMPACT STORAGE tables that have clustering columns",
+ "CREATE INDEX on %s(c)");
+
+ createTable("CREATE TABLE %s (pk blob PRIMARY KEY, v blob) WITH COMPACT STORAGE");
+ createIndex("CREATE INDEX on %s(v)");
+
+ execute("INSERT INTO %s (pk, v) VALUES (?, ?)", bytes("foo123"), bytes("1"));
+
+ // Test restrictions on non-primary key value
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"));
+
+ execute("INSERT INTO %s (pk, v) VALUES (?, ?)", bytes("foo124"), EMPTY_BYTE_BUFFER);
+
+ assertRows(execute("SELECT * FROM %s WHERE v = textAsBlob('');"),
+ row(bytes("foo124"), EMPTY_BYTE_BUFFER));
+ }
+
- /**
- * Custom index used to test the behavior of the system when the index is not ready.
- * As Custom indices cannot by <code>PerColumnSecondaryIndex</code> we use a <code>PerRowSecondaryIndex</code>
- * to avoid the check but return a <code>CompositesSearcher</code>.
- */
- public static class IndexBlockingOnInitialization extends PerRowSecondaryIndex
+ private ResultMessage.Prepared prepareStatement(String cql, boolean forThrift)
{
- private volatile CountDownLatch latch = new CountDownLatch(1);
-
- @Override
- public void index(ByteBuffer rowKey, ColumnFamily cf)
- {
- try
- {
- latch.await();
- }
- catch (InterruptedException e)
- {
- Thread.interrupted();
- }
- }
-
- @Override
- public void delete(DecoratedKey key, Group opGroup)
- {
- }
+ return QueryProcessor.prepare(String.format(cql, KEYSPACE, currentTable()),
+ ClientState.forInternalCalls(),
+ forThrift);
+ }
- @Override
- public void init()
- {
- }
+ private void validateCell(Cell cell, ColumnDefinition def, ByteBuffer val, long timestamp)
+ {
+ assertNotNull(cell);
+ assertEquals(0, def.type.compare(cell.value(), val));
+ assertEquals(timestamp, cell.timestamp());
+ }
- @Override
- public void reload()
- {
- }
+ private static void assertColumnValue(int expected, String name, Row row, CFMetaData cfm)
+ {
+ ColumnDefinition col = cfm.getColumnDefinition(new ColumnIdentifier(name, true));
+ AbstractType<?> type = col.type;
+ assertEquals(expected, type.compose(row.getCell(col).value()));
+ }
- @Override
- public void validateOptions() throws ConfigurationException
- {
- }
+ /**
+ * <code>CassandraIndex</code> that blocks during the initialization.
+ */
+ public static class IndexBlockingOnInitialization extends CustomCassandraIndex
+ {
+ private final CountDownLatch latch = new CountDownLatch(1);
- @Override
- public String getIndexName()
+ public IndexBlockingOnInitialization(ColumnFamilyStore baseCfs, IndexMetadata indexDef)
{
- return "testIndex";
+ super(baseCfs, indexDef);
}
@Override
[13/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0'
into cassandra-3.9
Posted by ms...@apache.org.
Merge branch 'cassandra-3.0' into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/15fd71f9
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/15fd71f9
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/15fd71f9
Branch: refs/heads/cassandra-3.8
Commit: 15fd71f9a3b07bbac7a1182f2e6bffd32e79b955
Parents: 1fff069 05483a9
Author: Stefania Alborghetti <st...@datastax.com>
Authored: Thu Aug 11 09:11:11 2016 +0800
Committer: Stefania Alborghetti <st...@datastax.com>
Committed: Thu Aug 11 09:11:11 2016 +0800
----------------------------------------------------------------------
CHANGES.txt | 1 +
src/java/org/apache/cassandra/db/compaction/CompactionTask.java | 5 +++--
2 files changed, 4 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/15fd71f9/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 498868c,bd3bb75..bca8879
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,11 -1,7 +1,12 @@@
-3.0.9
+3.9
+ * Fix value skipping with counter columns (CASSANDRA-11726)
+ * Fix nodetool tablestats miss SSTable count (CASSANDRA-12205)
+ * Fixed flacky SSTablesIteratedTest (CASSANDRA-12282)
+ * Fixed flacky SSTableRewriterTest: check file counts before calling validateCFS (CASSANDRA-12348)
+ * cqlsh: Fix handling of $$-escaped strings (CASSANDRA-12189)
+ * Fix SSL JMX requiring truststore containing server cert (CASSANDRA-12109)
+Merged from 3.0:
+ * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
- * Fix potential bad messaging service message for paged range reads
- within mixed-version 3.x clusters (CASSANDRA-12249)
* Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
* NullPointerException during compaction on table with static columns (CASSANDRA-12336)
* Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/15fd71f9/src/java/org/apache/cassandra/db/compaction/CompactionTask.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/compaction/CompactionTask.java
index 86c8a8f,7e4ed41..2913dc9
--- a/src/java/org/apache/cassandra/db/compaction/CompactionTask.java
+++ b/src/java/org/apache/cassandra/db/compaction/CompactionTask.java
@@@ -170,13 -164,14 +170,14 @@@ public class CompactionTask extends Abs
AbstractCompactionStrategy.ScannerList scanners = strategy.getScanners(actuallyCompact);
CompactionIterator ci = new CompactionIterator(compactionType, scanners.scanners, controller, nowInSec, taskId))
{
- if (collector != null)
- collector.beginCompaction(ci);
long lastCheckObsoletion = start;
- if (!controller.cfs.getCompactionStrategyManager().isActive)
+ if (!controller.cfs.getCompactionStrategyManager().isActive())
throw new CompactionInterruptedException(ci.getCompactionInfo());
+ if (collector != null)
+ collector.beginCompaction(ci);
+
try (CompactionAwareWriter writer = getCompactionAwareWriter(cfs, getDirectories(), transaction, actuallyCompact))
{
estimatedKeys = writer.estimatedKeys();
[18/50] [abbrv] cassandra git commit: Add Sigar to classes included
in clientutil.java
Posted by ms...@apache.org.
Add Sigar to classes included in clientutil.java
This is required as Sigar is now a dependency of UUIDGen.
Patch by Sylvain Lebresne; reviewed by Tyler Hobbs for CASSANDRA-11635
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5b38d3a8
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5b38d3a8
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5b38d3a8
Branch: refs/heads/cassandra-3.8
Commit: 5b38d3a81e171eb414ae144191a4ff6423bef8b0
Parents: 2e90259
Author: Sylvain Lebresne <sy...@datastax.com>
Authored: Thu Aug 11 13:41:22 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Thu Aug 11 13:41:22 2016 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
build.xml | 4 ++++
2 files changed, 5 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5b38d3a8/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 05059cc..14bed81 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.2.8
+ * Add Sigar to classes included in clientutil.jar (CASSANDRA-11635)
* Add decay to histograms and timers used for metrics (CASSANDRA-11752)
* Fix hanging stream session (CASSANDRA-10992)
* Add byteman support for testing (CASSANDRA-12377)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5b38d3a8/build.xml
----------------------------------------------------------------------
diff --git a/build.xml b/build.xml
index 49a0366..2426c91 100644
--- a/build.xml
+++ b/build.xml
@@ -917,6 +917,7 @@
<include name="org/apache/cassandra/exceptions/*.class" />
<include name="org/apache/cassandra/utils/CloseableIterator.class" />
<include name="org/apache/cassandra/io/util/*.class" />
+ <include name="org/apache/cassandra/utils/SigarLibrary.class" />
</fileset>
<manifest>
<attribute name="Implementation-Title" value="Cassandra"/>
@@ -1255,6 +1256,8 @@
-->
<target name="test-clientutil-jar" depends="build-test,jar" description="Test clientutil jar">
<junit fork="on" forkmode="perTest" failureproperty="testfailed" maxmemory="1024m" timeout="${test.timeout}">
+ <!-- Note that the test pass without that next line, but it prints an ugly error message -->
+ <jvmarg value="-Djava.library.path=${build.lib}/sigar-bin"/>
<test name="org.apache.cassandra.serializers.ClientUtilsTest" />
<formatter type="brief" usefile="false" />
<classpath>
@@ -1268,6 +1271,7 @@
<pathelement location="${build.lib}/logback-classic-1.1.3.jar" />
<pathelement location="${build.lib}/jackson-core-asl-1.9.2.jar" />
<pathelement location="${build.lib}/jackson-mapper-asl-1.9.2.jar" />
+ <pathelement location="${build.lib}/sigar-1.6.4.jar" />
<fileset dir="${build.dir.lib}">
<include name="**/junit*.jar" />
</fileset>
[27/50] [abbrv] cassandra git commit: avoid some date math if using
raw time formats
Posted by ms...@apache.org.
avoid some date math if using raw time formats
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/d052c904
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/d052c904
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/d052c904
Branch: refs/heads/cassandra-3.8
Commit: d052c90483e44ab8b280c4507d5c11202367dffd
Parents: ca556c4
Author: Dave Brosius <db...@mebigfatguy.com>
Authored: Fri Aug 12 19:13:13 2016 -0400
Committer: Dave Brosius <db...@mebigfatguy.com>
Committed: Fri Aug 12 19:13:13 2016 -0400
----------------------------------------------------------------------
src/java/org/apache/cassandra/tools/JsonTransformer.java | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d052c904/src/java/org/apache/cassandra/tools/JsonTransformer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tools/JsonTransformer.java b/src/java/org/apache/cassandra/tools/JsonTransformer.java
index 3b98595..801197d 100644
--- a/src/java/org/apache/cassandra/tools/JsonTransformer.java
+++ b/src/java/org/apache/cassandra/tools/JsonTransformer.java
@@ -461,9 +461,14 @@ public final class JsonTransformer
private String dateString(TimeUnit from, long time)
{
+ if (rawTime)
+ {
+ return Long.toString(time);
+ }
+
long secs = from.toSeconds(time);
long offset = Math.floorMod(from.toNanos(time), 1000_000_000L); // nanos per sec
- return rawTime? Long.toString(time) : Instant.ofEpochSecond(secs, offset).toString();
+ return Instant.ofEpochSecond(secs, offset).toString();
}
/**
[10/50] [abbrv] cassandra git commit: Merge branch 'cassandra-2.2'
into cassandra-3.0
Posted by ms...@apache.org.
Merge branch 'cassandra-2.2' into cassandra-3.0
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/2691c9e4
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/2691c9e4
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/2691c9e4
Branch: refs/heads/cassandra-3.8
Commit: 2691c9e4e12a72f143bb2f00d076ecbfad59f4e4
Parents: 465def8 2e90259
Author: T Jake Luciani <ja...@apache.org>
Authored: Wed Aug 10 14:39:52 2016 -0400
Committer: T Jake Luciani <ja...@apache.org>
Committed: Wed Aug 10 14:39:52 2016 -0400
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../metrics/CassandraMetricsRegistry.java | 4 +-
.../cassandra/metrics/ClearableHistogram.java | 4 +-
.../DecayingEstimatedHistogramReservoir.java | 549 +++++++++++++++++++
.../metrics/EstimatedHistogramReservoir.java | 111 ----
.../cassandra/utils/EstimatedHistogram.java | 2 +-
...DecayingEstimatedHistogramReservoirTest.java | 381 +++++++++++++
7 files changed, 936 insertions(+), 116 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2691c9e4/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 86746ad,05059cc..e1bc9f3
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,37 -1,7 +1,38 @@@
-2.2.8
+3.0.9
+ * Fix potential bad messaging service message for paged range reads
+ within mixed-version 3.x clusters (CASSANDRA-12249)
+ * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
+ * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
+ * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
+ * Fix upgrade of super columns on thrift (CASSANDRA-12335)
+ * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
+ * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
+ * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
+ * Lost counter writes in compact table and static columns (CASSANDRA-12219)
+ * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
+ * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
+ * Add option to override compaction space check (CASSANDRA-12180)
+ * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
+ * Respond with v1/v2 protocol header when responding to driver that attempts
+ to connect with too low of a protocol version (CASSANDRA-11464)
+ * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
+ * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
+ * Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
+ * Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
+ * Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)
+ * Fix upgrading sparse tables that are incorrectly marked as dense (CASSANDRA-11315)
+ * Fix reverse queries ignoring range tombstones (CASSANDRA-11733)
+ * Avoid potential race when rebuilding CFMetaData (CASSANDRA-12098)
+ * Avoid missing sstables when getting the canonical sstables (CASSANDRA-11996)
+ * Always select the live sstables when getting sstables in bounds (CASSANDRA-11944)
+ * Fix column ordering of results with static columns for Thrift requests in
+ a mixed 2.x/3.x cluster, also fix potential non-resolved duplication of
+ those static columns in query results (CASSANDRA-12123)
+ * Avoid digest mismatch with empty but static rows (CASSANDRA-12090)
+ * Fix EOF exception when altering column type (CASSANDRA-11820)
+Merged from 2.2:
+ * Add decay to histograms and timers used for metrics (CASSANDRA-11752)
* Fix hanging stream session (CASSANDRA-10992)
- * Add byteman support for testing (CASSANDRA-12377)
* Fix INSERT JSON, fromJson() support of smallint, tinyint types (CASSANDRA-12371)
* Restore JVM metric export for metric reporters (CASSANDRA-12312)
* Release sstables of failed stream sessions only when outgoing transfers are finished (CASSANDRA-11345)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2691c9e4/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
index 1cd3b6c,8e5671b..2d4cbf9
--- a/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
+++ b/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
@@@ -65,31 -58,17 +65,31 @@@ public class CassandraMetricsRegistry e
return meter;
}
+ public Meter meter(MetricName name, MetricName alias)
+ {
+ Meter meter = meter(name);
+ registerAlias(name, alias);
+ return meter;
+ }
+
public Histogram histogram(MetricName name, boolean considerZeroes)
{
- Histogram histogram = register(name, new ClearableHistogram(new EstimatedHistogramReservoir(considerZeroes)));
+ Histogram histogram = register(name, new ClearableHistogram(new DecayingEstimatedHistogramReservoir(considerZeroes)));
registerMBean(histogram, name.getMBeanName());
return histogram;
}
+ public Histogram histogram(MetricName name, MetricName alias, boolean considerZeroes)
+ {
+ Histogram histogram = histogram(name, considerZeroes);
+ registerAlias(name, alias);
+ return histogram;
+ }
+
public Timer timer(MetricName name)
{
- Timer timer = register(name, new Timer(new EstimatedHistogramReservoir(false)));
+ Timer timer = register(name, new Timer(new DecayingEstimatedHistogramReservoir()));
registerMBean(timer, name.getMBeanName());
return timer;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2691c9e4/src/java/org/apache/cassandra/utils/EstimatedHistogram.java
----------------------------------------------------------------------
[24/50] [abbrv] cassandra git commit: Fix clean interval not sent to
commit log for empty memtable flush
Posted by ms...@apache.org.
Fix clean interval not sent to commit log for empty memtable flush
patch by Branimir Lambov; reviewed by Jake Luciani for
CASSANDRA-12436
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5cef78af
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5cef78af
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5cef78af
Branch: refs/heads/cassandra-3.8
Commit: 5cef78af9214308ef817de43ec36054a2549329c
Parents: c85749e
Author: Branimir Lambov <br...@datastax.com>
Authored: Thu Aug 11 11:15:44 2016 +0300
Committer: T Jake Luciani <ja...@apache.org>
Committed: Fri Aug 12 11:24:16 2016 -0400
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../apache/cassandra/db/ColumnFamilyStore.java | 19 +++------
.../cassandra/db/commitlog/CommitLog.java | 5 ++-
.../db/commitlog/CommitLogSegment.java | 5 ++-
.../db/commitlog/CommitLogCQLTest.java | 41 ++++++++++++++++++++
5 files changed, 54 insertions(+), 17 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5cef78af/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 959b967..cccb62d 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
3.0.9
+ * Fix clean interval not sent to commit log for empty memtable flush (CASSANDRA-12436)
* Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
* Backport CASSANDRA-12002 (CASSANDRA-12177)
* Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5cef78af/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
index 82604e2..6c02909 100644
--- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
@@ -1039,20 +1039,9 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
writeBarrier.markBlocking();
writeBarrier.await();
- // mark all memtables as flushing, removing them from the live memtable list, and
- // remove any memtables that are already clean from the set we need to flush
- Iterator<Memtable> iter = memtables.iterator();
- while (iter.hasNext())
- {
- Memtable memtable = iter.next();
+ // mark all memtables as flushing, removing them from the live memtable list
+ for (Memtable memtable : memtables)
memtable.cfs.data.markFlushing(memtable);
- if (memtable.isClean() || truncate)
- {
- memtable.cfs.replaceFlushed(memtable, Collections.emptyList());
- reclaim(memtable);
- iter.remove();
- }
- }
metric.memtableSwitchCount.inc();
@@ -1060,7 +1049,9 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
{
for (Memtable memtable : memtables)
{
- Collection<SSTableReader> readers = memtable.flush();
+ Collection<SSTableReader> readers = Collections.emptyList();
+ if (!memtable.isClean() && !truncate)
+ readers = memtable.flush();
memtable.cfs.replaceFlushed(memtable, readers);
reclaim(memtable);
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5cef78af/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/commitlog/CommitLog.java b/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
index dfe3f91..d167735 100644
--- a/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
+++ b/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
@@ -313,8 +313,9 @@ public class CommitLog implements CommitLogMBean
}
else
{
- logger.trace("Not safe to delete{} commit log segment {}; dirty is {}",
- (iter.hasNext() ? "" : " active"), segment, segment.dirtyString());
+ if (logger.isTraceEnabled())
+ logger.trace("Not safe to delete{} commit log segment {}; dirty is {}",
+ (iter.hasNext() ? "" : " active"), segment, segment.dirtyString());
}
// Don't mark or try to delete any newer segments once we've reached the one containing the
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5cef78af/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java b/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
index d2f12bf..0a03c3c 100644
--- a/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
+++ b/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
@@ -536,7 +536,10 @@ public abstract class CommitLogSegment
for (UUID cfId : getDirtyCFIDs())
{
CFMetaData m = Schema.instance.getCFMetaData(cfId);
- sb.append(m == null ? "<deleted>" : m.cfName).append(" (").append(cfId).append("), ");
+ sb.append(m == null ? "<deleted>" : m.cfName).append(" (").append(cfId)
+ .append(", dirty: ").append(cfDirty.get(cfId))
+ .append(", clean: ").append(cfClean.get(cfId))
+ .append("), ");
}
return sb.toString();
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5cef78af/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java b/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java
new file mode 100644
index 0000000..8537ebb
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java
@@ -0,0 +1,41 @@
+package org.apache.cassandra.db.commitlog;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.junit.Test;
+
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.db.ColumnFamilyStore;
+
+public class CommitLogCQLTest extends CQLTester
+{
+ @Test
+ public void testTruncateSegmentDiscard() throws Throwable
+ {
+ String otherTable = createTable("CREATE TABLE %s (idx INT, data TEXT, PRIMARY KEY(idx));");
+
+ createTable("CREATE TABLE %s (idx INT, data TEXT, PRIMARY KEY(idx));");
+ execute("INSERT INTO %s (idx, data) VALUES (?, ?)", 15, Integer.toString(15));
+ flush();
+
+ // We write something in different table to advance the commit log position. Current table remains clean.
+ execute(String.format("INSERT INTO %s.%s (idx, data) VALUES (?, ?)", keyspace(), otherTable), 16, Integer.toString(16));
+
+ ColumnFamilyStore cfs = getCurrentColumnFamilyStore();
+ assert cfs.getTracker().getView().getCurrentMemtable().isClean();
+ // Calling switchMemtable directly applies Flush even though memtable is empty. This can happen with some races
+ // (flush with recycling by segment manager). It should still tell commitlog that the memtable's region is clean.
+ // CASSANDRA-12436
+ cfs.switchMemtable();
+
+ execute("INSERT INTO %s (idx, data) VALUES (?, ?)", 15, Integer.toString(17));
+
+ Collection<CommitLogSegment> active = new ArrayList<>(CommitLog.instance.allocator.getActiveSegments());
+ CommitLog.instance.forceRecycleAllSegments();
+
+ // If one of the previous segments remains, it wasn't clean.
+ active.retainAll(CommitLog.instance.allocator.getActiveSegments());
+ assert active.isEmpty();
+ }
+}
[19/50] [abbrv] cassandra git commit: Merge branch 'cassandra-2.2'
into cassandra-3.0
Posted by ms...@apache.org.
Merge branch 'cassandra-2.2' into cassandra-3.0
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/2a828afd
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/2a828afd
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/2a828afd
Branch: refs/heads/cassandra-3.8
Commit: 2a828afdbfc7410b288c1681506dcbeecb9df3c2
Parents: 19245e5 5b38d3a
Author: Tyler Hobbs <ty...@gmail.com>
Authored: Thu Aug 11 13:46:47 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Thu Aug 11 13:46:47 2016 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
build.xml | 4 ++++
2 files changed, 5 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2a828afd/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 0a1d406,14bed81..3ce9f9e
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,41 -1,8 +1,42 @@@
-2.2.8
+3.0.9
+ * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
+ * Backport CASSANDRA-12002 (CASSANDRA-12177)
+ * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
+ * Fix potential bad messaging service message for paged range reads
+ within mixed-version 3.x clusters (CASSANDRA-12249)
+ * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
+ * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
+ * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
+ * Fix upgrade of super columns on thrift (CASSANDRA-12335)
+ * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
+ * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
+ * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
+ * Lost counter writes in compact table and static columns (CASSANDRA-12219)
+ * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
+ * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
+ * Add option to override compaction space check (CASSANDRA-12180)
+ * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
+ * Respond with v1/v2 protocol header when responding to driver that attempts
+ to connect with too low of a protocol version (CASSANDRA-11464)
+ * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
+ * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
+ * Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
+ * Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
+ * Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)
+ * Fix upgrading sparse tables that are incorrectly marked as dense (CASSANDRA-11315)
+ * Fix reverse queries ignoring range tombstones (CASSANDRA-11733)
+ * Avoid potential race when rebuilding CFMetaData (CASSANDRA-12098)
+ * Avoid missing sstables when getting the canonical sstables (CASSANDRA-11996)
+ * Always select the live sstables when getting sstables in bounds (CASSANDRA-11944)
+ * Fix column ordering of results with static columns for Thrift requests in
+ a mixed 2.x/3.x cluster, also fix potential non-resolved duplication of
+ those static columns in query results (CASSANDRA-12123)
+ * Avoid digest mismatch with empty but static rows (CASSANDRA-12090)
+ * Fix EOF exception when altering column type (CASSANDRA-11820)
+Merged from 2.2:
+ * Add Sigar to classes included in clientutil.jar (CASSANDRA-11635)
* Add decay to histograms and timers used for metrics (CASSANDRA-11752)
* Fix hanging stream session (CASSANDRA-10992)
- * Add byteman support for testing (CASSANDRA-12377)
* Fix INSERT JSON, fromJson() support of smallint, tinyint types (CASSANDRA-12371)
* Restore JVM metric export for metric reporters (CASSANDRA-12312)
* Release sstables of failed stream sessions only when outgoing transfers are finished (CASSANDRA-11345)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2a828afd/build.xml
----------------------------------------------------------------------
[02/50] [abbrv] cassandra git commit: Fix value skipping with counter
columns
Posted by ms...@apache.org.
Fix value skipping with counter columns
patch by Sylvain Lebresne; reviewed by Aleksey Yeschenko for
CASSANDRA-11726
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/ee609411
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/ee609411
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/ee609411
Branch: refs/heads/cassandra-3.8
Commit: ee609411a8e154255812b157788a79bbdf076566
Parents: 02ebf87
Author: Sylvain Lebresne <sy...@datastax.com>
Authored: Thu Aug 4 17:54:03 2016 +0200
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Tue Aug 9 16:33:10 2016 +0100
----------------------------------------------------------------------
CHANGES.txt | 1 +
src/java/org/apache/cassandra/db/Conflicts.java | 9 +++++++
.../cql3/validation/entities/CountersTest.java | 27 ++++++++++++++++++++
3 files changed, 37 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee609411/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 2c5c221..b7bbf72 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
3.9
+ * Fix value skipping with counter columns (CASSANDRA-11726)
* Fix nodetool tablestats miss SSTable count (CASSANDRA-12205)
* Fixed flacky SSTablesIteratedTest (CASSANDRA-12282)
* Fixed flacky SSTableRewriterTest: check file counts before calling validateCFS (CASSANDRA-12348)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee609411/src/java/org/apache/cassandra/db/Conflicts.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/Conflicts.java b/src/java/org/apache/cassandra/db/Conflicts.java
index fa0e819..9e8bd9a 100644
--- a/src/java/org/apache/cassandra/db/Conflicts.java
+++ b/src/java/org/apache/cassandra/db/Conflicts.java
@@ -68,6 +68,15 @@ public abstract class Conflicts
if (!rightLive)
return Resolution.RIGHT_WINS;
+ // Handle empty values. Counters can't truly have empty values, but we can have a counter cell that temporarily
+ // has one on read if the column for the cell is not queried by the user due to the optimization of #10657. We
+ // thus need to handle this (see #11726 too).
+ if (!leftValue.hasRemaining())
+ return rightValue.hasRemaining() || leftTimestamp > rightTimestamp ? Resolution.LEFT_WINS : Resolution.RIGHT_WINS;
+
+ if (!rightValue.hasRemaining())
+ return Resolution.RIGHT_WINS;
+
return Resolution.MERGE;
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee609411/test/unit/org/apache/cassandra/cql3/validation/entities/CountersTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/CountersTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/CountersTest.java
index 33b4a4f..b1cd0bb 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/CountersTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/CountersTest.java
@@ -196,4 +196,31 @@ public class CountersTest extends CQLTester
assertInvalidThrowMessage("counter type is not supported for PRIMARY KEY part a",
InvalidRequestException.class, String.format("CREATE TABLE %s.%s (a counter, b int, PRIMARY KEY (b, a)) WITH CLUSTERING ORDER BY (a desc);", KEYSPACE, createTableName()));
}
+
+ /**
+ * Test for the bug of #11726.
+ */
+ @Test
+ public void testCounterAndColumnSelection() throws Throwable
+ {
+ for (String compactStorageClause : new String[]{ "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (k int PRIMARY KEY, c counter)" + compactStorageClause);
+
+ // Flush 2 updates in different sstable so that the following select does a merge, which is what triggers
+ // the problem from #11726
+
+ execute("UPDATE %s SET c = c + ? WHERE k = ?", 1L, 0);
+
+ flush();
+
+ execute("UPDATE %s SET c = c + ? WHERE k = ?", 1L, 0);
+
+ flush();
+
+ // Querying, but not including the counter. Pre-CASSANDRA-11726, this made us query the counter but include
+ // it's value, which broke at merge (post-CASSANDRA-11726 are special cases to never skip values).
+ assertRows(execute("SELECT k FROM %s"), row(0));
+ }
+ }
}
[14/50] [abbrv] cassandra git commit: Backport CASSANDRA-12002
Posted by ms...@apache.org.
Backport CASSANDRA-12002
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/de86ab1b
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/de86ab1b
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/de86ab1b
Branch: refs/heads/cassandra-3.8
Commit: de86ab1bb6e4b71451348397e0cbb2a577cd92d4
Parents: 05483a9
Author: Yuki Morishita <yu...@apache.org>
Authored: Thu Aug 11 10:06:05 2016 -0500
Committer: Yuki Morishita <yu...@apache.org>
Committed: Thu Aug 11 10:11:13 2016 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../apache/cassandra/tools/SSTableExport.java | 8 ++----
.../org/apache/cassandra/utils/FBUtilities.java | 28 ++++++++++++++++++++
3 files changed, 31 insertions(+), 6 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/de86ab1b/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index bd3bb75..3db2c77 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
3.0.9
+ * Backport CASSANDRA-12002 (CASSANDRA-12177)
* Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
* Fix potential bad messaging service message for paged range reads
within mixed-version 3.x clusters (CASSANDRA-12249)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/de86ab1b/src/java/org/apache/cassandra/tools/SSTableExport.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tools/SSTableExport.java b/src/java/org/apache/cassandra/tools/SSTableExport.java
index 09dbbed..cc6b84b 100644
--- a/src/java/org/apache/cassandra/tools/SSTableExport.java
+++ b/src/java/org/apache/cassandra/tools/SSTableExport.java
@@ -98,14 +98,10 @@ public class SSTableExport
if (!desc.version.storeRows())
throw new IOException("pre-3.0 SSTable is not supported.");
- EnumSet<MetadataType> types = EnumSet.of(MetadataType.VALIDATION, MetadataType.STATS, MetadataType.HEADER);
+ EnumSet<MetadataType> types = EnumSet.of(MetadataType.STATS, MetadataType.HEADER);
Map<MetadataType, MetadataComponent> sstableMetadata = desc.getMetadataSerializer().deserialize(desc, types);
- ValidationMetadata validationMetadata = (ValidationMetadata) sstableMetadata.get(MetadataType.VALIDATION);
SerializationHeader.Component header = (SerializationHeader.Component) sstableMetadata.get(MetadataType.HEADER);
-
- IPartitioner partitioner = SecondaryIndexManager.isIndexColumnFamily(desc.cfname)
- ? new LocalPartitioner(header.getKeyType())
- : FBUtilities.newPartitioner(validationMetadata.partitioner);
+ IPartitioner partitioner = FBUtilities.newPartitioner(desc);
CFMetaData.Builder builder = CFMetaData.Builder.create("keyspace", "table").withPartitioner(partitioner);
header.getStaticColumns().entrySet().stream()
http://git-wip-us.apache.org/repos/asf/cassandra/blob/de86ab1b/src/java/org/apache/cassandra/utils/FBUtilities.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/utils/FBUtilities.java b/src/java/org/apache/cassandra/utils/FBUtilities.java
index 5f0e0a0..d996c91 100644
--- a/src/java/org/apache/cassandra/utils/FBUtilities.java
+++ b/src/java/org/apache/cassandra/utils/FBUtilities.java
@@ -42,11 +42,17 @@ import org.apache.cassandra.auth.IAuthorizer;
import org.apache.cassandra.auth.IRoleManager;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.DecoratedKey;
+import org.apache.cassandra.db.SerializationHeader;
import org.apache.cassandra.dht.IPartitioner;
+import org.apache.cassandra.dht.LocalPartitioner;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.io.IVersionedSerializer;
+import org.apache.cassandra.io.sstable.Descriptor;
+import org.apache.cassandra.io.sstable.metadata.MetadataComponent;
+import org.apache.cassandra.io.sstable.metadata.MetadataType;
+import org.apache.cassandra.io.sstable.metadata.ValidationMetadata;
import org.apache.cassandra.schema.CompressionParams;
import org.apache.cassandra.io.util.DataOutputBuffer;
import org.apache.cassandra.io.util.DataOutputBufferFixed;
@@ -395,6 +401,28 @@ public class FBUtilities
result.get(ms, TimeUnit.MILLISECONDS);
}
+ /**
+ * Create a new instance of a partitioner defined in an SSTable Descriptor
+ * @param desc Descriptor of an sstable
+ * @return a new IPartitioner instance
+ * @throws IOException
+ */
+ public static IPartitioner newPartitioner(Descriptor desc) throws IOException
+ {
+ EnumSet<MetadataType> types = EnumSet.of(MetadataType.VALIDATION, MetadataType.HEADER);
+ Map<MetadataType, MetadataComponent> sstableMetadata = desc.getMetadataSerializer().deserialize(desc, types);
+ ValidationMetadata validationMetadata = (ValidationMetadata) sstableMetadata.get(MetadataType.VALIDATION);
+ SerializationHeader.Component header = (SerializationHeader.Component) sstableMetadata.get(MetadataType.HEADER);
+ if (validationMetadata.partitioner.endsWith("LocalPartitioner"))
+ {
+ return new LocalPartitioner(header.getKeyType());
+ }
+ else
+ {
+ return newPartitioner(validationMetadata.partitioner);
+ }
+ }
+
public static IPartitioner newPartitioner(String partitionerClassName) throws ConfigurationException
{
if (!partitionerClassName.contains("."))
[25/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0'
into cassandra-3.9
Posted by ms...@apache.org.
Merge branch 'cassandra-3.0' into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/e60f741e
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/e60f741e
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/e60f741e
Branch: refs/heads/cassandra-3.8
Commit: e60f741e34eb2ebf80391be640e4a57022554c7f
Parents: 0c771a2 5cef78a
Author: T Jake Luciani <ja...@apache.org>
Authored: Fri Aug 12 11:28:47 2016 -0400
Committer: T Jake Luciani <ja...@apache.org>
Committed: Fri Aug 12 11:28:47 2016 -0400
----------------------------------------------------------------------
----------------------------------------------------------------------
[04/50] [abbrv] cassandra git commit: Merge branch 'cassandra-2.2'
into cassandra-3.0
Posted by ms...@apache.org.
Merge branch 'cassandra-2.2' into cassandra-3.0
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/62ef8617
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/62ef8617
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/62ef8617
Branch: refs/heads/cassandra-3.8
Commit: 62ef8617cdaa07fa37b1b2121ad5923da64e74a3
Parents: 676b6a8 76e3100
Author: Yuki Morishita <yu...@apache.org>
Authored: Tue Aug 9 16:45:52 2016 -0500
Committer: Yuki Morishita <yu...@apache.org>
Committed: Tue Aug 9 16:45:52 2016 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../org/apache/cassandra/config/Config.java | 4 ++
.../cassandra/config/DatabaseDescriptor.java | 5 --
.../cassandra/streaming/StreamReader.java | 26 +---------
.../cassandra/streaming/StreamSession.java | 36 +-------------
.../compress/CompressedInputStream.java | 21 +++++++-
.../compress/CompressedStreamReader.java | 11 ++---
.../streaming/messages/IncomingFileMessage.java | 22 ++-------
.../streaming/messages/RetryMessage.java | 4 ++
.../org/apache/cassandra/utils/Throwables.java | 14 ++++++
.../compression/CompressedInputStreamTest.java | 52 +++++++++++++++++---
11 files changed, 100 insertions(+), 96 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 78bd32d,232203e..f613c5f
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,34 -1,6 +1,35 @@@
-2.2.8
+3.0.9
+ * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
+ * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
+ * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
+ * Fix upgrade of super columns on thrift (CASSANDRA-12335)
+ * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
+ * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
+ * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
+ * Lost counter writes in compact table and static columns (CASSANDRA-12219)
+ * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
+ * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
+ * Add option to override compaction space check (CASSANDRA-12180)
+ * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
+ * Respond with v1/v2 protocol header when responding to driver that attempts
+ to connect with too low of a protocol version (CASSANDRA-11464)
+ * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
+ * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
+ * Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
+ * Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
+ * Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)
+ * Fix upgrading sparse tables that are incorrectly marked as dense (CASSANDRA-11315)
+ * Fix reverse queries ignoring range tombstones (CASSANDRA-11733)
+ * Avoid potential race when rebuilding CFMetaData (CASSANDRA-12098)
+ * Avoid missing sstables when getting the canonical sstables (CASSANDRA-11996)
+ * Always select the live sstables when getting sstables in bounds (CASSANDRA-11944)
+ * Fix column ordering of results with static columns for Thrift requests in
+ a mixed 2.x/3.x cluster, also fix potential non-resolved duplication of
+ those static columns in query results (CASSANDRA-12123)
+ * Avoid digest mismatch with empty but static rows (CASSANDRA-12090)
+ * Fix EOF exception when altering column type (CASSANDRA-11820)
+Merged from 2.2:
+ * Fix hanging stream session (CASSANDRA-10992)
- * Add byteman support for testing (CASSANDRA-12377)
* Fix INSERT JSON, fromJson() support of smallint, tinyint types (CASSANDRA-12371)
* Restore JVM metric export for metric reporters (CASSANDRA-12312)
* Release sstables of failed stream sessions only when outgoing transfers are finished (CASSANDRA-11345)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/config/Config.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/config/Config.java
index e6c56cb,60daee6..86f1016
--- a/src/java/org/apache/cassandra/config/Config.java
+++ b/src/java/org/apache/cassandra/config/Config.java
@@@ -171,8 -170,11 +171,12 @@@ public class Confi
public Integer concurrent_compactors;
public volatile Integer compaction_throughput_mb_per_sec = 16;
public volatile Integer compaction_large_partition_warning_threshold_mb = 100;
+ public Integer min_free_space_per_drive_in_mb = 50;
+ /**
+ * @deprecated retry support removed on CASSANDRA-10992
+ */
+ @Deprecated
public Integer max_streaming_retries = 3;
public volatile Integer stream_throughput_outbound_megabits_per_sec = 200;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/streaming/StreamReader.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/streaming/StreamReader.java
index f8db26b,c96ea22..4ca7937
--- a/src/java/org/apache/cassandra/streaming/StreamReader.java
+++ b/src/java/org/apache/cassandra/streaming/StreamReader.java
@@@ -31,24 -33,19 +31,25 @@@ import org.slf4j.LoggerFactory
import com.ning.compress.lzf.LZFInputStream;
+import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.Schema;
-import org.apache.cassandra.db.ColumnFamilyStore;
-import org.apache.cassandra.db.DecoratedKey;
-import org.apache.cassandra.db.Directories;
-import org.apache.cassandra.db.Keyspace;
+import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.rows.*;
import org.apache.cassandra.io.sstable.Descriptor;
-import org.apache.cassandra.service.StorageService;
+import org.apache.cassandra.io.sstable.SSTableMultiWriter;
+import org.apache.cassandra.io.sstable.SSTableSimpleIterator;
+import org.apache.cassandra.io.sstable.format.SSTableFormat;
+import org.apache.cassandra.io.sstable.format.Version;
+import org.apache.cassandra.io.util.RewindableDataInputStreamPlus;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.streaming.messages.FileMessageHeader;
import org.apache.cassandra.utils.ByteBufferUtil;
-import org.apache.cassandra.utils.BytesReadTracker;
+import org.apache.cassandra.io.util.TrackedInputStream;
+import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Pair;
+ import static org.apache.cassandra.utils.Throwables.extractIOExceptionCause;
/**
* StreamReader reads from stream and writes to SSTable.
@@@ -124,63 -121,37 +125,40 @@@ public class StreamReade
logger.debug("[Stream #{}] Finished receiving file #{} from {} readBytes = {}, totalSize = {}",
session.planId(), fileSeqNum, session.peer, in.getBytesRead(), totalSize);
return writer;
- } catch (Throwable e)
+ }
+ catch (Throwable e)
{
- if (key != null)
+ if (deserializer != null)
logger.warn("[Stream {}] Error while reading partition {} from stream on ks='{}' and table='{}'.",
- session.planId(), key, cfs.keyspace.getName(), cfs.getColumnFamilyName());
+ session.planId(), deserializer.partitionKey(), cfs.keyspace.getName(), cfs.getColumnFamilyName());
if (writer != null)
{
- try
- {
- writer.abort();
- }
- catch (Throwable e2)
- {
- // add abort error to original and continue so we can drain unread stream
- e.addSuppressed(e2);
- }
+ writer.abort(e);
}
- drain(in, in.getBytesRead());
- if (e instanceof IOException)
- throw (IOException) e;
- else
- throw Throwables.propagate(e);
+ throw Throwables.propagate(e);
}
+ finally
+ {
+ if (deserializer != null)
+ deserializer.cleanup();
+ }
+ }
+
+ protected SerializationHeader getHeader(CFMetaData metadata)
+ {
+ return header != null? header.toHeader(metadata) : null; //pre-3.0 sstable have no SerializationHeader
}
- protected SSTableWriter createWriter(ColumnFamilyStore cfs, long totalSize, long repairedAt, SSTableFormat.Type format) throws IOException
+ protected SSTableMultiWriter createWriter(ColumnFamilyStore cfs, long totalSize, long repairedAt, SSTableFormat.Type format) throws IOException
{
- Directories.DataDirectory localDir = cfs.directories.getWriteableLocation(totalSize);
+ Directories.DataDirectory localDir = cfs.getDirectories().getWriteableLocation(totalSize);
if (localDir == null)
throw new IOException("Insufficient disk space to store " + totalSize + " bytes");
- desc = Descriptor.fromFilename(cfs.getTempSSTablePath(cfs.directories.getLocationForDisk(localDir), format));
+ desc = Descriptor.fromFilename(cfs.getSSTablePath(cfs.getDirectories().getLocationForDisk(localDir), format));
- return SSTableWriter.create(desc, estimatedKeys, repairedAt, sstableLevel);
+ return cfs.createSSTableMultiWriter(desc, estimatedKeys, repairedAt, sstableLevel, getHeader(cfs.metadata), session.getTransaction(cfId));
}
- protected void drain(InputStream dis, long bytesRead) throws IOException
- {
- long toSkip = totalSize() - bytesRead;
-
- // InputStream.skip can return -1 if dis is inaccessible.
- long skipped = dis.skip(toSkip);
- if (skipped == -1)
- return;
-
- toSkip = toSkip - skipped;
- while (toSkip > 0)
- {
- skipped = dis.skip(toSkip);
- if (skipped == -1)
- break;
- toSkip = toSkip - skipped;
- }
- }
-
protected long totalSize()
{
long size = 0;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/streaming/StreamSession.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
index 9719587,fa1022d..bc87c8f
--- a/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
+++ b/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
@@@ -17,7 -17,8 +17,6 @@@
*/
package org.apache.cassandra.streaming.compress;
--import java.io.DataInputStream;
-
import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
@@@ -37,9 -40,12 +36,11 @@@ import org.apache.cassandra.streaming.P
import org.apache.cassandra.streaming.StreamReader;
import org.apache.cassandra.streaming.StreamSession;
import org.apache.cassandra.streaming.messages.FileMessageHeader;
-import org.apache.cassandra.utils.ByteBufferUtil;
-import org.apache.cassandra.utils.BytesReadTracker;
+import org.apache.cassandra.io.util.TrackedInputStream;
import org.apache.cassandra.utils.Pair;
+ import static org.apache.cassandra.utils.Throwables.extractIOExceptionCause;
+
/**
* StreamReader that reads from streamed compressed SSTable
*/
@@@ -114,24 -119,25 +115,22 @@@ public class CompressedStreamReader ext
}
catch (Throwable e)
{
- if (key != null)
+ if (deserializer != null)
logger.warn("[Stream {}] Error while reading partition {} from stream on ks='{}' and table='{}'.",
- session.planId(), key, cfs.keyspace.getName(), cfs.getColumnFamilyName());
+ session.planId(), deserializer.partitionKey(), cfs.keyspace.getName(), cfs.getTableName());
if (writer != null)
{
- try
- {
- writer.abort();
- }
- catch (Throwable e2)
- {
- // add abort error to original and continue so we can drain unread stream
- e.addSuppressed(e2);
- }
+ writer.abort(e);
}
- drain(in, in.getBytesRead());
- if (e instanceof IOException)
- throw (IOException) e;
- else
- throw Throwables.propagate(e);
+ if (extractIOExceptionCause(e).isPresent())
+ throw e;
+ throw Throwables.propagate(e);
}
+ finally
+ {
+ if (deserializer != null)
+ deserializer.cleanup();
+ }
}
@Override
http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
index d881d43,2870c03..438cb0b
--- a/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
+++ b/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
@@@ -20,10 -20,11 +20,12 @@@ package org.apache.cassandra.streaming.
import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
++import java.util.Optional;
-import com.google.common.base.Optional;
+import org.apache.cassandra.io.sstable.SSTableMultiWriter;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataInputPlus.DataInputStreamPlus;
+
-import org.apache.cassandra.io.sstable.format.SSTableWriter;
import org.apache.cassandra.io.util.DataOutputStreamPlus;
import org.apache.cassandra.streaming.StreamReader;
import org.apache.cassandra.streaming.StreamSession;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/streaming/messages/RetryMessage.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/utils/Throwables.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/utils/Throwables.java
index 8ef6a63,877f388..5ad9686
--- a/src/java/org/apache/cassandra/utils/Throwables.java
+++ b/src/java/org/apache/cassandra/utils/Throwables.java
@@@ -18,25 -18,14 +18,26 @@@
*/
package org.apache.cassandra.utils;
+import java.io.File;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.Iterator;
++import java.util.Optional;
+import java.util.stream.Stream;
-import com.google.common.base.Optional;
+import org.apache.cassandra.io.FSReadError;
+import org.apache.cassandra.io.FSWriteError;
-public class Throwables
+public final class Throwables
{
+ public enum FileOpType { READ, WRITE }
- public static Throwable merge(Throwable existingFail, Throwable newFail)
+ public interface DiscreteAction<E extends Exception>
+ {
+ void perform() throws E;
+ }
+
+ public static <T extends Throwable> T merge(T existingFail, T newFail)
{
if (existingFail == null)
return newFail;
@@@ -167,4 -54,17 +168,17 @@@
}
return accumulate;
}
+
+ public static Optional<IOException> extractIOExceptionCause(Throwable t)
+ {
+ if (t instanceof IOException)
+ return Optional.of((IOException) t);
+ Throwable cause = t;
+ while ((cause = cause.getCause()) != null)
+ {
+ if (cause instanceof IOException)
+ return Optional.of((IOException) cause);
+ }
- return Optional.absent();
++ return Optional.empty();
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/test/unit/org/apache/cassandra/streaming/compression/CompressedInputStreamTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/streaming/compression/CompressedInputStreamTest.java
index a3300ac,0000000..8512d8f
mode 100644,000000..100644
--- a/test/unit/org/apache/cassandra/streaming/compression/CompressedInputStreamTest.java
+++ b/test/unit/org/apache/cassandra/streaming/compression/CompressedInputStreamTest.java
@@@ -1,137 -1,0 +1,173 @@@
+/*
+ * 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.streaming.compression;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Test;
+import org.apache.cassandra.db.ClusteringComparator;
+import org.apache.cassandra.db.marshal.BytesType;
+import org.apache.cassandra.io.compress.CompressedSequentialWriter;
+import org.apache.cassandra.io.compress.CompressionMetadata;
+import org.apache.cassandra.schema.CompressionParams;
+import org.apache.cassandra.io.sstable.Component;
+import org.apache.cassandra.io.sstable.Descriptor;
+import org.apache.cassandra.io.sstable.metadata.MetadataCollector;
+import org.apache.cassandra.streaming.compress.CompressedInputStream;
+import org.apache.cassandra.streaming.compress.CompressionInfo;
+import org.apache.cassandra.utils.ChecksumType;
+import org.apache.cassandra.utils.Pair;
+
+import static org.junit.Assert.assertEquals;
++import static org.junit.Assert.fail;
+
+/**
+ */
+public class CompressedInputStreamTest
+{
+ @Test
+ public void testCompressedRead() throws Exception
+ {
- testCompressedReadWith(new long[]{0L}, false);
- testCompressedReadWith(new long[]{1L}, false);
- testCompressedReadWith(new long[]{100L}, false);
++ testCompressedReadWith(new long[]{0L}, false, false);
++ testCompressedReadWith(new long[]{1L}, false, false);
++ testCompressedReadWith(new long[]{100L}, false, false);
+
- testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, false);
++ testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, false, false);
+ }
+
+ @Test(expected = EOFException.class)
+ public void testTruncatedRead() throws Exception
+ {
- testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, true);
++ testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, true, false);
++ }
++
++ /**
++ * Test that CompressedInputStream does not block if there's an exception while reading stream
++ */
++ @Test(timeout = 30000)
++ public void testException() throws Exception
++ {
++ testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, false, true);
+ }
+
+ /**
+ * @param valuesToCheck array of longs of range(0-999)
+ * @throws Exception
+ */
- private void testCompressedReadWith(long[] valuesToCheck, boolean testTruncate) throws Exception
++ private void testCompressedReadWith(long[] valuesToCheck, boolean testTruncate, boolean testException) throws Exception
+ {
+ assert valuesToCheck != null && valuesToCheck.length > 0;
+
+ // write compressed data file of longs
+ File tmp = new File(File.createTempFile("cassandra", "unittest").getParent(), "ks-cf-ib-1-Data.db");
+ Descriptor desc = Descriptor.fromFilename(tmp.getAbsolutePath());
+ MetadataCollector collector = new MetadataCollector(new ClusteringComparator(BytesType.instance));
+ CompressionParams param = CompressionParams.snappy(32);
+ Map<Long, Long> index = new HashMap<Long, Long>();
+ try (CompressedSequentialWriter writer = new CompressedSequentialWriter(tmp, desc.filenameFor(Component.COMPRESSION_INFO), param, collector))
+ {
+ for (long l = 0L; l < 1000; l++)
+ {
+ index.put(l, writer.position());
+ writer.writeLong(l);
+ }
+ writer.finish();
+ }
+
+ CompressionMetadata comp = CompressionMetadata.create(tmp.getAbsolutePath());
+ List<Pair<Long, Long>> sections = new ArrayList<>();
+ for (long l : valuesToCheck)
+ {
+ long position = index.get(l);
+ sections.add(Pair.create(position, position + 8));
+ }
+ CompressionMetadata.Chunk[] chunks = comp.getChunksForSections(sections);
+ long totalSize = comp.getTotalSizeForSections(sections);
+ long expectedSize = 0;
+ for (CompressionMetadata.Chunk c : chunks)
+ expectedSize += c.length + 4;
+ assertEquals(expectedSize, totalSize);
+
+ // buffer up only relevant parts of file
+ int size = 0;
+ for (CompressionMetadata.Chunk c : chunks)
+ size += (c.length + 4); // 4bytes CRC
+ byte[] toRead = new byte[size];
+
+ try (RandomAccessFile f = new RandomAccessFile(tmp, "r"))
+ {
+ int pos = 0;
+ for (CompressionMetadata.Chunk c : chunks)
+ {
+ f.seek(c.offset);
+ pos += f.read(toRead, pos, c.length + 4);
+ }
+ }
+
+ if (testTruncate)
+ {
+ byte [] actuallyRead = new byte[50];
+ System.arraycopy(toRead, 0, actuallyRead, 0, 50);
+ toRead = actuallyRead;
+ }
+
+ // read buffer using CompressedInputStream
+ CompressionInfo info = new CompressionInfo(chunks, param);
- CompressedInputStream input = new CompressedInputStream(new ByteArrayInputStream(toRead), info,
- ChecksumType.CRC32, () -> 1.0);
++
++ if (testException)
++ {
++ testException(sections, info);
++ return;
++ }
++ CompressedInputStream input = new CompressedInputStream(new ByteArrayInputStream(toRead), info, ChecksumType.CRC32, () -> 1.0);
+
+ try (DataInputStream in = new DataInputStream(input))
+ {
+ for (int i = 0; i < sections.size(); i++)
+ {
+ input.position(sections.get(i).left);
+ long readValue = in.readLong();
+ assertEquals("expected " + valuesToCheck[i] + " but was " + readValue, valuesToCheck[i], readValue);
+ }
+ }
+ }
++
++ private static void testException(List<Pair<Long, Long>> sections, CompressionInfo info) throws IOException
++ {
++ CompressedInputStream input = new CompressedInputStream(new ByteArrayInputStream(new byte[0]), info, ChecksumType.CRC32, () -> 1.0);
++
++ try (DataInputStream in = new DataInputStream(input))
++ {
++ for (int i = 0; i < sections.size(); i++)
++ {
++ input.position(sections.get(i).left);
++ try {
++ in.readLong();
++ fail("Should have thrown IOException");
++ }
++ catch (IOException e)
++ {
++ continue;
++ }
++ }
++ }
++ }
+}
[41/50] [abbrv] cassandra git commit: Merge branch cassandra-2.1 into
cassandra-2.2
Posted by ms...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
index 365abe0,4a54a9a..11d2462
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
@@@ -17,36 -17,26 +17,39 @@@
*/
package org.apache.cassandra.cql3.validation.entities;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import java.nio.ByteBuffer;
import java.util.HashMap;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
-
-import org.apache.commons.lang.StringUtils;
-
-import org.junit.Test;
+import java.util.concurrent.CountDownLatch;
import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.db.ColumnFamily;
+import org.apache.cassandra.db.ColumnFamilyStore;
+import org.apache.cassandra.db.DecoratedKey;
+import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.composites.CellName;
+import org.apache.cassandra.db.index.IndexNotAvailableException;
+import org.apache.cassandra.db.index.PerRowSecondaryIndex;
+import org.apache.cassandra.db.index.SecondaryIndexSearcher;
+import org.apache.cassandra.db.index.composites.CompositesSearcher;
import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.exceptions.SyntaxException;
import org.apache.cassandra.utils.FBUtilities;
+import org.apache.cassandra.utils.concurrent.OpOrder.Group;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.Test;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+ import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
+ import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+
public class SecondaryIndexTest extends CQLTester
{
private static final int TOO_BIG = 1024 * 65;
@@@ -655,134 -644,141 +658,273 @@@
}
@Test
+ public void testIndexQueriesWithIndexNotReady() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk int, ck int, value int, PRIMARY KEY (pk, ck))");
+
+ for (int i = 0; i < 10; i++)
+ for (int j = 0; j < 10; j++)
+ execute("INSERT INTO %s (pk, ck, value) VALUES (?, ?, ?)", i, j, i + j);
+
+ createIndex("CREATE CUSTOM INDEX testIndex ON %s (value) USING '" + IndexBlockingOnInitialization.class.getName()
+ + "'");
+ try
+ {
+ execute("SELECT value FROM %s WHERE value = 2");
+ fail();
+ }
+ catch (IndexNotAvailableException e)
+ {
+ assertTrue(true);
+ }
+ finally
+ {
+ execute("DROP index " + KEYSPACE + ".testIndex");
+ }
+ }
+
++ @Test
+ public void testWithEmptyRestrictionValueAndSecondaryIndex() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))");
+ createIndex("CREATE INDEX on %s(c)");
+ createIndex("CREATE INDEX on %s(v)");
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("1"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ // Test clustering columns restrictions
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob('')) AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+ }
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ // Test restrictions on non-primary key value
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"));
+ }
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER);
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"),
+ row(bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER));
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithSecondaryIndexAndCompactTables() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c)) WITH COMPACT STORAGE");
+ assertInvalidMessage("Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables",
+ "CREATE INDEX on %s(c)");
+
+ createTable("CREATE TABLE %s (pk blob PRIMARY KEY, v blob) WITH COMPACT STORAGE");
+ createIndex("CREATE INDEX on %s(v)");
+
+ execute("INSERT INTO %s (pk, v) VALUES (?, ?)", bytes("foo123"), bytes("1"));
+
+ // Test restrictions on non-primary key value
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"));
+
+ execute("INSERT INTO %s (pk, v) VALUES (?, ?)", bytes("foo124"), EMPTY_BYTE_BUFFER);
+
+ assertRows(execute("SELECT * FROM %s WHERE v = textAsBlob('');"),
+ row(bytes("foo124"), EMPTY_BYTE_BUFFER));
+ }
++
+ /**
+ * Custom index used to test the behavior of the system when the index is not ready.
+ * As Custom indices cannot by <code>PerColumnSecondaryIndex</code> we use a <code>PerRowSecondaryIndex</code>
+ * to avoid the check but return a <code>CompositesSearcher</code>.
+ */
+ public static class IndexBlockingOnInitialization extends PerRowSecondaryIndex
+ {
+ private volatile CountDownLatch latch = new CountDownLatch(1);
+
+ @Override
+ public void index(ByteBuffer rowKey, ColumnFamily cf)
+ {
+ try
+ {
+ latch.await();
+ }
+ catch (InterruptedException e)
+ {
+ Thread.interrupted();
+ }
+ }
+
+ @Override
+ public void delete(DecoratedKey key, Group opGroup)
+ {
+ }
+
+ @Override
+ public void init()
+ {
+ }
+
+ @Override
+ public void reload()
+ {
+ }
+
+ @Override
+ public void validateOptions() throws ConfigurationException
+ {
+ }
+
+ @Override
+ public String getIndexName()
+ {
+ return "testIndex";
+ }
+
+ @Override
+ protected SecondaryIndexSearcher createSecondaryIndexSearcher(Set<ByteBuffer> columns)
+ {
+ return new CompositesSearcher(baseCfs.indexManager, columns)
+ {
+ @Override
+ public boolean canHandleIndexClause(List<IndexExpression> clause)
+ {
+ return true;
+ }
+
+ @Override
+ public void validate(IndexExpression indexExpression) throws InvalidRequestException
+ {
+ }
+ };
+ }
+
+ @Override
+ public void forceBlockingFlush()
+ {
+ }
+
+ @Override
+ public ColumnFamilyStore getIndexCfs()
+ {
+ return baseCfs;
+ }
+
+ @Override
+ public void removeIndex(ByteBuffer columnName)
+ {
+ latch.countDown();
+ }
+
+ @Override
+ public void invalidate()
+ {
+ }
+
+ @Override
+ public void truncateBlocking(long truncatedAt)
+ {
+ }
+
+ @Override
+ public boolean indexes(CellName name)
+ {
+ return false;
+ }
+
+ @Override
+ public long estimateResultRows()
+ {
+ return 0;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
index f8ec13c,cf923bc..32d800a
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
@@@ -36,31 -35,31 +36,37 @@@ public class SelectOrderByTest extends
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 1, 1);
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 2, 2);
-- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0, 0, 0),
-- row(0, 1, 1),
-- row(0, 2, 2)
-- );
--
-- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(0, 2, 2),
-- row(0, 1, 1),
-- row(0, 0, 0)
-- );
--
-- // order by the only column in the selection
-- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(1), row(2));
--
-- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0));
--
-- // order by a column not in the selection
-- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(1), row(2));
--
-- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0, 0, 0),
++ row(0, 1, 1),
++ row(0, 2, 2)
++ );
++
++ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(0, 2, 2),
++ row(0, 1, 1),
++ row(0, 0, 0)
++ );
++
++ // order by the only column in the selection
++ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(1), row(2));
++
++ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0));
++
++ // order by a column not in the selection
++ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(1), row(2));
++
++ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0));
++ }
}
}
@@@ -74,22 -73,22 +80,28 @@@
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 1, 1);
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 2, 2);
-- // order by the only column in the selection
-- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(1), row(2));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
-- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0));
++ // order by the only column in the selection
++ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(1), row(2));
-- // order by a column not in the selection
-- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(1), row(2));
++ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0));
-- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0));
++ // order by a column not in the selection
++ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(1), row(2));
-- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
-- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
++ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0));
++
++ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
++ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
++ }
}
}
@@@ -105,15 -104,15 +117,20 @@@
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, {a: ?})", 0, 1, 1);
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, {a: ?})", 0, 2, 2);
-- // order by a column not in the selection
-- assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(1), row(2));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++ // order by a column not in the selection
++ assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(1), row(2));
-- assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0));
++ assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0));
-- assertRows(execute("SELECT blobAsInt(intAsBlob(c.a)) FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0));
++ assertRows(execute("SELECT blobAsInt(intAsBlob(c.a)) FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0));
++ }
dropTable("DROP TABLE %s");
}
}
@@@ -129,62 -128,62 +146,68 @@@
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 1, 4);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 2, 5);
-- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0, 0, 0, 0),
-- row(0, 0, 1, 1),
-- row(0, 0, 2, 2),
-- row(0, 1, 0, 3),
-- row(0, 1, 1, 4),
-- row(0, 1, 2, 5)
-- );
--
-- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(0, 1, 2, 5),
-- row(0, 1, 1, 4),
-- row(0, 1, 0, 3),
-- row(0, 0, 2, 2),
-- row(0, 0, 1, 1),
-- row(0, 0, 0, 0)
-- );
--
-- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
-- row(0, 1, 2, 5),
-- row(0, 1, 1, 4),
-- row(0, 1, 0, 3),
-- row(0, 0, 2, 2),
-- row(0, 0, 1, 1),
-- row(0, 0, 0, 0)
-- );
--
-- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
-- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
-- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
-- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
-- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY d ASC", 0);
--
-- // select and order by b
-- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(0), row(0), row(1), row(1), row(1));
-- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(1), row(1), row(1), row(0), row(0), row(0));
--
-- // select c, order by b
-- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(1), row(2), row(0), row(1), row(2));
-- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0), row(2), row(1), row(0));
--
-- // select c, order by b, c
-- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
-- row(0), row(1), row(2), row(0), row(1), row(2));
-- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
-- row(2), row(1), row(0), row(2), row(1), row(0));
--
-- // select d, order by b, c
-- assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
-- row(0), row(1), row(2), row(3), row(4), row(5));
-- assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
-- row(5), row(4), row(3), row(2), row(1), row(0));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0, 0, 0, 0),
++ row(0, 0, 1, 1),
++ row(0, 0, 2, 2),
++ row(0, 1, 0, 3),
++ row(0, 1, 1, 4),
++ row(0, 1, 2, 5)
++ );
++
++ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(0, 1, 2, 5),
++ row(0, 1, 1, 4),
++ row(0, 1, 0, 3),
++ row(0, 0, 2, 2),
++ row(0, 0, 1, 1),
++ row(0, 0, 0, 0)
++ );
++
++ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
++ row(0, 1, 2, 5),
++ row(0, 1, 1, 4),
++ row(0, 1, 0, 3),
++ row(0, 0, 2, 2),
++ row(0, 0, 1, 1),
++ row(0, 0, 0, 0)
++ );
++
++ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
++ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
++ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
++ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
++ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY d ASC", 0);
++
++ // select and order by b
++ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(0), row(0), row(1), row(1), row(1));
++ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(1), row(1), row(1), row(0), row(0), row(0));
++
++ // select c, order by b
++ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(1), row(2), row(0), row(1), row(2));
++ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0), row(2), row(1), row(0));
++
++ // select c, order by b, c
++ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
++ row(0), row(1), row(2), row(0), row(1), row(2));
++ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
++ row(2), row(1), row(0), row(2), row(1), row(0));
++
++ // select d, order by b, c
++ assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
++ row(0), row(1), row(2), row(3), row(4), row(5));
++ assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
++ row(5), row(4), row(3), row(2), row(1), row(0));
++ }
}
@Test
@@@ -198,41 -197,41 +221,46 @@@
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 1, 4);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 2, 5);
-- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c ASC", 0);
-- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c DESC", 0);
-- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
-- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
-- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY d ASC", 0);
--
-- // select and order by b
-- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(0), row(0), row(1), row(1), row(1));
-- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(1), row(1), row(1), row(0), row(0), row(0));
--
-- assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0, 0), row(0, 0), row(0, 0), row(1, 1), row(1, 1), row(1, 1));
-- assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(1, 1), row(1, 1), row(1, 1), row(0, 0), row(0, 0), row(0, 0));
--
-- // select c, order by b
-- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(1), row(2), row(0), row(1), row(2));
-- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0), row(2), row(1), row(0));
--
-- // select c, order by b, c
-- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
-- row(0), row(1), row(2), row(0), row(1), row(2));
-- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
-- row(2), row(1), row(0), row(2), row(1), row(0));
--
-- // select d, order by b, c
-- assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
-- row(0), row(1), row(2), row(3), row(4), row(5));
-- assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
-- row(5), row(4), row(3), row(2), row(1), row(0));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c ASC", 0);
++ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c DESC", 0);
++ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
++ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
++ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY d ASC", 0);
++ // select and order by b
++ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(0), row(0), row(1), row(1), row(1));
++ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(1), row(1), row(1), row(0), row(0), row(0));
++
++ assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0, 0), row(0, 0), row(0, 0), row(1, 1), row(1, 1), row(1, 1));
++ assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(1, 1), row(1, 1), row(1, 1), row(0, 0), row(0, 0), row(0, 0));
++
++ // select c, order by b
++ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(1), row(2), row(0), row(1), row(2));
++ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0), row(2), row(1), row(0));
++
++ // select c, order by b, c
++ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
++ row(0), row(1), row(2), row(0), row(1), row(2));
++ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
++ row(2), row(1), row(0), row(2), row(1), row(0));
++
++ // select d, order by b, c
++ assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
++ row(0), row(1), row(2), row(3), row(4), row(5));
++ assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
++ row(5), row(4), row(3), row(2), row(1), row(0));
++ }
}
/**
@@@ -247,8 -246,8 +275,14 @@@
for (int i = 0; i < 10; i++)
execute("INSERT INTO %s (k, c, v) VALUES (0, ?, ?)", i, i);
-- assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC"),
-- row(9), row(8), row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC"),
++ row(9), row(8), row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
++ }
createTable("CREATE TABLE %s (k int, c1 int, c2 int, v int, PRIMARY KEY (k, c1, c2)) WITH COMPACT STORAGE");
@@@ -256,15 -255,15 +290,21 @@@
for (int j = 0; j < 2; j++)
execute("INSERT INTO %s (k, c1, c2, v) VALUES (0, ?, ?, ?)", i, j, i * 2 + j);
-- assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC");
-- assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c2 DESC");
-- assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY k DESC");
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC");
++ assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c2 DESC");
++ assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY k DESC");
-- assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1 DESC"),
-- row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
++ assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1 DESC"),
++ row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
-- assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1"),
-- row(0), row(1), row(2), row(3), row(4), row(5), row(6), row(7));
++ assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1"),
++ row(0), row(1), row(2), row(3), row(4), row(5), row(6), row(7));
++ }
}
/**
@@@ -281,23 -280,23 +321,29 @@@
execute("INSERT INTO %s (row, number, string) VALUES ('row', 3, 'three')");
execute("INSERT INTO %s (row, number, string) VALUES ('row', 4, 'four')");
-- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number ASC"),
-- row(1), row(2));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number ASC"),
++ row(1), row(2));
-- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number ASC"),
-- row(3), row(4));
++ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number ASC"),
++ row(3), row(4));
-- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number DESC"),
-- row(2), row(1));
++ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number DESC"),
++ row(2), row(1));
-- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number DESC"),
-- row(4), row(3));
++ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number DESC"),
++ row(4), row(3));
-- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number > 3 ORDER BY number DESC"),
-- row(4));
++ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number > 3 ORDER BY number DESC"),
++ row(4));
-- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number <= 3 ORDER BY number DESC"),
-- row(3), row(2), row(1));
++ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number <= 3 ORDER BY number DESC"),
++ row(3), row(2), row(1));
++ }
}
/**
@@@ -330,14 -329,14 +376,20 @@@
execute("INSERT INTO %s (my_id, col1, value) VALUES ( 'key3', 2, 'b')");
execute("INSERT INTO %s (my_id, col1, value) VALUES ( 'key4', 4, 'd')");
-- assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
-- row(1), row(2), row(3));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
+
- assertRows(execute("SELECT col1, my_id FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
- row(1, "key1"), row(2, "key3"), row(3, "key2"));
++ assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
++ row(1), row(2), row(3));
- assertRows(execute("SELECT my_id, col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
- row("key1", 1), row("key3", 2), row("key2", 3));
- assertRows(execute("SELECT col1, my_id FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
- row(1, "key1"), row(2, "key3"), row(3, "key2"));
++ assertRows(execute("SELECT col1, my_id FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
++ row(1, "key1"), row(2, "key3"), row(3, "key2"));
+
- assertRows(execute("SELECT my_id, col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
- row("key1", 1), row("key3", 2), row("key2", 3));
++ assertRows(execute("SELECT my_id, col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
++ row("key1", 1), row("key3", 2), row("key2", 3));
++ }
createTable("CREATE TABLE %s (pk1 int, pk2 int, c int, v text, PRIMARY KEY ((pk1, pk2), c) )");
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 1, 2, "A");
@@@ -345,25 -344,25 +397,31 @@@
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 3, 3, "C");
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 1, 4, "D");
-- assertRows(execute("SELECT v, ttl(v), c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
-- row("B", null, 1),
-- row("A", null, 2),
-- row("D", null, 4));
--
-- assertRows(execute("SELECT v, ttl(v), c as name_1 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
-- row("B", null, 1),
-- row("A", null, 2),
-- row("D", null, 4));
--
-- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
-- row("B"),
-- row("A"),
-- row("D"));
--
-- assertRows(execute("SELECT v as c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
-- row("B"),
-- row("A"),
-- row("D"));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT v, ttl(v), c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B", null, 1),
++ row("A", null, 2),
++ row("D", null, 4));
++
++ assertRows(execute("SELECT v, ttl(v), c as name_1 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B", null, 1),
++ row("A", null, 2),
++ row("D", null, 4));
++
++ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B"),
++ row("A"),
++ row("D"));
++
++ assertRows(execute("SELECT v as c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B"),
++ row("A"),
++ row("D"));
++ }
createTable("CREATE TABLE %s (pk1 int, pk2 int, c1 int, c2 int, v text, PRIMARY KEY ((pk1, pk2), c1, c2) )");
execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", 1, 1, 4, 4, "A");
@@@ -371,68 -370,25 +429,80 @@@
execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", 1, 3, 3, 3, "C");
execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", 1, 1, 4, 1, "D");
-- assertRows(execute("SELECT v, ttl(v), c1, c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
-- row("B", null, 1, 2),
-- row("D", null, 4, 1),
-- row("A", null, 4, 4));
--
-- assertRows(execute("SELECT v, ttl(v), c1 as name_1, c2 as name_2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
-- row("B", null, 1, 2),
-- row("D", null, 4, 1),
-- row("A", null, 4, 4));
--
-- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
-- row("B"),
-- row("D"),
-- row("A"));
--
-- assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
-- row("B"),
-- row("D"),
-- row("A"));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT v, ttl(v), c1, c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
++ row("B", null, 1, 2),
++ row("D", null, 4, 1),
++ row("A", null, 4, 4));
++
++ assertRows(execute("SELECT v, ttl(v), c1 as name_1, c2 as name_2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
++ row("B", null, 1, 2),
++ row("D", null, 4, 1),
++ row("A", null, 4, 4));
++
++ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
++ row("B"),
++ row("D"),
++ row("A"));
++
++ assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
++ row("B"),
++ row("D"),
++ row("A"));
++ }
+ }
+
+ @Test
+ public void testOrderByForInClauseWithNullValue() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c int, s int static, d int, PRIMARY KEY (a, b, c))");
+
+ execute("INSERT INTO %s (a, b, c, d) VALUES (1, 1, 1, 1)");
+ execute("INSERT INTO %s (a, b, c, d) VALUES (1, 1, 2, 1)");
+ execute("INSERT INTO %s (a, b, c, d) VALUES (2, 2, 1, 1)");
+ execute("INSERT INTO %s (a, b, c, d) VALUES (2, 2, 2, 1)");
+
+ execute("UPDATE %s SET s = 1 WHERE a = 1");
+ execute("UPDATE %s SET s = 2 WHERE a = 2");
+ execute("UPDATE %s SET s = 3 WHERE a = 3");
+
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC"),
- row(2, 2, 2, 1, 2),
- row(2, 2, 1, 1, 2),
- row(1, 1, 2, 1, 1),
- row(1, 1, 1, 1, 1),
- row(3, null, null, null, 3));
-
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC"),
- row(3, null, null, null, 3),
- row(1, 1, 1, 1, 1),
- row(1, 1, 2, 1, 1),
- row(2, 2, 1, 1, 2),
- row(2, 2, 2, 1, 2));
-
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC , c DESC"),
- row(2, 2, 2, 1, 2),
- row(2, 2, 1, 1, 2),
- row(1, 1, 2, 1, 1),
- row(1, 1, 1, 1, 1),
- row(3, null, null, null, 3));
-
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC, c ASC"),
- row(3, null, null, null, 3),
- row(1, 1, 1, 1, 1),
- row(1, 1, 2, 1, 1),
- row(2, 2, 1, 1, 2),
- row(2, 2, 2, 1, 2));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC"),
++ row(2, 2, 2, 1, 2),
++ row(2, 2, 1, 1, 2),
++ row(1, 1, 2, 1, 1),
++ row(1, 1, 1, 1, 1),
++ row(3, null, null, null, 3));
++
++ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC"),
++ row(3, null, null, null, 3),
++ row(1, 1, 1, 1, 1),
++ row(1, 1, 2, 1, 1),
++ row(2, 2, 1, 1, 2),
++ row(2, 2, 2, 1, 2));
++
++ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC , c DESC"),
++ row(2, 2, 2, 1, 2),
++ row(2, 2, 1, 1, 2),
++ row(1, 1, 2, 1, 1),
++ row(1, 1, 1, 1, 1),
++ row(3, null, null, null, 3));
++
++ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC, c ASC"),
++ row(3, null, null, null, 3),
++ row(1, 1, 1, 1, 1),
++ row(1, 1, 2, 1, 1),
++ row(2, 2, 1, 1, 2),
++ row(2, 2, 2, 1, 2));
++ }
}
/**
@@@ -447,13 -403,13 +517,19 @@@
for(int i =0; i < 10; i++)
execute("INSERT INTO %s (k, c, v) VALUES (0, ?, ?)", i, i);
-- assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c ASC"),
-- row(0, 0), row(1, 1), row(2, 2), row(3, 3), row(4, 4),
-- row(5, 5), row(6, 6), row(7, 7), row(8, 8), row(9, 9));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c ASC"),
++ row(0, 0), row(1, 1), row(2, 2), row(3, 3), row(4, 4),
++ row(5, 5), row(6, 6), row(7, 7), row(8, 8), row(9, 9));
-- assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c DESC"),
-- row(9, 9), row(8, 8), row(7, 7), row(6, 6), row(5, 5),
-- row(4, 4), row(3, 3), row(2, 2), row(1, 1), row(0, 0));
++ assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c DESC"),
++ row(9, 9), row(8, 8), row(7, 7), row(6, 6), row(5, 5),
++ row(4, 4), row(3, 3), row(2, 2), row(1, 1), row(0, 0));
++ }
createTable("CREATE TABLE %s (k int, c1 int, c2 int, v text, PRIMARY KEY (k, c1, c2)) WITH CLUSTERING ORDER BY (c1 ASC, c2 DESC)");
@@@ -461,28 -417,28 +537,34 @@@
for(int j = 0; j < 10; j++)
execute("INSERT INTO %s (k, c1, c2, v) VALUES (0, ?, ?, ?)", i, j, String.format("%d%d", i, j));
-- assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 ASC");
-- assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 DESC");
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
-- Object[][] expectedRows = new Object[100][];
-- for(int i = 0; i < 10; i++)
-- for(int j = 9; j >= 0; j--)
-- expectedRows[i * 10 + (9 - j)] = row(i, j, String.format("%d%d", i, j));
++ assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 ASC");
++ assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 DESC");
-- assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC"),
-- expectedRows);
++ Object[][] expectedRows = new Object[100][];
++ for(int i = 0; i < 10; i++)
++ for(int j = 9; j >= 0; j--)
++ expectedRows[i * 10 + (9 - j)] = row(i, j, String.format("%d%d", i, j));
-- assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC"),
-- expectedRows);
++ assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC"),
++ expectedRows);
-- for(int i = 9; i >= 0; i--)
-- for(int j = 0; j < 10; j++)
-- expectedRows[(9 - i) * 10 + j] = row(i, j, String.format("%d%d", i, j));
++ assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC"),
++ expectedRows);
++
++ for(int i = 9; i >= 0; i--)
++ for(int j = 0; j < 10; j++)
++ expectedRows[(9 - i) * 10 + j] = row(i, j, String.format("%d%d", i, j));
-- assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 ASC"),
-- expectedRows);
++ assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 ASC"),
++ expectedRows);
-- assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c2 DESC, c1 ASC");
++ assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c2 DESC, c1 ASC");
++ }
}
/**
@@@ -497,18 -453,18 +579,24 @@@
for (int j = 0; j < 2; j++)
execute("INSERT INTO %s (k, c1, c2) VALUES ('foo', ?, ?)", i, j);
-- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo'"),
-- row(0, 1), row(0, 0), row(1, 1), row(1, 0));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo'"),
++ row(0, 1), row(0, 0), row(1, 1), row(1, 0));
-- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 DESC"),
-- row(0, 1), row(0, 0), row(1, 1), row(1, 0));
++ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 DESC"),
++ row(0, 1), row(0, 0), row(1, 1), row(1, 0));
-- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 DESC, c2 ASC"),
-- row(1, 0), row(1, 1), row(0, 0), row(0, 1));
++ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 DESC, c2 ASC"),
++ row(1, 0), row(1, 1), row(0, 0), row(0, 1));
-- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 DESC");
-- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 ASC");
-- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 ASC");
++ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 DESC");
++ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 ASC");
++ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 ASC");
++ }
}
/**
@@@ -522,9 -478,9 +610,15 @@@
execute("INSERT INTO %s(k, c1, c2) VALUES (0, 0, 1)");
execute("INSERT INTO %s(k, c1, c2) VALUES (0, 0, 2)");
-- assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
-- row(0, 0, 2),
-- row(0, 0, 0));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
++ row(0, 0, 2),
++ row(0, 0, 0));
++ }
}
/**
@@@ -543,87 -499,44 +637,99 @@@
execute("INSERT INTO %s (k, c1, c2, v) VALUES (1, 1, 1, 4)");
execute("INSERT INTO %s (k, c1, c2, v) VALUES (1, 1, 2, 5)");
-- assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
-- row(0, 0, 0, 0),
-- row(0, 0, 2, 2));
-- assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC, c2 ASC"),
-- row(0, 0, 0, 0),
-- row(0, 0, 2, 2));
--
-- // check that we don 't need to select the column on which we order
-- assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
-- row(0),
-- row(2));
-- assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC"),
-- row(0),
-- row(2));
-- assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
-- row(2),
-- row(0));
-
-- assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0)"),
- row(0),
- row(1),
- row(2),
-- row(3),
-- row(4),
- row(5));
- row(5),
- row(0),
- row(1),
- row(2));
--
-- assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"),
-- row(0),
-- row(1),
-- row(2),
-- row(3),
-- row(4),
-- row(5));
--
-- // we should also be able to use functions in the select clause (additional test for CASSANDRA - 8286)
-- Object[][] results = getRows(execute("SELECT writetime(v) FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"));
--
-- // since we don 't know the write times, just assert that the order matches the order we expect
-- assertTrue(isFirstIntSorted(results));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
++ row(0, 0, 0, 0),
++ row(0, 0, 2, 2));
++ assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC, c2 ASC"),
++ row(0, 0, 0, 0),
++ row(0, 0, 2, 2));
++
++ // check that we don 't need to select the column on which we order
++ assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
++ row(0),
++ row(2));
++ assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC"),
++ row(0),
++ row(2));
++ assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
++ row(2),
++ row(0));
++
++ assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0)"),
++ row(0),
++ row(1),
++ row(2),
++ row(3),
++ row(4),
++ row(5));
++
++ assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"),
++ row(0),
++ row(1),
++ row(2),
++ row(3),
++ row(4),
++ row(5));
++
++ // we should also be able to use functions in the select clause (additional test for CASSANDRA - 8286)
++ Object[][] results = getRows(execute("SELECT writetime(v) FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"));
++
++ // since we don 't know the write times, just assert that the order matches the order we expect
++ assertTrue(isFirstIntSorted(results));
++ }
+ }
+
+ @Test
+ public void testInOrderByWithTwoPartitionKeyColumns() throws Throwable
+ {
+ for (String option : asList("", "WITH CLUSTERING ORDER BY (col_3 DESC)"))
+ {
+ createTable("CREATE TABLE %s (col_1 int, col_2 int, col_3 int, PRIMARY KEY ((col_1, col_2), col_3)) " + option);
+ execute("INSERT INTO %s (col_1, col_2, col_3) VALUES(?, ?, ?)", 1, 1, 1);
+ execute("INSERT INTO %s (col_1, col_2, col_3) VALUES(?, ?, ?)", 1, 1, 2);
+ execute("INSERT INTO %s (col_1, col_2, col_3) VALUES(?, ?, ?)", 1, 1, 13);
+ execute("INSERT INTO %s (col_1, col_2, col_3) VALUES(?, ?, ?)", 1, 2, 10);
+ execute("INSERT INTO %s (col_1, col_2, col_3) VALUES(?, ?, ?)", 1, 2, 11);
+
- assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3;", 1, 1, 2),
- row(1, 1, 1),
- row(1, 1, 2),
- row(1, 2, 10),
- row(1, 2, 11),
- row(1, 1, 13));
-
- assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3 desc;", 1, 1, 2),
- row(1, 1, 13),
- row(1, 2, 11),
- row(1, 2, 10),
- row(1, 1, 2),
- row(1, 1, 1));
-
- assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3;", 1, 2, 1),
- row(1, 1, 1),
- row(1, 1, 2),
- row(1, 2, 10),
- row(1, 2, 11),
- row(1, 1, 13));
-
- assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3 desc;", 1, 2, 1),
- row(1, 1, 13),
- row(1, 2, 11),
- row(1, 2, 10),
- row(1, 1, 2),
- row(1, 1, 1));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3;", 1, 1, 2),
++ row(1, 1, 1),
++ row(1, 1, 2),
++ row(1, 2, 10),
++ row(1, 2, 11),
++ row(1, 1, 13));
++
++ assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3 desc;", 1, 1, 2),
++ row(1, 1, 13),
++ row(1, 2, 11),
++ row(1, 2, 10),
++ row(1, 1, 2),
++ row(1, 1, 1));
++
++ assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3;", 1, 2, 1),
++ row(1, 1, 1),
++ row(1, 1, 2),
++ row(1, 2, 10),
++ row(1, 2, 11),
++ row(1, 1, 13));
++
++ assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3 desc;", 1, 2, 1),
++ row(1, 1, 13),
++ row(1, 2, 11),
++ row(1, 2, 10),
++ row(1, 1, 2),
++ row(1, 1, 1));
++ }
+ }
}
private boolean isFirstIntSorted(Object[][] rows)
[50/50] [abbrv] cassandra git commit: Update CHANGES.txt for 3.8
Posted by ms...@apache.org.
Update CHANGES.txt for 3.8
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/c1e9ad8c
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/c1e9ad8c
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/c1e9ad8c
Branch: refs/heads/cassandra-3.8
Commit: c1e9ad8cde9f0eec6c87fc19db45460eb8e8f500
Parents: cb97649
Author: Michael Shuler <ms...@apache.org>
Authored: Tue Aug 16 17:37:37 2016 -0500
Committer: Michael Shuler <ms...@apache.org>
Committed: Tue Aug 16 17:37:37 2016 -0500
----------------------------------------------------------------------
CHANGES.txt | 54 ++++++++++++++++++++----------------------------------
1 file changed, 20 insertions(+), 34 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/c1e9ad8c/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 2c83c42..b27d974 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,43 +1,10 @@
-3.9
+3.8
* Fix value skipping with counter columns (CASSANDRA-11726)
* Fix nodetool tablestats miss SSTable count (CASSANDRA-12205)
* Fixed flacky SSTablesIteratedTest (CASSANDRA-12282)
* Fixed flacky SSTableRewriterTest: check file counts before calling validateCFS (CASSANDRA-12348)
* cqlsh: Fix handling of $$-escaped strings (CASSANDRA-12189)
* Fix SSL JMX requiring truststore containing server cert (CASSANDRA-12109)
-Merged from 3.0:
- * Fix clean interval not sent to commit log for empty memtable flush (CASSANDRA-12436)
- * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
- * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
- * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
- * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
- * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
- * Fix upgrade of super columns on thrift (CASSANDRA-12335)
- * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
- * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
- * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
- * Lost counter writes in compact table and static columns (CASSANDRA-12219)
- * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
- * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
- * Add option to override compaction space check (CASSANDRA-12180)
- * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
- * Respond with v1/v2 protocol header when responding to driver that attempts
- to connect with too low of a protocol version (CASSANDRA-11464)
- * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
- * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
-Merged from 2.2:
- * Fix hanging stream session (CASSANDRA-10992)
- * Fix INSERT JSON, fromJson() support of smallint, tinyint types (CASSANDRA-12371)
- * Release sstables of failed stream sessions only when outgoing transfers are finished (CASSANDRA-11345)
- * Wait for tracing events before returning response and query at same consistency level client side (CASSANDRA-11465)
- * cqlsh copyutil should get host metadata by connected address (CASSANDRA-11979)
- * Fixed cqlshlib.test.remove_test_db (CASSANDRA-12214)
-Merged from 2.1:
- * cannot use cql since upgrading python to 2.7.11+ (CASSANDRA-11850)
- * Allow STCS-in-L0 compactions to reduce scope with LCS (CASSANDRA-12040)
-
-
-3.8
* RTE from new CDC column breaks in flight queries (CASSANDRA-12236)
* Fix hdr logging for single operation workloads (CASSANDRA-12145)
* Fix SASI PREFIX search in CONTAINS mode with partial terms (CASSANDRA-12073)
@@ -67,6 +34,25 @@ Merged from 2.1:
* Add repaired percentage metric (CASSANDRA-11503)
* Add Change-Data-Capture (CASSANDRA-8844)
Merged from 3.0:
+ * Fix clean interval not sent to commit log for empty memtable flush (CASSANDRA-12436)
+ * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
+ * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
+ * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
+ * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
+ * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
+ * Fix upgrade of super columns on thrift (CASSANDRA-12335)
+ * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
+ * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
+ * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
+ * Lost counter writes in compact table and static columns (CASSANDRA-12219)
+ * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
+ * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
+ * Add option to override compaction space check (CASSANDRA-12180)
+ * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
+ * Respond with v1/v2 protocol header when responding to driver that attempts
+ to connect with too low of a protocol version (CASSANDRA-11464)
+ * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
+ * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
* Fix potential bad messaging service message for paged range reads
within mixed-version 3.x clusters (CASSANDRA-12249)
* Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
[46/50] [abbrv] cassandra git commit: Merge branch cassandra-2.2 into
cassandra-3.0
Posted by ms...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
index 6e06419,32d800a..bb35458
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
@@@ -36,31 -36,37 +36,33 @@@ public class SelectOrderByTest extends
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 1, 1);
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 2, 2);
- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0, 0, 0),
- row(0, 1, 1),
- row(0, 2, 2)
- );
-
- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(0, 2, 2),
- row(0, 1, 1),
- row(0, 0, 0)
- );
-
- // order by the only column in the selection
- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
-
- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
-
- // order by a column not in the selection
- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
--
- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0, 0, 0),
+ row(0, 1, 1),
+ row(0, 2, 2)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(0, 2, 2),
+ row(0, 1, 1),
+ row(0, 0, 0)
+ );
+
+ // order by the only column in the selection
+ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(1), row(2));
+
+ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0));
+
+ // order by a column not in the selection
+ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(1), row(2));
+
+ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0));
- }
++ });
}
}
@@@ -74,22 -80,28 +76,24 @@@
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 1, 1);
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 2, 2);
- // order by the only column in the selection
- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ // order by the only column in the selection
+ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(1), row(2));
- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
+ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0));
- // order by a column not in the selection
- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
+ // order by a column not in the selection
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(1), row(2));
- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0));
- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
+ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
+ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
- }
++ });
}
}
@@@ -105,15 -117,20 +109,17 @@@
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, {a: ?})", 0, 1, 1);
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, {a: ?})", 0, 2, 2);
- // order by a column not in the selection
- assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
-
- assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
-
- assertRows(execute("SELECT blobAsInt(intAsBlob(c.a)) FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+ // order by a column not in the selection
+ assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
-
++ row(0), row(1), row(2));
++
+ assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
-
++ row(2), row(1), row(0));
++
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c.a)) FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0));
- }
++ });
dropTable("DROP TABLE %s");
}
}
@@@ -129,62 -146,68 +135,64 @@@
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 1, 4);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 2, 5);
- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0, 0, 0, 0),
- row(0, 0, 1, 1),
- row(0, 0, 2, 2),
- row(0, 1, 0, 3),
- row(0, 1, 1, 4),
- row(0, 1, 2, 5)
- );
-
- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(0, 1, 2, 5),
- row(0, 1, 1, 4),
- row(0, 1, 0, 3),
- row(0, 0, 2, 2),
- row(0, 0, 1, 1),
- row(0, 0, 0, 0)
- );
-
- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
- row(0, 1, 2, 5),
- row(0, 1, 1, 4),
- row(0, 1, 0, 3),
- row(0, 0, 2, 2),
- row(0, 0, 1, 1),
- row(0, 0, 0, 0)
- );
-
- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY d ASC", 0);
-
- // select and order by b
- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(0), row(0), row(1), row(1), row(1));
- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(1), row(1), row(1), row(0), row(0), row(0));
-
- // select c, order by b
- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2), row(0), row(1), row(2));
- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0), row(2), row(1), row(0));
-
- // select c, order by b, c
- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
- row(0), row(1), row(2), row(0), row(1), row(2));
- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
- row(2), row(1), row(0), row(2), row(1), row(0));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
--
- // select d, order by b, c
- assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
- row(0), row(1), row(2), row(3), row(4), row(5));
- assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
- row(5), row(4), row(3), row(2), row(1), row(0));
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0, 0, 0, 0),
+ row(0, 0, 1, 1),
+ row(0, 0, 2, 2),
+ row(0, 1, 0, 3),
+ row(0, 1, 1, 4),
+ row(0, 1, 2, 5)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(0, 1, 2, 5),
+ row(0, 1, 1, 4),
+ row(0, 1, 0, 3),
+ row(0, 0, 2, 2),
+ row(0, 0, 1, 1),
+ row(0, 0, 0, 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
+ row(0, 1, 2, 5),
+ row(0, 1, 1, 4),
+ row(0, 1, 0, 3),
+ row(0, 0, 2, 2),
+ row(0, 0, 1, 1),
+ row(0, 0, 0, 0)
+ );
+
+ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
+ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
+ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
+ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
+ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY d ASC", 0);
+
+ // select and order by b
+ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(0), row(0), row(1), row(1), row(1));
+ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(1), row(1), row(1), row(0), row(0), row(0));
+
+ // select c, order by b
+ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(1), row(2), row(0), row(1), row(2));
+ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0), row(2), row(1), row(0));
+
+ // select c, order by b, c
+ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
+ row(0), row(1), row(2), row(0), row(1), row(2));
+ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
+ row(2), row(1), row(0), row(2), row(1), row(0));
+
+ // select d, order by b, c
+ assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
+ row(0), row(1), row(2), row(3), row(4), row(5));
+ assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
+ row(5), row(4), row(3), row(2), row(1), row(0));
- }
++ });
}
@Test
@@@ -198,41 -221,46 +206,42 @@@
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 1, 4);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 2, 5);
- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c ASC", 0);
- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c DESC", 0);
- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY d ASC", 0);
-
- // select and order by b
- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(0), row(0), row(1), row(1), row(1));
- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(1), row(1), row(1), row(0), row(0), row(0));
-
- assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0, 0), row(0, 0), row(0, 0), row(1, 1), row(1, 1), row(1, 1));
- assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(1, 1), row(1, 1), row(1, 1), row(0, 0), row(0, 0), row(0, 0));
-
- // select c, order by b
- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2), row(0), row(1), row(2));
- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0), row(2), row(1), row(0));
-
- // select c, order by b, c
- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
- row(0), row(1), row(2), row(0), row(1), row(2));
- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
- row(2), row(1), row(0), row(2), row(1), row(0));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
--
- // select d, order by b, c
- assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
- row(0), row(1), row(2), row(3), row(4), row(5));
- assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
- row(5), row(4), row(3), row(2), row(1), row(0));
++ beforeAndAfterFlush(() -> {
+ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c ASC", 0);
+ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c DESC", 0);
+ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
+ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
+ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY d ASC", 0);
+ // select and order by b
+ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(0), row(0), row(1), row(1), row(1));
+ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(1), row(1), row(1), row(0), row(0), row(0));
+
+ assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0, 0), row(0, 0), row(0, 0), row(1, 1), row(1, 1), row(1, 1));
+ assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(1, 1), row(1, 1), row(1, 1), row(0, 0), row(0, 0), row(0, 0));
+
+ // select c, order by b
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(1), row(2), row(0), row(1), row(2));
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0), row(2), row(1), row(0));
+
+ // select c, order by b, c
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
+ row(0), row(1), row(2), row(0), row(1), row(2));
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
+ row(2), row(1), row(0), row(2), row(1), row(0));
+
+ // select d, order by b, c
+ assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
+ row(0), row(1), row(2), row(3), row(4), row(5));
+ assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
+ row(5), row(4), row(3), row(2), row(1), row(0));
- }
++ });
}
/**
@@@ -247,8 -275,14 +256,10 @@@
for (int i = 0; i < 10; i++)
execute("INSERT INTO %s (k, c, v) VALUES (0, ?, ?)", i, i);
- assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC"),
- row(9), row(8), row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC"),
+ row(9), row(8), row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
- }
++ });
createTable("CREATE TABLE %s (k int, c1 int, c2 int, v int, PRIMARY KEY (k, c1, c2)) WITH COMPACT STORAGE");
@@@ -256,15 -290,21 +267,17 @@@
for (int j = 0; j < 2; j++)
execute("INSERT INTO %s (k, c1, c2, v) VALUES (0, ?, ?, ?)", i, j, i * 2 + j);
- assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC");
- assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c2 DESC");
- assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY k DESC");
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC");
+ assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c2 DESC");
+ assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY k DESC");
- assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1 DESC"),
- row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
+ assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1 DESC"),
+ row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
- assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1"),
- row(0), row(1), row(2), row(3), row(4), row(5), row(6), row(7));
+ assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1"),
+ row(0), row(1), row(2), row(3), row(4), row(5), row(6), row(7));
- }
++ });
}
/**
@@@ -281,23 -321,29 +294,25 @@@
execute("INSERT INTO %s (row, number, string) VALUES ('row', 3, 'three')");
execute("INSERT INTO %s (row, number, string) VALUES ('row', 4, 'four')");
- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number ASC"),
- row(1), row(2));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number ASC"),
+ row(1), row(2));
- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number ASC"),
- row(3), row(4));
+ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number ASC"),
+ row(3), row(4));
- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number DESC"),
- row(2), row(1));
+ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number DESC"),
+ row(2), row(1));
- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number DESC"),
- row(4), row(3));
+ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number DESC"),
+ row(4), row(3));
- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number > 3 ORDER BY number DESC"),
- row(4));
+ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number > 3 ORDER BY number DESC"),
+ row(4));
- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number <= 3 ORDER BY number DESC"),
- row(3), row(2), row(1));
+ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number <= 3 ORDER BY number DESC"),
+ row(3), row(2), row(1));
- }
++ });
}
/**
@@@ -330,20 -376,20 +345,22 @@@
execute("INSERT INTO %s (my_id, col1, value) VALUES ( 'key3', 2, 'b')");
execute("INSERT INTO %s (my_id, col1, value) VALUES ( 'key4', 4, 'd')");
- assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
- row(1), row(2), row(3));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
+ row(1), row(2), row(3));
- assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1 LIMIT 2"),
- row(1), row(2));
++ assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1 LIMIT 2"),
++ row(1), row(2));
+
- assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1 LIMIT 10"),
- row(1), row(2), row(3));
++ assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1 LIMIT 10"),
++ row(1), row(2), row(3));
+
- assertRows(execute("SELECT col1, my_id FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
- row(1, "key1"), row(2, "key3"), row(3, "key2"));
+ assertRows(execute("SELECT col1, my_id FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
+ row(1, "key1"), row(2, "key3"), row(3, "key2"));
- assertRows(execute("SELECT my_id, col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
- row("key1", 1), row("key3", 2), row("key2", 3));
+ assertRows(execute("SELECT my_id, col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
+ row("key1", 1), row("key3", 2), row("key2", 3));
- }
++ });
createTable("CREATE TABLE %s (pk1 int, pk2 int, c int, v text, PRIMARY KEY ((pk1, pk2), c) )");
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 1, 2, "A");
@@@ -351,34 -397,31 +368,36 @@@
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 3, 3, "C");
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 1, 4, "D");
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
- assertRows(execute("SELECT v, ttl(v), c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B", null, 1),
- row("A", null, 2),
- row("D", null, 4));
-
- assertRows(execute("SELECT v, ttl(v), c as name_1 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B", null, 1),
- row("A", null, 2),
- row("D", null, 4));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
-
- assertRows(execute("SELECT v as c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
- }
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT v, ttl(v), c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
+ row("B", null, 1),
+ row("A", null, 2),
+ row("D", null, 4));
+
+ assertRows(execute("SELECT v, ttl(v), c as name_1 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
+ row("B", null, 1),
+ row("A", null, 2),
+ row("D", null, 4));
+
+ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
+ row("B"),
+ row("A"),
+ row("D"));
+
+ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 2; ", 1, 1, 2),
+ row("B"),
+ row("A"));
+
+ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 10; ", 1, 1, 2),
+ row("B"),
+ row("A"),
+ row("D"));
+
+ assertRows(execute("SELECT v as c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
+ row("B"),
+ row("A"),
+ row("D"));
++ });
createTable("CREATE TABLE %s (pk1 int, pk2 int, c1 int, c2 int, v text, PRIMARY KEY ((pk1, pk2), c1, c2) )");
execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", 1, 1, 4, 4, "A");
@@@ -386,51 -429,31 +405,53 @@@
execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", 1, 3, 3, 3, "C");
execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", 1, 1, 4, 1, "D");
- assertRows(execute("SELECT v, ttl(v), c1, c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
- row("B", null, 1, 2),
- row("D", null, 4, 1),
- row("A", null, 4, 4));
-
- assertRows(execute("SELECT v, ttl(v), c1 as name_1, c2 as name_2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
- row("B", null, 1, 2),
- row("D", null, 4, 1),
- row("A", null, 4, 4));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
- row("B"),
- row("D"),
- row("A"));
-
- assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
- row("B"),
- row("D"),
- row("A"));
-
- assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2 LIMIT 2; ", 1, 1, 2),
- row("B"),
- row("D"));
-
- assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2 LIMIT 10; ", 1, 1, 2),
- row("B"),
- row("D"),
- row("A"));
-
- assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC; ", 1, 1, 2),
- row("A"),
- row("D"),
- row("B"));
-
- assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC LIMIT 2; ", 1, 1, 2),
- row("A"),
- row("D"));
-
- assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC LIMIT 10; ", 1, 1, 2),
- row("A"),
- row("D"),
- row("B"));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
--
- assertInvalidMessage("LIMIT must be strictly positive",
- "SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC LIMIT 0; ", 1, 1, 2);
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT v, ttl(v), c1, c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
+ row("B", null, 1, 2),
+ row("D", null, 4, 1),
+ row("A", null, 4, 4));
+
+ assertRows(execute("SELECT v, ttl(v), c1 as name_1, c2 as name_2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
+ row("B", null, 1, 2),
+ row("D", null, 4, 1),
+ row("A", null, 4, 4));
+
+ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
+ row("B"),
+ row("D"),
+ row("A"));
+
+ assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
+ row("B"),
+ row("D"),
+ row("A"));
- }
++
++ assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2 LIMIT 2; ", 1, 1, 2),
++ row("B"),
++ row("D"));
++
++ assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2 LIMIT 10; ", 1, 1, 2),
++ row("B"),
++ row("D"),
++ row("A"));
++
++ assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC; ", 1, 1, 2),
++ row("A"),
++ row("D"),
++ row("B"));
++
++ assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC LIMIT 2; ", 1, 1, 2),
++ row("A"),
++ row("D"));
++
++ assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC LIMIT 10; ", 1, 1, 2),
++ row("A"),
++ row("D"),
++ row("B"));
++
++ assertInvalidMessage("LIMIT must be strictly positive",
++ "SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC LIMIT 0; ", 1, 1, 2);
++ });
}
@Test
@@@ -447,33 -470,39 +468,35 @@@
execute("UPDATE %s SET s = 2 WHERE a = 2");
execute("UPDATE %s SET s = 3 WHERE a = 3");
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC"),
- row(2, 2, 2, 1, 2),
- row(2, 2, 1, 1, 2),
- row(1, 1, 2, 1, 1),
- row(1, 1, 1, 1, 1),
- row(3, null, null, null, 3));
-
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC"),
- row(3, null, null, null, 3),
- row(1, 1, 1, 1, 1),
- row(1, 1, 2, 1, 1),
- row(2, 2, 1, 1, 2),
- row(2, 2, 2, 1, 2));
-
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC , c DESC"),
- row(2, 2, 2, 1, 2),
- row(2, 2, 1, 1, 2),
- row(1, 1, 2, 1, 1),
- row(1, 1, 1, 1, 1),
- row(3, null, null, null, 3));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
--
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC, c ASC"),
- row(3, null, null, null, 3),
- row(1, 1, 1, 1, 1),
- row(1, 1, 2, 1, 1),
- row(2, 2, 1, 1, 2),
- row(2, 2, 2, 1, 2));
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC"),
+ row(2, 2, 2, 1, 2),
+ row(2, 2, 1, 1, 2),
+ row(1, 1, 2, 1, 1),
+ row(1, 1, 1, 1, 1),
+ row(3, null, null, null, 3));
+
+ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC"),
+ row(3, null, null, null, 3),
+ row(1, 1, 1, 1, 1),
+ row(1, 1, 2, 1, 1),
+ row(2, 2, 1, 1, 2),
+ row(2, 2, 2, 1, 2));
+
+ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC , c DESC"),
+ row(2, 2, 2, 1, 2),
+ row(2, 2, 1, 1, 2),
+ row(1, 1, 2, 1, 1),
+ row(1, 1, 1, 1, 1),
+ row(3, null, null, null, 3));
+
+ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC, c ASC"),
+ row(3, null, null, null, 3),
+ row(1, 1, 1, 1, 1),
+ row(1, 1, 2, 1, 1),
+ row(2, 2, 1, 1, 2),
+ row(2, 2, 2, 1, 2));
- }
++ });
}
/**
@@@ -488,13 -517,19 +511,15 @@@
for(int i =0; i < 10; i++)
execute("INSERT INTO %s (k, c, v) VALUES (0, ?, ?)", i, i);
- assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c ASC"),
- row(0, 0), row(1, 1), row(2, 2), row(3, 3), row(4, 4),
- row(5, 5), row(6, 6), row(7, 7), row(8, 8), row(9, 9));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c ASC"),
+ row(0, 0), row(1, 1), row(2, 2), row(3, 3), row(4, 4),
+ row(5, 5), row(6, 6), row(7, 7), row(8, 8), row(9, 9));
- assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c DESC"),
- row(9, 9), row(8, 8), row(7, 7), row(6, 6), row(5, 5),
- row(4, 4), row(3, 3), row(2, 2), row(1, 1), row(0, 0));
+ assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c DESC"),
+ row(9, 9), row(8, 8), row(7, 7), row(6, 6), row(5, 5),
+ row(4, 4), row(3, 3), row(2, 2), row(1, 1), row(0, 0));
- }
++ });
createTable("CREATE TABLE %s (k int, c1 int, c2 int, v text, PRIMARY KEY (k, c1, c2)) WITH CLUSTERING ORDER BY (c1 ASC, c2 DESC)");
@@@ -502,28 -537,34 +527,30 @@@
for(int j = 0; j < 10; j++)
execute("INSERT INTO %s (k, c1, c2, v) VALUES (0, ?, ?, ?)", i, j, String.format("%d%d", i, j));
- assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 ASC");
- assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 DESC");
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 ASC");
+ assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 DESC");
- Object[][] expectedRows = new Object[100][];
- for(int i = 0; i < 10; i++)
- for(int j = 9; j >= 0; j--)
- expectedRows[i * 10 + (9 - j)] = row(i, j, String.format("%d%d", i, j));
+ Object[][] expectedRows = new Object[100][];
+ for(int i = 0; i < 10; i++)
+ for(int j = 9; j >= 0; j--)
+ expectedRows[i * 10 + (9 - j)] = row(i, j, String.format("%d%d", i, j));
- assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC"),
- expectedRows);
+ assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC"),
+ expectedRows);
- assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC"),
- expectedRows);
+ assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC"),
+ expectedRows);
- for(int i = 9; i >= 0; i--)
- for(int j = 0; j < 10; j++)
- expectedRows[(9 - i) * 10 + j] = row(i, j, String.format("%d%d", i, j));
+ for(int i = 9; i >= 0; i--)
+ for(int j = 0; j < 10; j++)
+ expectedRows[(9 - i) * 10 + j] = row(i, j, String.format("%d%d", i, j));
- assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 ASC"),
- expectedRows);
+ assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 ASC"),
+ expectedRows);
- assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c2 DESC, c1 ASC");
+ assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c2 DESC, c1 ASC");
- }
++ });
}
/**
@@@ -534,22 -575,28 +561,24 @@@
{
createTable("CREATE TABLE %s (k text, c1 int, c2 int, PRIMARY KEY (k, c1, c2) ) WITH CLUSTERING ORDER BY (c1 ASC, c2 DESC)");
-- for (int i = 0; i < 2; i++)
-- for (int j = 0; j < 2; j++)
-- execute("INSERT INTO %s (k, c1, c2) VALUES ('foo', ?, ?)", i, j);
-
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
++ for (int i = 0; i < 2; i++)
++ for (int j = 0; j < 2; j++)
++ execute("INSERT INTO %s (k, c1, c2) VALUES ('foo', ?, ?)", i, j);
- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo'"),
- row(0, 1), row(0, 0), row(1, 1), row(1, 0));
+ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo'"),
+ row(0, 1), row(0, 0), row(1, 1), row(1, 0));
- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 DESC"),
- row(0, 1), row(0, 0), row(1, 1), row(1, 0));
+ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 DESC"),
+ row(0, 1), row(0, 0), row(1, 1), row(1, 0));
- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 DESC, c2 ASC"),
- row(1, 0), row(1, 1), row(0, 0), row(0, 1));
+ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 DESC, c2 ASC"),
+ row(1, 0), row(1, 1), row(0, 0), row(0, 1));
- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 DESC");
- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 ASC");
- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 ASC");
+ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 DESC");
+ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 ASC");
+ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 ASC");
- }
++ });
}
/**
@@@ -563,9 -610,15 +592,11 @@@
execute("INSERT INTO %s(k, c1, c2) VALUES (0, 0, 1)");
execute("INSERT INTO %s(k, c1, c2) VALUES (0, 0, 2)");
- assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
- row(0, 0, 2),
- row(0, 0, 0));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
+ row(0, 0, 2),
+ row(0, 0, 0));
- }
++ });
}
/**
@@@ -584,45 -637,51 +615,47 @@@
execute("INSERT INTO %s (k, c1, c2, v) VALUES (1, 1, 1, 4)");
execute("INSERT INTO %s (k, c1, c2, v) VALUES (1, 1, 2, 5)");
- assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
- row(0, 0, 0, 0),
- row(0, 0, 2, 2));
- assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC, c2 ASC"),
- row(0, 0, 0, 0),
- row(0, 0, 2, 2));
-
- // check that we don 't need to select the column on which we order
- assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
- row(0),
- row(2));
- assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC"),
- row(0),
- row(2));
- assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
- row(2),
- row(0));
-
- assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0)"),
- row(0),
- row(1),
- row(2),
- row(3),
- row(4),
- row(5));
-
- assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"),
- row(0),
- row(1),
- row(2),
- row(3),
- row(4),
- row(5));
-
- // we should also be able to use functions in the select clause (additional test for CASSANDRA - 8286)
- Object[][] results = getRows(execute("SELECT writetime(v) FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
--
- // since we don 't know the write times, just assert that the order matches the order we expect
- assertTrue(isFirstIntSorted(results));
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
+ row(0, 0, 0, 0),
+ row(0, 0, 2, 2));
+ assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC, c2 ASC"),
+ row(0, 0, 0, 0),
+ row(0, 0, 2, 2));
+
+ // check that we don 't need to select the column on which we order
+ assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
+ row(0),
+ row(2));
+ assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC"),
+ row(0),
+ row(2));
+ assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
+ row(2),
+ row(0));
+
+ assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0)"),
+ row(0),
+ row(1),
+ row(2),
+ row(3),
+ row(4),
+ row(5));
+
+ assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"),
+ row(0),
+ row(1),
+ row(2),
+ row(3),
+ row(4),
+ row(5));
+
+ // we should also be able to use functions in the select clause (additional test for CASSANDRA - 8286)
+ Object[][] results = getRows(execute("SELECT writetime(v) FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"));
+
+ // since we don 't know the write times, just assert that the order matches the order we expect
+ assertTrue(isFirstIntSorted(results));
- }
++ });
}
@Test
@@@ -637,33 -696,39 +670,35 @@@
execute("INSERT INTO %s (col_1, col_2, col_3) VALUES(?, ?, ?)", 1, 2, 10);
execute("INSERT INTO %s (col_1, col_2, col_3) VALUES(?, ?, ?)", 1, 2, 11);
- assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3;", 1, 1, 2),
- row(1, 1, 1),
- row(1, 1, 2),
- row(1, 2, 10),
- row(1, 2, 11),
- row(1, 1, 13));
-
- assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3 desc;", 1, 1, 2),
- row(1, 1, 13),
- row(1, 2, 11),
- row(1, 2, 10),
- row(1, 1, 2),
- row(1, 1, 1));
-
- assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3;", 1, 2, 1),
- row(1, 1, 1),
- row(1, 1, 2),
- row(1, 2, 10),
- row(1, 2, 11),
- row(1, 1, 13));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
--
- assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3 desc;", 1, 2, 1),
- row(1, 1, 13),
- row(1, 2, 11),
- row(1, 2, 10),
- row(1, 1, 2),
- row(1, 1, 1));
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3;", 1, 1, 2),
+ row(1, 1, 1),
+ row(1, 1, 2),
+ row(1, 2, 10),
+ row(1, 2, 11),
+ row(1, 1, 13));
+
+ assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3 desc;", 1, 1, 2),
+ row(1, 1, 13),
+ row(1, 2, 11),
+ row(1, 2, 10),
+ row(1, 1, 2),
+ row(1, 1, 1));
+
+ assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3;", 1, 2, 1),
+ row(1, 1, 1),
+ row(1, 1, 2),
+ row(1, 2, 10),
+ row(1, 2, 11),
+ row(1, 1, 13));
+
+ assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3 desc;", 1, 2, 1),
+ row(1, 1, 13),
+ row(1, 2, 11),
+ row(1, 2, 10),
+ row(1, 1, 2),
+ row(1, 1, 1));
- }
++ });
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
index 1b6fe9b,ac1ba4c..a5f153e
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
@@@ -22,16 -22,19 +22,18 @@@ import java.util.UUID
import org.junit.Test;
--import junit.framework.Assert;
++import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.cql3.UntypedResultSet;
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
import org.apache.cassandra.exceptions.InvalidRequestException;
- import org.apache.cassandra.utils.ByteBufferUtil;
- import org.apache.cassandra.cql3.CQLTester;
-import org.apache.cassandra.cql3.CQLTester;
-
++import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
++import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
-import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
++import junit.framework.Assert;
+
/**
* Test column ranges and ordering with static column in table
*/
@@@ -2576,4 -2324,385 +2578,349 @@@ public class SelectTest extends CQLTest
assertInvalidMessage("PRIMARY KEY column \"c2\" cannot be restricted as preceding column \"c1\" is not restricted",
"SELECT * FROM %s WHERE pk = 1 AND c2 = 1 ALLOW FILTERING;");
}
+
+ @Test
- public void testIndexQueryWithCompositePartitionKey() throws Throwable
- {
- createTable("CREATE TABLE %s (p1 int, p2 int, v int, PRIMARY KEY ((p1, p2)))");
- assertInvalidMessage("Partition key parts: p2 must be restricted as other parts are",
- "SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW FILTERING");
-
- createIndex("CREATE INDEX ON %s(v)");
-
- execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 1, 3);
- execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 2, 3);
- execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 2, 1, 3);
-
- assertRows(execute("SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW FILTERING"),
- row(1, 2, 3),
- row(1, 1, 3));
- }
-
- @Test
+ public void testEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("2"), bytes("2"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk = textAsBlob('');");
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk IN (textAsBlob(''), textAsBlob('1'));");
+
+ assertInvalidMessage("Key may not be empty",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ EMPTY_BYTE_BUFFER, bytes("2"), bytes("2"));
+
+ // Test clustering columns restrictions
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND c < textAsBlob('');"));
- }
++ });
+
+ if (options.contains("COMPACT"))
+ {
- assertInvalidMessage("Missing PRIMARY KEY part c",
++ assertInvalidMessage("Invalid empty or null value for column c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND c < textAsBlob('');"));
- }
++ });
+ }
+
+ // Test restrictions on non-primary key value
- assertInvalidMessage("Predicates on non-primary-key columns (v) are not yet supported for non secondary index queries",
- "SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;");
++ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;"));
++
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
++ bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER);
++
++ beforeAndAfterFlush(() -> {
++ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;"),
++ row(bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER));
++ });
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumns() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob('1'), textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob('1'), textAsBlob(''));"));
- }
++ });
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) < (textAsBlob(''), textAsBlob('1'));"));
- }
++ });
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("1"),
+ bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("2"),
+ bytes("2"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause));
- }
++
++ });
+
+ if (options.contains("COMPACT"))
+ {
- assertInvalidMessage("Missing PRIMARY KEY part c",
++ assertInvalidMessage("Invalid empty or null value for column c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ EMPTY_BYTE_BUFFER,
+ bytes("4"));
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
- }
++ });
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumnsAndOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c1 DESC, c2 DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c1 DESC, c2 DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c1 DESC, c2 DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
- }
++ });
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
- }
++ });
+ }
+ }
}
[37/50] [abbrv] cassandra git commit: Add schema to snapshot manifest,
add WITH TIMESTAMP to DROP statement
Posted by ms...@apache.org.
Add schema to snapshot manifest, add WITH TIMESTAMP to DROP statement
Patch by Alex Petrov; reviewed by Aleksey Yeschenko for CASSANDRA-7190
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/6528fbf2
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/6528fbf2
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/6528fbf2
Branch: refs/heads/cassandra-3.8
Commit: 6528fbf250fdd2c75c8f9464e279628234a5fef4
Parents: e6dd152
Author: Alex Petrov <ol...@gmail.com>
Authored: Wed Apr 20 14:57:52 2016 +0200
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Mon Aug 15 18:31:45 2016 +0100
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../org/apache/cassandra/config/CFMetaData.java | 7 +-
.../apache/cassandra/cql3/ColumnIdentifier.java | 2 +-
src/java/org/apache/cassandra/cql3/Cql.g | 11 +-
.../cql3/statements/AlterTableStatement.java | 8 +-
.../apache/cassandra/db/ColumnFamilyStore.java | 23 +
.../db/ColumnFamilyStoreCQLHelper.java | 442 ++++++++++++
.../org/apache/cassandra/db/Directories.java | 6 +
.../unit/org/apache/cassandra/SchemaLoader.java | 5 +
.../cql3/validation/operations/AlterTest.java | 71 ++
.../db/ColumnFamilyStoreCQLHelperTest.java | 679 +++++++++++++++++++
.../schema/LegacySchemaMigratorTest.java | 3 +-
12 files changed, 1248 insertions(+), 10 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 7ba8370..a202755 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
3.0.9
+ * Add schema to snapshot manifest, add USING TIMESTAMP clause to ALTER TABLE statements (CASSANDRA-7190)
* Fix clean interval not sent to commit log for empty memtable flush (CASSANDRA-12436)
* Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
* Backport CASSANDRA-12002 (CASSANDRA-12177)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/src/java/org/apache/cassandra/config/CFMetaData.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/CFMetaData.java b/src/java/org/apache/cassandra/config/CFMetaData.java
index 5678ada..c0f8d2d 100644
--- a/src/java/org/apache/cassandra/config/CFMetaData.java
+++ b/src/java/org/apache/cassandra/config/CFMetaData.java
@@ -954,9 +954,12 @@ public final class CFMetaData
return removed;
}
- public void recordColumnDrop(ColumnDefinition def)
+ /**
+ * Adds the column definition as a dropped column, recording the drop with the provided timestamp.
+ */
+ public void recordColumnDrop(ColumnDefinition def, long deleteTimestamp)
{
- droppedColumns.put(def.name.bytes, new DroppedColumn(def.name.toString(), def.type, FBUtilities.timestampMicros()));
+ droppedColumns.put(def.name.bytes, new DroppedColumn(def.name.toString(), def.type, deleteTimestamp));
}
public void renameColumn(ColumnIdentifier from, ColumnIdentifier to) throws InvalidRequestException
http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java b/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
index 93734e9..afb65e1 100644
--- a/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
+++ b/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
@@ -328,7 +328,7 @@ public class ColumnIdentifier extends Selectable implements IMeasurableMemory, C
}
}
- static String maybeQuote(String text)
+ public static String maybeQuote(String text)
{
if (UNQUOTED_IDENTIFIER.matcher(text).matches())
return text;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/src/java/org/apache/cassandra/cql3/Cql.g
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g
index 453a03d..d50b979 100644
--- a/src/java/org/apache/cassandra/cql3/Cql.g
+++ b/src/java/org/apache/cassandra/cql3/Cql.g
@@ -818,18 +818,21 @@ alterTableStatement returns [AlterTableStatement expr]
TableAttributes attrs = new TableAttributes();
Map<ColumnIdentifier.Raw, ColumnIdentifier.Raw> renames = new HashMap<ColumnIdentifier.Raw, ColumnIdentifier.Raw>();
boolean isStatic = false;
+ Long dropTimestamp = null;
}
: K_ALTER K_COLUMNFAMILY cf=columnFamilyName
( K_ALTER id=cident K_TYPE v=comparatorType { type = AlterTableStatement.Type.ALTER; }
| K_ADD id=cident v=comparatorType ({ isStatic=true; } K_STATIC)? { type = AlterTableStatement.Type.ADD; }
- | K_DROP id=cident { type = AlterTableStatement.Type.DROP; }
- | K_WITH properties[attrs] { type = AlterTableStatement.Type.OPTS; }
- | K_RENAME { type = AlterTableStatement.Type.RENAME; }
+ | K_DROP id=cident { type = AlterTableStatement.Type.DROP; }
+ | K_DROP id=cident K_USING K_TIMESTAMP t=INTEGER { type = AlterTableStatement.Type.DROP;
+ dropTimestamp = Long.parseLong(Constants.Literal.integer($t.text).getText()); }
+ | K_WITH properties[attrs] { type = AlterTableStatement.Type.OPTS; }
+ | K_RENAME { type = AlterTableStatement.Type.RENAME; }
id1=cident K_TO toId1=cident { renames.put(id1, toId1); }
( K_AND idn=cident K_TO toIdn=cident { renames.put(idn, toIdn); } )*
)
{
- $expr = new AlterTableStatement(cf, type, id, v, attrs, renames, isStatic);
+ $expr = new AlterTableStatement(cf, type, id, v, attrs, renames, isStatic, dropTimestamp);
}
;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
index 381971f..d64e541 100644
--- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
@@ -41,6 +41,7 @@ import org.apache.cassandra.schema.TableParams;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.MigrationManager;
import org.apache.cassandra.transport.Event;
+import org.apache.cassandra.utils.FBUtilities;
import static org.apache.cassandra.thrift.ThriftValidation.validateColumnFamily;
@@ -57,6 +58,7 @@ public class AlterTableStatement extends SchemaAlteringStatement
private final TableAttributes attrs;
private final Map<ColumnIdentifier.Raw, ColumnIdentifier.Raw> renames;
private final boolean isStatic; // Only for ALTER ADD
+ private final long deleteTimestamp;
public AlterTableStatement(CFName name,
Type type,
@@ -64,7 +66,8 @@ public class AlterTableStatement extends SchemaAlteringStatement
CQL3Type.Raw validator,
TableAttributes attrs,
Map<ColumnIdentifier.Raw, ColumnIdentifier.Raw> renames,
- boolean isStatic)
+ boolean isStatic,
+ Long deleteTimestamp)
{
super(name);
this.oType = type;
@@ -73,6 +76,7 @@ public class AlterTableStatement extends SchemaAlteringStatement
this.attrs = attrs;
this.renames = renames;
this.isStatic = isStatic;
+ this.deleteTimestamp = deleteTimestamp == null ? FBUtilities.timestampMicros() : deleteTimestamp;
}
public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException
@@ -238,7 +242,7 @@ public class AlterTableStatement extends SchemaAlteringStatement
}
assert toDelete != null;
cfm.removeColumnDefinition(toDelete);
- cfm.recordColumnDrop(toDelete);
+ cfm.recordColumnDrop(toDelete, deleteTimestamp);
break;
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
index 6c02909..fb3665b 100644
--- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
@@ -1627,6 +1627,8 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
}
writeSnapshotManifest(filesJSONArr, snapshotName);
+ if (!Schema.SYSTEM_KEYSPACE_NAMES.contains(metadata.ksName) && !Schema.REPLICATED_SYSTEM_KEYSPACE_NAMES.contains(metadata.ksName))
+ writeSnapshotSchema(snapshotName);
}
}
if (ephemeral)
@@ -1656,6 +1658,27 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
}
}
+ private void writeSnapshotSchema(final String snapshotName)
+ {
+ final File schemaFile = getDirectories().getSnapshotSchemaFile(snapshotName);
+
+ try
+ {
+ if (!schemaFile.getParentFile().exists())
+ schemaFile.getParentFile().mkdirs();
+
+ try (PrintStream out = new PrintStream(schemaFile))
+ {
+ for (String s: ColumnFamilyStoreCQLHelper.dumpReCreateStatements(metadata))
+ out.println(s);
+ }
+ }
+ catch (IOException e)
+ {
+ throw new FSWriteError(e, schemaFile);
+ }
+ }
+
private void createEphemeralSnapshotMarkerFile(final String snapshot)
{
final File ephemeralSnapshotMarker = getDirectories().getNewEphemeralSnapshotMarkerFile(snapshot);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/src/java/org/apache/cassandra/db/ColumnFamilyStoreCQLHelper.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStoreCQLHelper.java b/src/java/org/apache/cassandra/db/ColumnFamilyStoreCQLHelper.java
new file mode 100644
index 0000000..f9d6897
--- /dev/null
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyStoreCQLHelper.java
@@ -0,0 +1,442 @@
+/*
+ * Licensed to the Apache Softw\u2264are 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.db;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+import java.util.concurrent.atomic.*;
+import java.util.function.*;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Iterables;
+
+import org.apache.cassandra.config.*;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.statements.*;
+import org.apache.cassandra.db.marshal.*;
+import org.apache.cassandra.schema.*;
+import org.apache.cassandra.utils.*;
+
+/**
+ * Helper methods to represent CFMetadata and related objects in CQL format
+ */
+public class ColumnFamilyStoreCQLHelper
+{
+ public static List<String> dumpReCreateStatements(CFMetaData metadata)
+ {
+ List<String> l = new ArrayList<>();
+ // Types come first, as table can't be created without them
+ l.addAll(ColumnFamilyStoreCQLHelper.getUserTypesAsCQL(metadata));
+ // Record re-create schema statements
+ l.add(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(metadata, true));
+ // Dropped columns (and re-additions)
+ l.addAll(ColumnFamilyStoreCQLHelper.getDroppedColumnsAsCQL(metadata));
+ // Indexes applied as last, since otherwise they may interfere with column drops / re-additions
+ l.addAll(ColumnFamilyStoreCQLHelper.getIndexesAsCQL(metadata));
+ return l;
+ }
+
+ private static List<ColumnDefinition> getClusteringColumns(CFMetaData metadata)
+ {
+ List<ColumnDefinition> cds = new ArrayList<>(metadata.clusteringColumns().size());
+
+ if (!metadata.isStaticCompactTable())
+ for (ColumnDefinition cd : metadata.clusteringColumns())
+ cds.add(cd);
+
+ return cds;
+ }
+
+ private static List<ColumnDefinition> getPartitionColumns(CFMetaData metadata)
+ {
+ List<ColumnDefinition> cds = new ArrayList<>(metadata.partitionColumns().size());
+
+ for (ColumnDefinition cd : metadata.partitionColumns().statics)
+ cds.add(cd);
+
+ if (metadata.isDense())
+ {
+ // remove an empty type
+ for (ColumnDefinition cd : metadata.partitionColumns().withoutStatics())
+ if (!cd.type.equals(EmptyType.instance))
+ cds.add(cd);
+ }
+ // "regular" columns are not exposed for static compact tables
+ else if (!metadata.isStaticCompactTable())
+ {
+ for (ColumnDefinition cd : metadata.partitionColumns().withoutStatics())
+ cds.add(cd);
+ }
+
+ return cds;
+ }
+
+ /**
+ * Build a CQL String representation of Column Family Metadata
+ */
+ @VisibleForTesting
+ public static String getCFMetadataAsCQL(CFMetaData metadata, boolean includeDroppedColumns)
+ {
+ StringBuilder sb = new StringBuilder();
+ if (!isCqlCompatible(metadata))
+ {
+ sb.append(String.format("/*\nWarning: Table %s.%s omitted because it has constructs not compatible with CQL (was created via legacy API).\n",
+ metadata.ksName,
+ metadata.cfName));
+ sb.append("\nApproximate structure, for reference:");
+ sb.append("\n(this should not be used to reproduce this schema)\n\n");
+ }
+
+ sb.append("CREATE TABLE IF NOT EXISTS ");
+ sb.append(quoteIdentifier(metadata.ksName)).append('.').append(quoteIdentifier(metadata.cfName)).append(" (");
+
+ List<ColumnDefinition> partitionKeyColumns = metadata.partitionKeyColumns();
+ List<ColumnDefinition> clusteringColumns = getClusteringColumns(metadata);
+ List<ColumnDefinition> partitionColumns = getPartitionColumns(metadata);
+
+ Consumer<StringBuilder> cdCommaAppender = commaAppender("\n\t");
+ sb.append("\n\t");
+ for (ColumnDefinition cfd: partitionKeyColumns)
+ {
+ cdCommaAppender.accept(sb);
+ sb.append(toCQL(cfd));
+ if (partitionKeyColumns.size() == 1 && clusteringColumns.size() == 0)
+ sb.append(" PRIMARY KEY");
+ }
+
+ for (ColumnDefinition cfd: clusteringColumns)
+ {
+ cdCommaAppender.accept(sb);
+ sb.append(toCQL(cfd));
+ }
+
+ for (ColumnDefinition cfd: partitionColumns)
+ {
+ cdCommaAppender.accept(sb);
+ sb.append(toCQL(cfd, metadata.isStaticCompactTable()));
+ }
+
+ if (includeDroppedColumns)
+ {
+ for (Map.Entry<ByteBuffer, CFMetaData.DroppedColumn> entry: metadata.getDroppedColumns().entrySet())
+ {
+ if (metadata.getColumnDefinition(entry.getKey()) != null)
+ continue;
+
+ CFMetaData.DroppedColumn droppedColumn = entry.getValue();
+ cdCommaAppender.accept(sb);
+ sb.append(quoteIdentifier(droppedColumn.name));
+ sb.append(' ');
+ sb.append(droppedColumn.type.asCQL3Type().toString());
+ }
+ }
+
+ if (clusteringColumns.size() > 0 || partitionKeyColumns.size() > 1)
+ {
+ sb.append(",\n\tPRIMARY KEY (");
+ if (partitionKeyColumns.size() > 1)
+ {
+ sb.append("(");
+ Consumer<StringBuilder> pkCommaAppender = commaAppender(" ");
+ for (ColumnDefinition cfd : partitionKeyColumns)
+ {
+ pkCommaAppender.accept(sb);
+ sb.append(quoteIdentifier(cfd.name.toString()));
+ }
+ sb.append(")");
+ }
+ else
+ {
+ sb.append(quoteIdentifier(partitionKeyColumns.get(0).name.toString()));
+ }
+
+ for (ColumnDefinition cfd : metadata.clusteringColumns())
+ sb.append(", ").append(quoteIdentifier(cfd.name.toString()));
+
+ sb.append(')');
+ }
+ sb.append(")\n\t");
+ sb.append("WITH ");
+
+ sb.append("ID = ").append(metadata.cfId).append("\n\tAND ");
+
+ if (metadata.isCompactTable())
+ sb.append("COMPACT STORAGE\n\tAND ");
+
+ if (clusteringColumns.size() > 0)
+ {
+ sb.append("CLUSTERING ORDER BY (");
+
+ Consumer<StringBuilder> cOrderCommaAppender = commaAppender(" ");
+ for (ColumnDefinition cd : clusteringColumns)
+ {
+ cOrderCommaAppender.accept(sb);
+ sb.append(quoteIdentifier(cd.name.toString())).append(' ').append(cd.clusteringOrder().toString());
+ }
+ sb.append(")\n\tAND ");
+ }
+
+ sb.append(toCQL(metadata.params));
+ sb.append(";");
+
+ if (!isCqlCompatible(metadata))
+ {
+ sb.append("\n*/");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Build a CQL String representation of User Types used in the given Column Family.
+ *
+ * Type order is ensured as types are built incrementally: from the innermost (most nested)
+ * to the outermost.
+ */
+ @VisibleForTesting
+ public static List<String> getUserTypesAsCQL(CFMetaData metadata)
+ {
+ List<UserType> types = new ArrayList<>();
+ Set<UserType> typeSet = new HashSet<>();
+ for (ColumnDefinition cd: Iterables.concat(metadata.partitionKeyColumns(), metadata.clusteringColumns(), metadata.partitionColumns()))
+ {
+ AbstractType type = cd.type;
+ if (type instanceof UserType)
+ resolveUserType((UserType) type, typeSet, types);
+ }
+
+ List<String> typeStrings = new ArrayList<>();
+ for (UserType type: types)
+ typeStrings.add(toCQL(type));
+ return typeStrings;
+ }
+
+ /**
+ * Build a CQL String representation of Dropped Columns in the given Column Family.
+ *
+ * If the column was dropped once, but is now re-created `ADD` will be appended accordingly.
+ */
+ @VisibleForTesting
+ public static List<String> getDroppedColumnsAsCQL(CFMetaData metadata)
+ {
+ List<String> droppedColumns = new ArrayList<>();
+
+ for (Map.Entry<ByteBuffer, CFMetaData.DroppedColumn> entry: metadata.getDroppedColumns().entrySet())
+ {
+ CFMetaData.DroppedColumn column = entry.getValue();
+ droppedColumns.add(toCQLDrop(metadata.ksName, metadata.cfName, column));
+ if (metadata.getColumnDefinition(entry.getKey()) != null)
+ droppedColumns.add(toCQLAdd(metadata.ksName, metadata.cfName, metadata.getColumnDefinition(entry.getKey())));
+ }
+
+ return droppedColumns;
+ }
+
+ /**
+ * Build a CQL String representation of Indexes on columns in the given Column Family
+ */
+ @VisibleForTesting
+ public static List<String> getIndexesAsCQL(CFMetaData metadata)
+ {
+ List<String> indexes = new ArrayList<>();
+ for (IndexMetadata indexMetadata: metadata.getIndexes())
+ indexes.add(toCQL(metadata.ksName, metadata.cfName, indexMetadata));
+ return indexes;
+ }
+
+ private static String toCQL(String keyspace, String cf, IndexMetadata indexMetadata)
+ {
+ if (indexMetadata.isCustom())
+ {
+ Map<String, String> options = new HashMap<>();
+ indexMetadata.options.forEach((k, v) -> {
+ if (!k.equals(IndexTarget.TARGET_OPTION_NAME) && !k.equals(IndexTarget.CUSTOM_INDEX_OPTION_NAME))
+ options.put(k, v);
+ });
+
+ return String.format("CREATE CUSTOM INDEX %s ON %s.%s (%s) USING '%s'%s;",
+ quoteIdentifier(indexMetadata.name),
+ quoteIdentifier(keyspace),
+ quoteIdentifier(cf),
+ indexMetadata.options.get(IndexTarget.TARGET_OPTION_NAME),
+ indexMetadata.options.get(IndexTarget.CUSTOM_INDEX_OPTION_NAME),
+ options.isEmpty() ? "" : " WITH OPTIONS " + toCQL(options));
+ }
+ else
+ {
+ return String.format("CREATE INDEX %s ON %s.%s (%s);",
+ quoteIdentifier(indexMetadata.name),
+ quoteIdentifier(keyspace),
+ quoteIdentifier(cf),
+ indexMetadata.options.get(IndexTarget.TARGET_OPTION_NAME));
+ }
+ }
+ private static String toCQL(UserType userType)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(String.format("CREATE TYPE %s.%s(",
+ quoteIdentifier(userType.keyspace),
+ quoteIdentifier(userType.getNameAsString())));
+
+ Consumer<StringBuilder> commaAppender = commaAppender(" ");
+ for (int i = 0; i < userType.size(); i++)
+ {
+ commaAppender.accept(sb);
+ sb.append(String.format("%s %s",
+ userType.fieldNameAsString(i),
+ userType.fieldType(i).asCQL3Type()));
+ }
+ sb.append(");");
+ return sb.toString();
+ }
+
+ private static String toCQL(TableParams tableParams)
+ {
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("bloom_filter_fp_chance = ").append(tableParams.bloomFilterFpChance);
+ builder.append("\n\tAND dclocal_read_repair_chance = ").append(tableParams.dcLocalReadRepairChance);
+ builder.append("\n\tAND crc_check_chance = ").append(tableParams.crcCheckChance);
+ builder.append("\n\tAND default_time_to_live = ").append(tableParams.defaultTimeToLive);
+ builder.append("\n\tAND gc_grace_seconds = ").append(tableParams.gcGraceSeconds);
+ builder.append("\n\tAND min_index_interval = ").append(tableParams.minIndexInterval);
+ builder.append("\n\tAND max_index_interval = ").append(tableParams.maxIndexInterval);
+ builder.append("\n\tAND memtable_flush_period_in_ms = ").append(tableParams.memtableFlushPeriodInMs);
+ builder.append("\n\tAND read_repair_chance = ").append(tableParams.readRepairChance);
+ builder.append("\n\tAND speculative_retry = '").append(tableParams.speculativeRetry).append("'");
+ builder.append("\n\tAND comment = ").append(singleQuote(tableParams.comment));
+ builder.append("\n\tAND caching = ").append(toCQL(tableParams.caching.asMap()));
+ builder.append("\n\tAND compaction = ").append(toCQL(tableParams.compaction.asMap()));
+ builder.append("\n\tAND compression = ").append(toCQL(tableParams.compression.asMap()));
+
+ builder.append("\n\tAND extensions = { ");
+ for (Map.Entry<String, ByteBuffer> entry : tableParams.extensions.entrySet())
+ {
+ builder.append(singleQuote(entry.getKey()));
+ builder.append(": ");
+ builder.append("0x" + ByteBufferUtil.bytesToHex(entry.getValue()));
+ }
+ builder.append(" }");
+ return builder.toString();
+ }
+
+ private static String toCQL(Map<?, ?> map)
+ {
+ StringBuilder builder = new StringBuilder("{ ");
+
+ boolean isFirst = true;
+ for (Map.Entry entry: map.entrySet())
+ {
+ if (isFirst)
+ isFirst = false;
+ else
+ builder.append(", ");
+ builder.append(singleQuote(entry.getKey().toString()));
+ builder.append(": ");
+ builder.append(singleQuote(entry.getValue().toString()));
+ }
+
+ builder.append(" }");
+ return builder.toString();
+ }
+
+ private static String toCQL(ColumnDefinition cd)
+ {
+ return toCQL(cd, false);
+ }
+
+ private static String toCQL(ColumnDefinition cd, boolean isStaticCompactTable)
+ {
+ return String.format("%s %s%s",
+ quoteIdentifier(cd.name.toString()),
+ cd.type.asCQL3Type().toString(),
+ cd.isStatic() && !isStaticCompactTable ? " static" : "");
+ }
+
+ private static String toCQLAdd(String keyspace, String cf, ColumnDefinition cd)
+ {
+ return String.format("ALTER TABLE %s.%s ADD %s %s%s;",
+ quoteIdentifier(keyspace),
+ quoteIdentifier(cf),
+ quoteIdentifier(cd.name.toString()),
+ cd.type.asCQL3Type().toString(),
+ cd.isStatic() ? " static" : "");
+ }
+
+ private static String toCQLDrop(String keyspace, String cf, CFMetaData.DroppedColumn droppedColumn)
+ {
+ return String.format("ALTER TABLE %s.%s DROP %s USING TIMESTAMP %s;",
+ quoteIdentifier(keyspace),
+ quoteIdentifier(cf),
+ quoteIdentifier(droppedColumn.name),
+ droppedColumn.droppedTime);
+ }
+
+ private static void resolveUserType(UserType type, Set<UserType> typeSet, List<UserType> types)
+ {
+ for (AbstractType subType: type.fieldTypes())
+ if (!typeSet.contains(subType) && subType instanceof UserType)
+ resolveUserType((UserType) subType, typeSet, types);
+
+ if (!typeSet.contains(type))
+ {
+ UserType t = type;
+ typeSet.add(t);
+ types.add(t);
+ }
+ }
+
+ private static String singleQuote(String s)
+ {
+ return String.format("'%s'", s.replaceAll("'", "''"));
+ }
+
+ private static Consumer<StringBuilder> commaAppender(String afterComma)
+ {
+ AtomicBoolean isFirst = new AtomicBoolean(true);
+ return new Consumer<StringBuilder>()
+ {
+ public void accept(StringBuilder stringBuilder)
+ {
+ if (!isFirst.getAndSet(false))
+ stringBuilder.append(',').append(afterComma);
+ }
+ };
+ }
+
+ private static String quoteIdentifier(String id)
+ {
+ return ColumnIdentifier.maybeQuote(id);
+ }
+
+ /**
+ * Whether or not the given metadata is compatible / representable with CQL Language
+ */
+ public static boolean isCqlCompatible(CFMetaData metaData)
+ {
+ if (metaData.isSuper())
+ return false;
+
+ if (metaData.isCompactTable()
+ && metaData.partitionColumns().withoutStatics().size() > 1
+ && metaData.clusteringColumns().size() >= 1)
+ return false;
+
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/src/java/org/apache/cassandra/db/Directories.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/Directories.java b/src/java/org/apache/cassandra/db/Directories.java
index 877f984..30fe56d 100644
--- a/src/java/org/apache/cassandra/db/Directories.java
+++ b/src/java/org/apache/cassandra/db/Directories.java
@@ -477,6 +477,12 @@ public class Directories
return new File(snapshotDir, "manifest.json");
}
+ public File getSnapshotSchemaFile(String snapshotName)
+ {
+ File snapshotDir = getSnapshotDirectory(getDirectoryForNewSSTables(), snapshotName);
+ return new File(snapshotDir, "schema.cql");
+ }
+
public File getNewEphemeralSnapshotMarkerFile(String snapshotName)
{
File snapshotDir = new File(getWriteableLocationAsFile(1L), join(SNAPSHOT_SUBDIR, snapshotName));
http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/test/unit/org/apache/cassandra/SchemaLoader.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/SchemaLoader.java b/test/unit/org/apache/cassandra/SchemaLoader.java
index 5d720c4..dd4884c 100644
--- a/test/unit/org/apache/cassandra/SchemaLoader.java
+++ b/test/unit/org/apache/cassandra/SchemaLoader.java
@@ -264,6 +264,11 @@ public class SchemaLoader
MigrationManager.announceNewKeyspace(KeyspaceMetadata.create(name, params, Tables.of(tables)), true);
}
+ public static void createKeyspace(String name, KeyspaceParams params, Tables tables, Types types)
+ {
+ MigrationManager.announceNewKeyspace(KeyspaceMetadata.create(name, params, tables, Views.none(), types, Functions.none()), true);
+ }
+
public static ColumnDefinition integerColumn(String ksName, String cfName)
{
return new ColumnDefinition(ksName,
http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
index 509aeac..bcd6587 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
@@ -99,6 +99,77 @@ public class AlterTest extends CQLTester
}
@Test
+ public void testDropWithTimestamp() throws Throwable
+ {
+ createTable("CREATE TABLE %s (id int, c1 int, v1 int, todrop int, PRIMARY KEY (id, c1));");
+ for (int i = 0; i < 5; i++)
+ execute("INSERT INTO %s (id, c1, v1, todrop) VALUES (?, ?, ?, ?) USING TIMESTAMP ?", 1, i, i, i, 10000L * i);
+
+ // flush is necessary since otherwise the values of `todrop` will get discarded during
+ // alter statement
+ flush();
+ execute("ALTER TABLE %s DROP todrop USING TIMESTAMP 20000;");
+ execute("ALTER TABLE %s ADD todrop int;");
+ execute("INSERT INTO %s (id, c1, v1, todrop) VALUES (?, ?, ?, ?) USING TIMESTAMP ?", 1, 100, 100, 100, 30000L);
+ assertRows(execute("SELECT id, c1, v1, todrop FROM %s"),
+ row(1, 0, 0, null),
+ row(1, 1, 1, null),
+ row(1, 2, 2, null),
+ row(1, 3, 3, 3),
+ row(1, 4, 4, 4),
+ row(1, 100, 100, 100));
+ }
+
+ @Test
+ public void testDropStaticWithTimestamp() throws Throwable
+ {
+ createTable("CREATE TABLE %s (id int, c1 int, v1 int, todrop int static, PRIMARY KEY (id, c1));");
+ for (int i = 0; i < 5; i++)
+ execute("INSERT INTO %s (id, c1, v1, todrop) VALUES (?, ?, ?, ?) USING TIMESTAMP ?", 1, i, i, i, 10000L * i);
+
+ // flush is necessary since otherwise the values of `todrop` will get discarded during
+ // alter statement
+ flush();
+ execute("ALTER TABLE %s DROP todrop USING TIMESTAMP 20000;");
+ execute("ALTER TABLE %s ADD todrop int static;");
+ execute("INSERT INTO %s (id, c1, v1, todrop) VALUES (?, ?, ?, ?) USING TIMESTAMP ?", 1, 100, 100, 100, 30000L);
+ // static column value with largest timestmap will be available again
+ assertRows(execute("SELECT id, c1, v1, todrop FROM %s"),
+ row(1, 0, 0, 4),
+ row(1, 1, 1, 4),
+ row(1, 2, 2, 4),
+ row(1, 3, 3, 4),
+ row(1, 4, 4, 4),
+ row(1, 100, 100, 4));
+ }
+
+ @Test
+ public void testDropMultipleWithTimestamp() throws Throwable
+ {
+ createTable("CREATE TABLE %s (id int, c1 int, v1 int, todrop1 int, todrop2 int, PRIMARY KEY (id, c1));");
+ for (int i = 0; i < 5; i++)
+ execute("INSERT INTO %s (id, c1, v1, todrop1, todrop2) VALUES (?, ?, ?, ?, ?) USING TIMESTAMP ?", 1, i, i, i, i, 10000L * i);
+
+ // flush is necessary since otherwise the values of `todrop1` and `todrop2` will get discarded during
+ // alter statement
+ flush();
+ execute("ALTER TABLE %s DROP todrop1 USING TIMESTAMP 20000;");
+ execute("ALTER TABLE %s DROP todrop2 USING TIMESTAMP 20000;");
+ execute("ALTER TABLE %s ADD todrop1 int;");
+ execute("ALTER TABLE %s ADD todrop2 int;");
+
+ execute("INSERT INTO %s (id, c1, v1, todrop1, todrop2) VALUES (?, ?, ?, ?, ?) USING TIMESTAMP ?", 1, 100, 100, 100, 100, 40000L);
+ assertRows(execute("SELECT id, c1, v1, todrop1, todrop2 FROM %s"),
+ row(1, 0, 0, null, null),
+ row(1, 1, 1, null, null),
+ row(1, 2, 2, null, null),
+ row(1, 3, 3, 3, 3),
+ row(1, 4, 4, 4, 4),
+ row(1, 100, 100, 100, 100));
+ }
+
+
+ @Test
public void testChangeStrategyWithUnquotedAgrument() throws Throwable
{
createTable("CREATE TABLE %s (id text PRIMARY KEY);");
http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/test/unit/org/apache/cassandra/db/ColumnFamilyStoreCQLHelperTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/ColumnFamilyStoreCQLHelperTest.java b/test/unit/org/apache/cassandra/db/ColumnFamilyStoreCQLHelperTest.java
new file mode 100644
index 0000000..c2e5cb7
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/ColumnFamilyStoreCQLHelperTest.java
@@ -0,0 +1,679 @@
+/*
+ * 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.db;
+
+import java.io.FileReader;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.*;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Files;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.cassandra.*;
+import org.apache.cassandra.config.*;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.statements.*;
+import org.apache.cassandra.db.marshal.*;
+import org.apache.cassandra.exceptions.*;
+import org.apache.cassandra.schema.*;
+import org.apache.cassandra.utils.*;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ColumnFamilyStoreCQLHelperTest extends CQLTester
+{
+ @Before
+ public void defineSchema() throws ConfigurationException
+ {
+ SchemaLoader.prepareServer();
+ }
+
+ @Test
+ public void testUserTypesCQL()
+ {
+ String keyspace = "cql_test_keyspace_user_types";
+ String table = "test_table_user_types";
+
+ UserType typeA = new UserType(keyspace, ByteBufferUtil.bytes("a"),
+ Arrays.asList(ByteBufferUtil.bytes("a1"),
+ ByteBufferUtil.bytes("a2"),
+ ByteBufferUtil.bytes("a3")),
+ Arrays.asList(IntegerType.instance,
+ IntegerType.instance,
+ IntegerType.instance));
+
+ UserType typeB = new UserType(keyspace, ByteBufferUtil.bytes("b"),
+ Arrays.asList(ByteBufferUtil.bytes("b1"),
+ ByteBufferUtil.bytes("b2"),
+ ByteBufferUtil.bytes("b3")),
+ Arrays.asList(typeA,
+ typeA,
+ typeA));
+
+ UserType typeC = new UserType(keyspace, ByteBufferUtil.bytes("c"),
+ Arrays.asList(ByteBufferUtil.bytes("c1"),
+ ByteBufferUtil.bytes("c2"),
+ ByteBufferUtil.bytes("c3")),
+ Arrays.asList(typeB,
+ typeB,
+ typeB));
+
+ CFMetaData cfm = CFMetaData.Builder.create(keyspace, table)
+ .addPartitionKey("pk1", IntegerType.instance)
+ .addClusteringColumn("ck1", IntegerType.instance)
+ .addRegularColumn("reg1", typeC)
+ .addRegularColumn("reg2", ListType.getInstance(IntegerType.instance, false))
+ .addRegularColumn("reg3", MapType.getInstance(AsciiType.instance, IntegerType.instance, true))
+ .build();
+
+ SchemaLoader.createKeyspace(keyspace,
+ KeyspaceParams.simple(1),
+ Tables.of(cfm),
+ Types.of(typeA, typeB, typeC));
+
+ ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+ assertEquals(ImmutableList.of("CREATE TYPE cql_test_keyspace_user_types.a(a1 varint, a2 varint, a3 varint);",
+ "CREATE TYPE cql_test_keyspace_user_types.b(b1 frozen<a>, b2 frozen<a>, b3 frozen<a>);",
+ "CREATE TYPE cql_test_keyspace_user_types.c(c1 frozen<b>, c2 frozen<b>, c3 frozen<b>);"),
+ ColumnFamilyStoreCQLHelper.getUserTypesAsCQL(cfs.metadata));
+ }
+
+ @Test
+ public void testDroppedColumnsCQL()
+ {
+ String keyspace = "cql_test_keyspace_dropped_columns";
+ String table = "test_table_dropped_columns";
+
+ CFMetaData cfm = CFMetaData.Builder.create(keyspace, table)
+ .addPartitionKey("pk1", IntegerType.instance)
+ .addClusteringColumn("ck1", IntegerType.instance)
+ .addRegularColumn("reg1", IntegerType.instance)
+ .addRegularColumn("reg2", IntegerType.instance)
+ .addRegularColumn("reg3", IntegerType.instance)
+ .build();
+
+
+ ColumnDefinition reg1 = cfm.getColumnDefinition(ByteBufferUtil.bytes("reg1"));
+ ColumnDefinition reg2 = cfm.getColumnDefinition(ByteBufferUtil.bytes("reg2"));
+ ColumnDefinition reg3 = cfm.getColumnDefinition(ByteBufferUtil.bytes("reg3"));
+
+ cfm.removeColumnDefinition(reg1);
+ cfm.removeColumnDefinition(reg2);
+ cfm.removeColumnDefinition(reg3);
+
+ cfm.recordColumnDrop(reg1, 10000);
+ cfm.recordColumnDrop(reg2, 20000);
+ cfm.recordColumnDrop(reg3, 30000);
+
+ SchemaLoader.createKeyspace(keyspace,
+ KeyspaceParams.simple(1),
+ cfm);
+
+ ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+ assertEquals(ImmutableList.of("ALTER TABLE cql_test_keyspace_dropped_columns.test_table_dropped_columns DROP reg1 USING TIMESTAMP 10000;",
+ "ALTER TABLE cql_test_keyspace_dropped_columns.test_table_dropped_columns DROP reg3 USING TIMESTAMP 30000;",
+ "ALTER TABLE cql_test_keyspace_dropped_columns.test_table_dropped_columns DROP reg2 USING TIMESTAMP 20000;"),
+ ColumnFamilyStoreCQLHelper.getDroppedColumnsAsCQL(cfs.metadata));
+
+ assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+ "CREATE TABLE IF NOT EXISTS cql_test_keyspace_dropped_columns.test_table_dropped_columns (\n" +
+ "\tpk1 varint,\n" +
+ "\tck1 varint,\n" +
+ "\treg1 varint,\n" +
+ "\treg3 varint,\n" +
+ "\treg2 varint,\n" +
+ "\tPRIMARY KEY (pk1, ck1))"));
+ }
+
+ @Test
+ public void testReaddedColumns()
+ {
+ String keyspace = "cql_test_keyspace_readded_columns";
+ String table = "test_table_readded_columns";
+
+ CFMetaData cfm = CFMetaData.Builder.create(keyspace, table)
+ .addPartitionKey("pk1", IntegerType.instance)
+ .addClusteringColumn("ck1", IntegerType.instance)
+ .addRegularColumn("reg1", IntegerType.instance)
+ .addStaticColumn("reg2", IntegerType.instance)
+ .addRegularColumn("reg3", IntegerType.instance)
+ .build();
+
+ ColumnDefinition reg1 = cfm.getColumnDefinition(ByteBufferUtil.bytes("reg1"));
+ ColumnDefinition reg2 = cfm.getColumnDefinition(ByteBufferUtil.bytes("reg2"));
+
+ cfm.removeColumnDefinition(reg1);
+ cfm.removeColumnDefinition(reg2);
+
+ cfm.recordColumnDrop(reg1, 10000);
+ cfm.recordColumnDrop(reg2, 20000);
+
+ cfm.addColumnDefinition(reg1);
+ cfm.addColumnDefinition(reg2);
+
+ SchemaLoader.createKeyspace(keyspace,
+ KeyspaceParams.simple(1),
+ cfm);
+
+ ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+ // when re-adding, column is present in CREATE, then in DROP and then in ADD again, to record DROP with a proper timestamp
+ assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+ "CREATE TABLE IF NOT EXISTS cql_test_keyspace_readded_columns.test_table_readded_columns (\n" +
+ "\tpk1 varint,\n" +
+ "\tck1 varint,\n" +
+ "\treg2 varint static,\n" +
+ "\treg1 varint,\n" +
+ "\treg3 varint,\n" +
+ "\tPRIMARY KEY (pk1, ck1))"));
+
+ assertEquals(ImmutableList.of("ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns DROP reg1 USING TIMESTAMP 10000;",
+ "ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns ADD reg1 varint;",
+ "ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns DROP reg2 USING TIMESTAMP 20000;",
+ "ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns ADD reg2 varint static;"),
+ ColumnFamilyStoreCQLHelper.getDroppedColumnsAsCQL(cfs.metadata));
+ }
+
+ @Test
+ public void testCfmColumnsCQL()
+ {
+ String keyspace = "cql_test_keyspace_create_table";
+ String table = "test_table_create_table";
+
+ CFMetaData cfm = CFMetaData.Builder.create(keyspace, table)
+ .addPartitionKey("pk1", IntegerType.instance)
+ .addPartitionKey("pk2", AsciiType.instance)
+ .addClusteringColumn("ck1", ReversedType.getInstance(IntegerType.instance))
+ .addClusteringColumn("ck2", IntegerType.instance)
+ .addStaticColumn("st1", AsciiType.instance)
+ .addRegularColumn("reg1", AsciiType.instance)
+ .addRegularColumn("reg2", ListType.getInstance(IntegerType.instance, false))
+ .addRegularColumn("reg3", MapType.getInstance(AsciiType.instance, IntegerType.instance, true))
+ .build();
+
+ SchemaLoader.createKeyspace(keyspace,
+ KeyspaceParams.simple(1),
+ cfm);
+
+ ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+ assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+ "CREATE TABLE IF NOT EXISTS cql_test_keyspace_create_table.test_table_create_table (\n" +
+ "\tpk1 varint,\n" +
+ "\tpk2 ascii,\n" +
+ "\tck1 varint,\n" +
+ "\tck2 varint,\n" +
+ "\tst1 ascii static,\n" +
+ "\treg1 ascii,\n" +
+ "\treg2 frozen<list<varint>>,\n" +
+ "\treg3 map<ascii, varint>,\n" +
+ "\tPRIMARY KEY ((pk1, pk2), ck1, ck2))\n" +
+ "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+ "\tAND CLUSTERING ORDER BY (ck1 DESC, ck2 ASC)"));
+ }
+
+ @Test
+ public void testCfmCompactStorageCQL()
+ {
+ String keyspace = "cql_test_keyspace_compact";
+ String table = "test_table_compact";
+
+ CFMetaData cfm = CFMetaData.Builder.createDense(keyspace, table, true, false)
+ .addPartitionKey("pk1", IntegerType.instance)
+ .addPartitionKey("pk2", AsciiType.instance)
+ .addClusteringColumn("ck1", ReversedType.getInstance(IntegerType.instance))
+ .addClusteringColumn("ck2", IntegerType.instance)
+ .addRegularColumn("reg", IntegerType.instance)
+ .build();
+
+ SchemaLoader.createKeyspace(keyspace,
+ KeyspaceParams.simple(1),
+ cfm);
+
+ ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+ assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+ "CREATE TABLE IF NOT EXISTS cql_test_keyspace_compact.test_table_compact (\n" +
+ "\tpk1 varint,\n" +
+ "\tpk2 ascii,\n" +
+ "\tck1 varint,\n" +
+ "\tck2 varint,\n" +
+ "\treg varint,\n" +
+ "\tPRIMARY KEY ((pk1, pk2), ck1, ck2))\n" +
+ "\tWITH ID = " + cfm.cfId + "\n" +
+ "\tAND COMPACT STORAGE\n" +
+ "\tAND CLUSTERING ORDER BY (ck1 DESC, ck2 ASC)"));
+ }
+
+ @Test
+ public void testCfmCounterCQL()
+ {
+ String keyspace = "cql_test_keyspace_counter";
+ String table = "test_table_counter";
+
+ CFMetaData cfm = CFMetaData.Builder.createDense(keyspace, table, true, true)
+ .addPartitionKey("pk1", IntegerType.instance)
+ .addPartitionKey("pk2", AsciiType.instance)
+ .addClusteringColumn("ck1", ReversedType.getInstance(IntegerType.instance))
+ .addClusteringColumn("ck2", IntegerType.instance)
+ .addRegularColumn("cnt", CounterColumnType.instance)
+ .build();
+
+ SchemaLoader.createKeyspace(keyspace,
+ KeyspaceParams.simple(1),
+ cfm);
+
+ ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+ assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+ "CREATE TABLE IF NOT EXISTS cql_test_keyspace_counter.test_table_counter (\n" +
+ "\tpk1 varint,\n" +
+ "\tpk2 ascii,\n" +
+ "\tck1 varint,\n" +
+ "\tck2 varint,\n" +
+ "\tcnt counter,\n" +
+ "\tPRIMARY KEY ((pk1, pk2), ck1, ck2))\n" +
+ "\tWITH ID = " + cfm.cfId + "\n" +
+ "\tAND COMPACT STORAGE\n" +
+ "\tAND CLUSTERING ORDER BY (ck1 DESC, ck2 ASC)"));
+ }
+
+ @Test
+ public void testCfmOptionsCQL()
+ {
+ String keyspace = "cql_test_keyspace_options";
+ String table = "test_table_options";
+
+ CFMetaData cfm = CFMetaData.Builder.create(keyspace, table)
+ .addPartitionKey("pk1", IntegerType.instance)
+ .addClusteringColumn("cl1", IntegerType.instance)
+ .addRegularColumn("reg1", AsciiType.instance)
+ .build();
+
+ cfm.recordColumnDrop(cfm.getColumnDefinition(ByteBuffer.wrap("reg1".getBytes())), FBUtilities.timestampMicros());
+ cfm.bloomFilterFpChance(1.0);
+ cfm.comment("comment");
+ cfm.compaction(CompactionParams.lcs(Collections.singletonMap("sstable_size_in_mb", "1")));
+ cfm.compression(CompressionParams.lz4(1 << 16));
+ cfm.dcLocalReadRepairChance(0.2);
+ cfm.crcCheckChance(0.3);
+ cfm.defaultTimeToLive(4);
+ cfm.gcGraceSeconds(5);
+ cfm.minIndexInterval(6);
+ cfm.maxIndexInterval(7);
+ cfm.memtableFlushPeriod(8);
+ cfm.readRepairChance(0.9);
+ cfm.speculativeRetry(SpeculativeRetryParam.always());
+ cfm.extensions(ImmutableMap.of("ext1",
+ ByteBuffer.wrap("val1".getBytes())));
+
+ SchemaLoader.createKeyspace(keyspace,
+ KeyspaceParams.simple(1),
+ cfm);
+ ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+ assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).endsWith(
+ "AND bloom_filter_fp_chance = 1.0\n" +
+ "\tAND dclocal_read_repair_chance = 0.2\n" +
+ "\tAND crc_check_chance = 0.3\n" +
+ "\tAND default_time_to_live = 4\n" +
+ "\tAND gc_grace_seconds = 5\n" +
+ "\tAND min_index_interval = 6\n" +
+ "\tAND max_index_interval = 7\n" +
+ "\tAND memtable_flush_period_in_ms = 8\n" +
+ "\tAND read_repair_chance = 0.9\n" +
+ "\tAND speculative_retry = 'ALWAYS'\n" +
+ "\tAND comment = 'comment'\n" +
+ "\tAND caching = { 'keys': 'ALL', 'rows_per_partition': 'NONE' }\n" +
+ "\tAND compaction = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy', 'sstable_size_in_mb': '1' }\n" +
+ "\tAND compression = { 'chunk_length_in_kb': '64', 'class': 'org.apache.cassandra.io.compress.LZ4Compressor' }\n" +
+ "\tAND extensions = { 'ext1': 0x76616c31 };"
+ ));
+ }
+
+ @Test
+ public void testCfmIndexJson()
+ {
+ String keyspace = "cql_test_keyspace_3";
+ String table = "test_table_3";
+
+ CFMetaData cfm = CFMetaData.Builder.create(keyspace, table)
+ .addPartitionKey("pk1", IntegerType.instance)
+ .addClusteringColumn("cl1", IntegerType.instance)
+ .addRegularColumn("reg1", AsciiType.instance)
+ .build();
+
+ cfm.indexes(cfm.getIndexes()
+ .with(IndexMetadata.fromIndexTargets(cfm,
+ Collections.singletonList(new IndexTarget(cfm.getColumnDefinition(ByteBufferUtil.bytes("reg1")).name,
+ IndexTarget.Type.VALUES)),
+ "indexName",
+ IndexMetadata.Kind.COMPOSITES,
+ Collections.emptyMap()))
+ .with(IndexMetadata.fromIndexTargets(cfm,
+ Collections.singletonList(new IndexTarget(cfm.getColumnDefinition(ByteBufferUtil.bytes("reg1")).name,
+ IndexTarget.Type.KEYS)),
+ "indexName2",
+ IndexMetadata.Kind.COMPOSITES,
+ Collections.emptyMap()))
+ .with(IndexMetadata.fromIndexTargets(cfm,
+ Collections.singletonList(new IndexTarget(cfm.getColumnDefinition(ByteBufferUtil.bytes("reg1")).name,
+ IndexTarget.Type.KEYS_AND_VALUES)),
+ "indexName3",
+ IndexMetadata.Kind.COMPOSITES,
+ Collections.emptyMap())));
+
+
+ SchemaLoader.createKeyspace(keyspace,
+ KeyspaceParams.simple(1),
+ cfm);
+
+ ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+ assertEquals(ImmutableList.of("CREATE INDEX \"indexName\" ON cql_test_keyspace_3.test_table_3 (reg1);",
+ "CREATE INDEX \"indexName2\" ON cql_test_keyspace_3.test_table_3 (reg1);",
+ "CREATE INDEX \"indexName3\" ON cql_test_keyspace_3.test_table_3 (reg1);"),
+ ColumnFamilyStoreCQLHelper.getIndexesAsCQL(cfs.metadata));
+ }
+
+ private final static String SNAPSHOT = "testsnapshot";
+
+ @Test
+ public void testSnapshot() throws Throwable
+ {
+ String typeA = createType("CREATE TYPE %s (a1 varint, a2 varint, a3 varint);");
+ String typeB = createType("CREATE TYPE %s (b1 frozen<" + typeA + ">, b2 frozen<" + typeA + ">, b3 frozen<" + typeA + ">);");
+ String typeC = createType("CREATE TYPE %s (c1 frozen<" + typeB + ">, c2 frozen<" + typeB + ">, c3 frozen<" + typeB + ">);");
+
+ String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" +
+ "pk1 varint," +
+ "pk2 ascii," +
+ "ck1 varint," +
+ "ck2 varint," +
+ "reg1 frozen<" + typeC + ">," +
+ "reg2 int," +
+ "reg3 int," +
+ "PRIMARY KEY ((pk1, pk2), ck1, ck2)) WITH " +
+ "CLUSTERING ORDER BY (ck1 ASC, ck2 DESC);");
+
+ alterTable("ALTER TABLE %s DROP reg3 USING TIMESTAMP 10000;");
+ alterTable("ALTER TABLE %s ADD reg3 int;");
+
+ for (int i = 0; i < 10; i++)
+ execute("INSERT INTO %s (pk1, pk2, ck1, ck2, reg1, reg2) VALUES (?, ?, ?, ?, ?, ?)", i, i + 1, i + 2, i + 3, null, i + 5);
+
+ ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName);
+ cfs.snapshot(SNAPSHOT);
+
+ String schema = Files.toString(cfs.getDirectories().getSnapshotSchemaFile(SNAPSHOT), Charset.defaultCharset());
+ assertTrue(schema.contains(String.format("CREATE TYPE %s.%s(a1 varint, a2 varint, a3 varint);", keyspace(), typeA)));
+ assertTrue(schema.contains(String.format("CREATE TYPE %s.%s(a1 varint, a2 varint, a3 varint);", keyspace(), typeA)));
+ assertTrue(schema.contains(String.format("CREATE TYPE %s.%s(b1 frozen<%s>, b2 frozen<%s>, b3 frozen<%s>);", keyspace(), typeB, typeA, typeA, typeA)));
+ assertTrue(schema.contains(String.format("CREATE TYPE %s.%s(c1 frozen<%s>, c2 frozen<%s>, c3 frozen<%s>);", keyspace(), typeC, typeB, typeB, typeB)));
+
+ schema = schema.substring(schema.indexOf("CREATE TABLE")); // trim to ensure order
+
+ assertTrue(schema.startsWith("CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" +
+ "\tpk1 varint,\n" +
+ "\tpk2 ascii,\n" +
+ "\tck1 varint,\n" +
+ "\tck2 varint,\n" +
+ "\treg1 frozen<" + typeC + ">,\n" +
+ "\treg2 int,\n" +
+ "\treg3 int,\n" +
+ "\tPRIMARY KEY ((pk1, pk2), ck1, ck2))\n" +
+ "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+ "\tAND CLUSTERING ORDER BY (ck1 ASC, ck2 DESC)"));
+
+ schema = schema.substring(schema.indexOf("ALTER"));
+ assertTrue(schema.startsWith(String.format("ALTER TABLE %s.%s DROP reg3 USING TIMESTAMP 10000;", keyspace(), tableName)));
+ assertTrue(schema.contains(String.format("ALTER TABLE %s.%s ADD reg3 int;", keyspace(), tableName)));
+
+ JSONObject manifest = (JSONObject) new JSONParser().parse(new FileReader(cfs.getDirectories().getSnapshotManifestFile(SNAPSHOT)));
+ JSONArray files = (JSONArray) manifest.get("files");
+ Assert.assertEquals(1, files.size());
+ }
+
+ @Test
+ public void testSystemKsSnapshot() throws Throwable
+ {
+ ColumnFamilyStore cfs = Keyspace.open("system").getColumnFamilyStore("peers");
+ cfs.snapshot(SNAPSHOT);
+
+ Assert.assertTrue(cfs.getDirectories().getSnapshotManifestFile(SNAPSHOT).exists());
+ Assert.assertFalse(cfs.getDirectories().getSnapshotSchemaFile(SNAPSHOT).exists());
+ }
+
+ @Test
+ public void testDroppedType() throws Throwable
+ {
+ String typeA = createType("CREATE TYPE %s (a1 varint, a2 varint, a3 varint);");
+ String typeB = createType("CREATE TYPE %s (b1 frozen<" + typeA + ">, b2 frozen<" + typeA + ">, b3 frozen<" + typeA + ">);");
+
+ String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" +
+ "pk1 varint," +
+ "ck1 varint," +
+ "reg1 frozen<" + typeB + ">," +
+ "reg2 varint," +
+ "PRIMARY KEY (pk1, ck1));");
+
+ alterTable("ALTER TABLE %s DROP reg1 USING TIMESTAMP 10000;");
+
+ Runnable validate = () -> {
+ try
+ {
+ ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName);
+ cfs.snapshot(SNAPSHOT);
+ String schema = Files.toString(cfs.getDirectories().getSnapshotSchemaFile(SNAPSHOT), Charset.defaultCharset());
+
+ // When both column and it's type are dropped, the type in column definition gets substituted with a tuple
+ assertTrue(schema.startsWith("CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" +
+ "\tpk1 varint,\n" +
+ "\tck1 varint,\n" +
+ "\treg2 varint,\n" +
+ "\treg1 frozen<tuple<frozen<tuple<varint, varint, varint>>, frozen<tuple<varint, varint, varint>>, frozen<tuple<varint, varint, varint>>>>,\n" +
+ "\tPRIMARY KEY (pk1, ck1))"));
+ assertTrue(schema.contains("ALTER TABLE " + keyspace() + "." + tableName + " DROP reg1 USING TIMESTAMP 10000;"));
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ };
+
+ // Validate before and after the type drop
+ validate.run();
+ schemaChange("DROP TYPE " + keyspace() + "." + typeB);
+ schemaChange("DROP TYPE " + keyspace() + "." + typeA);
+ validate.run();
+ }
+
+ @Test
+ public void testDenseTable() throws Throwable
+ {
+ String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" +
+ "pk1 varint PRIMARY KEY," +
+ "reg1 int)" +
+ " WITH COMPACT STORAGE");
+
+ ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName);
+
+ assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+ "CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" +
+ "\tpk1 varint PRIMARY KEY,\n" +
+ "\treg1 int)\n" +
+ "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+ "\tAND COMPACT STORAGE"));
+ }
+
+ @Test
+ public void testStaticCompactTable() throws Throwable
+ {
+ String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" +
+ "pk1 varint PRIMARY KEY," +
+ "reg1 int," +
+ "reg2 int)" +
+ " WITH COMPACT STORAGE");
+
+ ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName);
+
+ assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+ "CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" +
+ "\tpk1 varint PRIMARY KEY,\n" +
+ "\treg1 int,\n" +
+ "\treg2 int)\n" +
+ "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+ "\tAND COMPACT STORAGE"));
+ }
+
+ @Test
+ public void testStaticCompactWithCounters() throws Throwable
+ {
+ String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" +
+ "pk1 varint PRIMARY KEY," +
+ "reg1 counter," +
+ "reg2 counter)" +
+ " WITH COMPACT STORAGE");
+
+
+ ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName);
+
+ assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+ "CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" +
+ "\tpk1 varint PRIMARY KEY,\n" +
+ "\treg1 counter,\n" +
+ "\treg2 counter)\n" +
+ "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+ "\tAND COMPACT STORAGE"));
+ }
+
+ @Test
+ public void testDenseCompactTableWithoutRegulars() throws Throwable
+ {
+ String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" +
+ "pk1 varint," +
+ "ck1 int," +
+ "PRIMARY KEY (pk1, ck1))" +
+ " WITH COMPACT STORAGE");
+
+ ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName);
+
+ assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+ "CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" +
+ "\tpk1 varint,\n" +
+ "\tck1 int,\n" +
+ "\tPRIMARY KEY (pk1, ck1))\n" +
+ "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+ "\tAND COMPACT STORAGE"));
+ }
+
+ @Test
+ public void testCompactDynamic() throws Throwable
+ {
+ String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" +
+ "pk1 varint," +
+ "ck1 int," +
+ "reg int," +
+ "PRIMARY KEY (pk1, ck1))" +
+ " WITH COMPACT STORAGE");
+
+ ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName);
+
+ assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+ "CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" +
+ "\tpk1 varint,\n" +
+ "\tck1 int,\n" +
+ "\treg int,\n" +
+ "\tPRIMARY KEY (pk1, ck1))\n" +
+ "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+ "\tAND COMPACT STORAGE"));
+ }
+
+ @Test
+ public void testDynamicComposite() throws Throwable
+ {
+ Map<Byte, AbstractType<?>> aliases = new HashMap<>();
+ aliases.put((byte)'a', BytesType.instance);
+ aliases.put((byte)'b', BytesType.instance);
+ aliases.put((byte)'c', BytesType.instance);
+
+ String DYNAMIC_COMPOSITE = "dynamic_composite";
+ AbstractType<?> dynamicComposite = DynamicCompositeType.getInstance(aliases);
+
+ SchemaLoader.createKeyspace(DYNAMIC_COMPOSITE,
+ KeyspaceParams.simple(1),
+ SchemaLoader.denseCFMD(DYNAMIC_COMPOSITE, DYNAMIC_COMPOSITE, dynamicComposite));
+
+ ColumnFamilyStore cfs = Keyspace.open(DYNAMIC_COMPOSITE).getColumnFamilyStore(DYNAMIC_COMPOSITE);
+
+ assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+ "CREATE TABLE IF NOT EXISTS " + DYNAMIC_COMPOSITE + "." + DYNAMIC_COMPOSITE + " (\n" +
+ "\tkey ascii,\n" +
+ "\tcols 'org.apache.cassandra.db.marshal.DynamicCompositeType(a=>org.apache.cassandra.db.marshal.BytesType,b=>org.apache.cassandra.db.marshal.BytesType,c=>org.apache.cassandra.db.marshal.BytesType)',\n" +
+ "\tval ascii,\n" +
+ "\tPRIMARY KEY (key, cols))\n" +
+ "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+ "\tAND COMPACT STORAGE"));
+ }
+
+ @Test
+ public void superColumnFamilyTest() throws Throwable
+ {
+ final String KEYSPACE = "thrift_compact_table_with_supercolumns_test";
+ final String TABLE = "test_table_1";
+
+ CFMetaData cfm = CFMetaData.Builder.createSuper(KEYSPACE, TABLE, false)
+ .addPartitionKey("pk", BytesType.instance)
+ .addClusteringColumn("c1", AsciiType.instance)
+ .addClusteringColumn("c2", AsciiType.instance)
+ .addRegularColumn("", MapType.getInstance(Int32Type.instance, AsciiType.instance, true))
+ .build();
+
+ SchemaLoader.createKeyspace(KEYSPACE,
+ KeyspaceParams.simple(1),
+ cfm);
+
+ ColumnFamilyStore cfs = Keyspace.open(KEYSPACE).getColumnFamilyStore(TABLE);
+
+ assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+ "/*\n" +
+ "Warning: Table " + KEYSPACE + "." + TABLE + " omitted because it has constructs not compatible with CQL (was created via legacy API).\n\n" +
+ "Approximate structure, for reference:\n" +
+ "(this should not be used to reproduce this schema)\n\n" +
+ "CREATE TABLE IF NOT EXISTS " + KEYSPACE + "." + TABLE + " (\n" +
+ "\tpk blob,\n" +
+ "\tc1 ascii,\n" +
+ "\tc2 ascii,\n" +
+ "\t\"\" map<int, ascii>,\n" +
+ "\tPRIMARY KEY (pk, c1, c2))\n" +
+ "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+ "\tAND COMPACT STORAGE"));
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java b/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
index feb2778..3676ca3 100644
--- a/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
+++ b/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
@@ -36,6 +36,7 @@ import org.apache.cassandra.db.*;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.index.internal.CassandraIndex;
import org.apache.cassandra.thrift.ThriftConversion;
+import org.apache.cassandra.utils.*;
import static java.lang.String.format;
import static junit.framework.Assert.assertEquals;
@@ -284,7 +285,7 @@ public class LegacySchemaMigratorTest
for (String name : collectionColumnNames)
{
ColumnDefinition column = table.getColumnDefinition(bytes(name));
- table.recordColumnDrop(column);
+ table.recordColumnDrop(column, FBUtilities.timestampMicros());
table.removeColumnDefinition(column);
}
[23/50] [abbrv] cassandra git commit: Merge branch cassandra-3.0 into
cassandra-3.9
Posted by ms...@apache.org.
Merge branch cassandra-3.0 into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/0c771a2d
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/0c771a2d
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/0c771a2d
Branch: refs/heads/cassandra-3.8
Commit: 0c771a2db1d94901b4c7b194b9b71a115a1dc828
Parents: f4c3e14 c85749e
Author: Benjamin Lerer <b....@gmail.com>
Authored: Fri Aug 12 15:40:10 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Aug 12 15:40:55 2016 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../apache/cassandra/service/StorageProxy.java | 49 +++++++++++++-------
2 files changed, 34 insertions(+), 16 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/0c771a2d/CHANGES.txt
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/0c771a2d/src/java/org/apache/cassandra/service/StorageProxy.java
----------------------------------------------------------------------
[42/50] [abbrv] cassandra git commit: Merge branch cassandra-2.1 into
cassandra-2.2
Posted by ms...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
index bf5128f,0000000..bd5395a
mode 100644,000000..100644
--- a/test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
+++ b/test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
@@@ -1,1957 -1,0 +1,1957 @@@
+/*
+ * 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.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.Term.MultiItemTerminal;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.ColumnFamilyType;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.db.composites.Composite.EOC;
+import org.apache.cassandra.db.composites.Composites;
+import org.apache.cassandra.db.composites.CompoundSparseCellNameType;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.Int32Type;
+import org.apache.cassandra.db.marshal.ReversedType;
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class PrimaryKeyRestrictionSetTest
+{
+ @Test
+ public void testBoundsAsCompositesWithNoRestrictions()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC);
+
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ }
+
+ /**
+ * Test 'clustering_0 = 1' with only one clustering column
+ */
+ @Test
+ public void testBoundsAsCompositesWithOneEqRestrictionsAndOneClusteringColumn()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC);
+
+ ByteBuffer clustering_0 = ByteBufferUtil.bytes(1);
+ Restriction eq = newSingleEq(cfMetaData, 0, clustering_0);
+
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), clustering_0, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), clustering_0, EOC.END);
+ }
+
+ /**
+ * Test 'clustering_1 = 1' with 2 clustering columns
+ */
+ @Test
+ public void testBoundsAsCompositesWithOneEqRestrictionsAndTwoClusteringColumns()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ ByteBuffer clustering_0 = ByteBufferUtil.bytes(1);
+ Restriction eq = newSingleEq(cfMetaData, 0, clustering_0);
+
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), clustering_0, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), clustering_0, EOC.END);
+ }
+
+ /**
+ * Test 'clustering_0 IN (1, 2, 3)' with only one clustering column
+ */
+ @Test
+ public void testBoundsAsCompositesWithOneInRestrictionsAndOneClusteringColumn()
+ {
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ ByteBuffer value3 = ByteBufferUtil.bytes(3);
+
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ Restriction in = newSingleIN(cfMetaData, 0, value1, value2, value3);
+
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(in);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value2, EOC.START);
+ assertComposite(bounds.get(2), value3, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+ assertComposite(bounds.get(1), value2, EOC.END);
+ assertComposite(bounds.get(2), value3, EOC.END);
+ }
+
+ /**
+ * Test slice restriction (e.g 'clustering_0 > 1') with only one clustering column
+ */
+ @Test
+ public void testBoundsAsCompositesWithSliceRestrictionsAndOneClusteringColumn()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ Restriction slice = newSingleSlice(cfMetaData, 0, Bound.START, false, value1);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.START, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.END, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.END, false, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.START, false, value1);
+ Restriction slice2 = newSingleSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.START);
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.START, true, value1);
+ slice2 = newSingleSlice(cfMetaData, 0, Bound.END, true, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.END);
+ }
+
+ /**
+ * Test slice restriction (e.g 'clustering_0 > 1') with only one descending clustering column
+ */
+ @Test
+ public void testBoundsAsCompositesWithSliceRestrictionsAndOneDescendingClusteringColumn()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.DESC, Sort.DESC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ Restriction slice = newSingleSlice(cfMetaData, 0, Bound.START, false, value1);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.START, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.END, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.END, false, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.START, false, value1);
+ Restriction slice2 = newSingleSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.START, true, value1);
+ slice2 = newSingleSlice(cfMetaData, 0, Bound.END, true, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+ }
+
+ /**
+ * Test 'clustering_0 = 1 AND clustering_1 IN (1, 2, 3)'
+ */
+ @Test
+ public void testBoundsAsCompositesWithEqAndInRestrictions()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ ByteBuffer value3 = ByteBufferUtil.bytes(3);
+ Restriction eq = newSingleEq(cfMetaData, 0, value1);
+ Restriction in = newSingleIN(cfMetaData, 1, value1, value2, value3);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq).mergeWith(in);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.START);
+ assertComposite(bounds.get(2), value1, value3, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, value1, EOC.END);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+ assertComposite(bounds.get(2), value1, value3, EOC.END);
+ }
+
+ /**
+ * Test equal and slice restrictions (e.g 'clustering_0 = 0 clustering_1 > 1')
+ */
+ @Test
+ public void testBoundsAsCompositesWithEqAndSliceRestrictions()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ ByteBuffer value3 = ByteBufferUtil.bytes(3);
+
+ Restriction eq = newSingleEq(cfMetaData, 0, value3);
+
+ Restriction slice = newSingleSlice(cfMetaData, 1, Bound.START, false, value1);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, EOC.END);
+
+ slice = newSingleSlice(cfMetaData, 1, Bound.START, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, EOC.END);
+
+ slice = newSingleSlice(cfMetaData, 1, Bound.END, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value1, EOC.END);
+
+ slice = newSingleSlice(cfMetaData, 1, Bound.END, false, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value1, EOC.START);
+
+ slice = newSingleSlice(cfMetaData, 1, Bound.START, false, value1);
+ Restriction slice2 = newSingleSlice(cfMetaData, 1, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq).mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value2, EOC.START);
+
+ slice = newSingleSlice(cfMetaData, 1, Bound.START, true, value1);
+ slice2 = newSingleSlice(cfMetaData, 1, Bound.END, true, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq).mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value2, EOC.END);
+ }
+
+ /**
+ * Test '(clustering_0, clustering_1) = (1, 2)' with two clustering column
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiEqRestrictions()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ Restriction eq = newMultiEq(cfMetaData, 0, value1, value2);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+ }
+
+ /**
+ * Test '(clustering_0, clustering_1) IN ((1, 2), (2, 3))' with two clustering column
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiInRestrictions()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ ByteBuffer value3 = ByteBufferUtil.bytes(3);
+ Restriction in = newMultiIN(cfMetaData, 0, asList(value1, value2), asList(value2, value3));
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(in);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value2, value3, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+ assertComposite(bounds.get(1), value2, value3, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions (e.g '(clustering_0) > (1)') with only one clustering column
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneClusteringColumn()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.START);
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions (e.g '(clustering_0) > (1)') with only one clustering column in reverse
+ * order
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneDescendingClusteringColumn()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.DESC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions (e.g '(clustering_0, clustering_1) > (1, 2)')
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsWithTwoClusteringColumn()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ // (clustering_0, clustering1) > (1, 2)
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ // (clustering_0, clustering1) >= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ // (clustering_0, clustering1) <= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+
+ // (clustering_0, clustering1) < (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+
+ // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.START);
+
+ // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, value1, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions with 2 descending clustering columns (e.g '(clustering_0, clustering_1) > (1, 2)')
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsWithTwoDescendingClusteringColumns()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.DESC, Sort.DESC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ // (clustering_0, clustering1) > (1, 2)
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+
+ // (clustering_0, clustering1) >= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+
+ // (clustering_0, clustering1) <= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ // (clustering_0, clustering1) < (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+
+ // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+
+ // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions with 1 descending clustering column and 1 ascending
+ * (e.g '(clustering_0, clustering_1) > (1, 2)')
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneDescendingAndOneAscendingClusteringColumns()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.DESC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ // (clustering_0, clustering1) > (1, 2)
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
+ // (clustering_0, clustering1) >= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
+ // (clustering_0, clustering1) <= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+ assertEmptyComposite(bounds.get(1));
+
+ // (clustering_0, clustering1) < (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertEmptyComposite(bounds.get(1));
+
+ // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.END);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
+ // (clustering_0) > (1) AND (clustering_0, clustering1) < (2, 1)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.START);
+ assertComposite(bounds.get(1), value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value2, value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.START);
+
+ // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.START);
+ assertComposite(bounds.get(1), value2, EOC.END);
+ assertComposite(bounds.get(2), value1, value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value2, value1, EOC.END);
+ assertComposite(bounds.get(1), value1, EOC.START);
+ assertComposite(bounds.get(2), value1, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions with 1 descending clustering column and 1 ascending
+ * (e.g '(clustering_0, clustering_1) > (1, 2)')
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneAscendingAndOneDescendingClusteringColumns()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.DESC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ // (clustering_0, clustering1) > (1, 2)
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertEmptyComposite(bounds.get(1));
+
+ // (clustering_0, clustering1) >= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+ assertEmptyComposite(bounds.get(1));
+
+ // (clustering_0, clustering1) <= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
+ // (clustering_0, clustering1) < (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
+ // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value2, EOC.START);
+
+ // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+ assertComposite(bounds.get(2), value2, value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+ assertComposite(bounds.get(1), value2, EOC.START);
+ assertComposite(bounds.get(2), value2, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions with 2 ascending clustering column and 2 descending
+ * (e.g '(clustering_0, clustering1, clustering_3, clustering4) > (1, 2, 3, 4)')
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsWithTwoAscendingAndTwoDescendingClusteringColumns()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC, Sort.DESC, Sort.DESC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ ByteBuffer value3 = ByteBufferUtil.bytes(3);
+ ByteBuffer value4 = ByteBufferUtil.bytes(4);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) > (1, 2, 3, 4)
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2, value3, value4);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
+ assertEmptyComposite(bounds.get(1));
+
+ // clustering_0 = 1 AND (clustering_1, clustering_2, clustering_3) > (2, 3, 4)
+ Restriction eq = newSingleEq(cfMetaData, 0, value1);
+ slice = newMultiSlice(cfMetaData, 1, Bound.START, false, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+ restrictions = restrictions.mergeWith(eq);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
+ // clustering_0 = 1 AND (clustering_1, clustering_2, clustering_3) > (2, 3, 4)
+ Restriction in = newSingleIN(cfMetaData, 0, value1, value2);
+ slice = newMultiSlice(cfMetaData, 1, Bound.START, false, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+ restrictions = restrictions.mergeWith(in);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+ assertComposite(bounds.get(2), value2, value2, EOC.START);
+ assertComposite(bounds.get(3), value2, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+ assertComposite(bounds.get(2), value2, value2, value3, value4, EOC.START);
+ assertComposite(bounds.get(3), value2, EOC.END);
+
+ // (clustering_0, clustering1) >= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) >= (1, 2, 3, 4)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.END);
+ assertEmptyComposite(bounds.get(1));
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) <= (1, 2, 3, 4)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) < (1, 2, 3, 4)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) > (1, 2, 3, 4) AND (clustering_0, clustering_1) < (2, 3)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2, value3, value4);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2, value3);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
+ assertComposite(bounds.get(1), value2, value3, EOC.START);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) >= (1, 2, 3, 4) AND (clustering_0, clustering1, clustering_2, clustering_3) <= (4, 3, 2, 1)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2, value3, value4);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value4, value3, value2, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+ assertComposite(bounds.get(2), value4, value3, value2, value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.END);
+ assertComposite(bounds.get(1), value4, value3, EOC.START);
+ assertComposite(bounds.get(2), value4, value3, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions with ascending, descending, ascending and descending columns
+ * (e.g '(clustering_0, clustering1, clustering_3, clustering4) > (1, 2, 3, 4)')
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsMixingAscendingDescendingClusteringColumns()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.DESC, Sort.ASC, Sort.DESC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ ByteBuffer value3 = ByteBufferUtil.bytes(3);
+ ByteBuffer value4 = ByteBufferUtil.bytes(4);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) > (1, 2, 3, 4)
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2, value3, value4);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+ assertComposite(bounds.get(3), value1, EOC.END);
+
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, EOC.END);
+ assertEmptyComposite(bounds.get(3));
+
+ // clustering_0 = 1 AND (clustering_1, clustering_2, clustering_3) > (2, 3, 4)
+ Restriction eq = newSingleEq(cfMetaData, 0, value1);
+ slice = newMultiSlice(cfMetaData, 1, Bound.START, false, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+ restrictions = restrictions.mergeWith(eq);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, EOC.END);
+
+ // (clustering_0, clustering1) >= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+ assertEmptyComposite(bounds.get(1));
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) >= (1, 2, 3, 4)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+ assertComposite(bounds.get(3), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.END);
+ assertComposite(bounds.get(2), value1, value2, EOC.END);
+ assertEmptyComposite(bounds.get(3));
+
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) <= (1, 2, 3, 4)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, value4, EOC.NONE);
+ assertComposite(bounds.get(3), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+ assertComposite(bounds.get(3), value1, EOC.END);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) < (1, 2, 3, 4)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, value4, EOC.END);
+ assertComposite(bounds.get(3), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+ assertComposite(bounds.get(3), value1, EOC.END);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) > (1, 2, 3, 4) AND (clustering_0, clustering_1) < (2, 3)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2, value3, value4);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2, value3);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(5, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+ assertComposite(bounds.get(3), value1, EOC.END);
+ assertComposite(bounds.get(4), value2, value3, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(5, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, EOC.END);
+ assertComposite(bounds.get(3), value2, EOC.START);
+ assertComposite(bounds.get(4), value2, EOC.END);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) >= (1, 2, 3, 4) AND (clustering_0, clustering1, clustering_2, clustering_3) <= (4, 3, 2, 1)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2, value3, value4);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value4, value3, value2, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(7, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+ assertComposite(bounds.get(3), value1, EOC.END);
+ assertComposite(bounds.get(4), value4, value3, EOC.START);
+ assertComposite(bounds.get(5), value4, value3, value2, value1, EOC.NONE);
+ assertComposite(bounds.get(6), value4, value3, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(7, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.END);
+ assertComposite(bounds.get(2), value1, value2, EOC.END);
+ assertComposite(bounds.get(3), value4, EOC.START);
+ assertComposite(bounds.get(4), value4, value3, value2, EOC.START);
+ assertComposite(bounds.get(5), value4, value3, value2, EOC.END);
+ assertComposite(bounds.get(6), value4, EOC.END);
+ }
+
+ /**
+ * Test mixing single and multi equals restrictions (e.g. clustering_0 = 1 AND (clustering_1, clustering_2) = (2, 3))
+ */
+ @Test
+ public void testBoundsAsCompositesWithSingleEqAndMultiEqRestrictions()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC, Sort.ASC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ ByteBuffer value3 = ByteBufferUtil.bytes(3);
+ ByteBuffer value4 = ByteBufferUtil.bytes(4);
+
+ // clustering_0 = 1 AND (clustering_1, clustering_2) = (2, 3)
+ Restriction singleEq = newSingleEq(cfMetaData, 0, value1);
+ Restriction multiEq = newMultiEq(cfMetaData, 1, value2, value3);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(singleEq).mergeWith(multiEq);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, value3, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComp
<TRUNCATED>
[40/50] [abbrv] cassandra git commit: Merge branch cassandra-2.1 into
cassandra-2.2
Posted by ms...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
index c8df4c3,cef4635..ac1ba4c
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
@@@ -23,15 -23,18 +23,18 @@@ import java.util.UUID
import org.junit.Test;
import junit.framework.Assert;
-import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.UntypedResultSet;
-import org.apache.cassandra.cql3.CQLTester;
-import org.apache.cassandra.dht.Murmur3Partitioner;
+import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
import org.apache.cassandra.exceptions.InvalidRequestException;
- import org.apache.cassandra.utils.ByteBufferUtil;
+
+import org.apache.cassandra.cql3.CQLTester;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+
+ import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
+ import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+
/**
* Test column ranges and ordering with static column in table
*/
@@@ -1367,891 -1240,6 +1370,891 @@@ public class SelectTest extends CQLTest
}
@Test
+ public void testAlias() throws Throwable
+ {
+ createTable("CREATE TABLE %s (id int PRIMARY KEY, name text)");
+
+ for (int i = 0; i < 5; i++)
+ execute("INSERT INTO %s (id, name) VALUES (?, ?) USING TTL 10 AND TIMESTAMP 0", i, Integer.toString(i));
+
+ assertInvalidMessage("Aliases aren't allowed in the where clause",
+ "SELECT id AS user_id, name AS user_name FROM %s WHERE user_id = 0");
+
+ // test that select throws a meaningful exception for aliases in order by clause
+ assertInvalidMessage("Aliases are not allowed in order by clause",
+ "SELECT id AS user_id, name AS user_name FROM %s WHERE id IN (0) ORDER BY user_name");
+
+ }
+
+ @Test
+ public void testFilteringWithoutIndices() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c int, d int, e map<int, int>, PRIMARY KEY (a, b))");
+
+ // Checks filtering
+ assertInvalidMessage("Predicates on non-primary-key columns (c, d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = 1 AND d = 2 ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE a = 1 AND b = 1 AND c = 2 ALLOW FILTERING");
+ assertInvalidMessage("IN predicates on non-primary-key columns (c) is not yet supported",
+ "SELECT * FROM %s WHERE a IN (1, 2) AND c IN (2, 3) ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > 2 ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS 1 ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY 1 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING", unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING", unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS ? ALLOW FILTERING", unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY ? ALLOW FILTERING", unset());
+
+ createTable("CREATE TABLE %s (a int, b int, c int, PRIMARY KEY(a)) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 2, 8)");
+ execute("INSERT INTO %s (a, b, c) VALUES (3, 6, 4)");
+
+ assertRows(execute("SELECT * FROM %s WHERE c = 4 ALLOW FILTERING"),
+ row(1, 2, 4),
+ row(3, 6, 4));
+
+ // Checks filtering with null
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Unsupported null value for column c", "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage("Unsupported null value for column c", "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c = ?", unset());
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > ?", unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING", unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING", unset());
+ }
+
+ @Test
+ public void testFilteringOnStaticColumnWithoutIndices() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, s int static, c int, PRIMARY KEY (a, b))");
+
+ // Checks filtering
+ assertInvalidMessage("Predicates on non-primary-key columns (c, s) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = 1 AND s = 2 ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (s) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE a = 1 AND b = 1 AND s = 2 ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (s) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE s > 2 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (s) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE s = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (s) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE s > null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (s) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE s = ? ALLOW FILTERING", unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (s) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE s > ? ALLOW FILTERING", unset());
+ }
+
+ @Test
+ public void testFilteringOnCompactTablesWithoutIndices() throws Throwable
+ {
+ //----------------------------------------------
+ // Test COMPACT table with clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int, b int, c int, PRIMARY KEY (a, b)) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 3, 6)");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 4, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 3, 7)");
+
+ // Lets add some tombstones to make sure that filtering handle them properly
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 1, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 2, 7)");
+ execute("DELETE FROM %s WHERE a = 1 AND b = 1");
+ execute("DELETE FROM %s WHERE a = 2 AND b = 2");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE a = 1 AND b = 4 AND c = 4 ALLOW FILTERING");
+
+ assertInvalidMessage("IN predicates on non-primary-key columns (c) is not yet supported",
+ "SELECT * FROM %s WHERE a IN (1, 2) AND c IN (6, 7) ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > 4 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+
+ //----------------------------------------------
+ // Test COMPACT table without clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int PRIMARY KEY, b int, c int) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 1, 6)");
+ execute("INSERT INTO %s (a, b, c) VALUES (3, 2, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES (4, 1, 7)");
+
+ // Lets add some tombstones to make sure that filtering handle them properly
+ execute("INSERT INTO %s (a, b, c) VALUES (0, 1, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES (5, 2, 7)");
+ execute("DELETE FROM %s WHERE a = 0");
+ execute("DELETE FROM %s WHERE a = 5");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE a = 1 AND b = 4 AND c = 4");
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 1 AND b = 2 AND c = 4 ALLOW FILTERING"),
+ row(1, 2, 4));
+
+ assertInvalidMessage("IN predicates on non-primary-key columns (c) is not yet supported",
+ "SELECT * FROM %s WHERE a IN (1, 2) AND c IN (6, 7)");
+
+ assertInvalidMessage("IN predicates on non-primary-key columns (c) is not yet supported",
+ "SELECT * FROM %s WHERE a IN (1, 2) AND c IN (6, 7) ALLOW FILTERING");
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > 4");
+
+ assertRows(execute("SELECT * FROM %s WHERE c > 4 ALLOW FILTERING"),
+ row(2, 1, 6),
+ row(4, 1, 7));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE b < 3 AND c <= 4");
+
+ assertRows(execute("SELECT * FROM %s WHERE b < 3 AND c <= 4 ALLOW FILTERING"),
+ row(1, 2, 4),
+ row(3, 2, 4));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c >= 3 AND c <= 6");
+
+ assertRows(execute("SELECT * FROM %s WHERE c >= 3 AND c <= 6 ALLOW FILTERING"),
+ row(1, 2, 4),
+ row(2, 1, 6),
+ row(3, 2, 4));
+
+ // Checks filtering with null
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+ }
+
+ @Test
+ public void testFilteringWithoutIndicesWithCollections() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c list<int>, d set<int>, e map<int, int>, PRIMARY KEY (a, b))");
+
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (1, 2, [1, 6], {2, 12}, {1: 6})");
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (1, 3, [3, 2], {6, 4}, {3: 2})");
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (1, 4, [1, 2], {2, 4}, {1: 2})");
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (2, 3, [3, 6], {6, 12}, {3: 6})");
+
+ flush();
+
+ // Checks filtering for lists
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING");
+
+ // Checks filtering for sets
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d CONTAINS 4 ALLOW FILTERING");
+
+ // Checks filtering for maps
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS 2 ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY 2 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e[null] = 2 ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e[1] = null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e[?] = 2 ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e[1] = ? ALLOW FILTERING",
+ unset());
+ }
+
+ @Test
+ public void testFilteringWithoutIndicesWithFrozenCollections() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c frozen<list<int>>, d frozen<set<int>>, e frozen<map<int, int>>, PRIMARY KEY (a, b))");
+
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (1, 2, [1, 6], {2, 12}, {1: 6})");
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (1, 3, [3, 2], {6, 4}, {3: 2})");
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (1, 4, [1, 2], {2, 4}, {1: 2})");
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (2, 3, [3, 6], {6, 12}, {3: 6})");
+
+ flush();
+
+ // Checks filtering for lists
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = [3, 2] ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > [1, 5] AND c < [3, 6] ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING");
+
+ // Checks filtering for sets
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d = {6, 4} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d > {4, 5} AND d < {6} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d CONTAINS 4 ALLOW FILTERING");
+
+ // Checks filtering for maps
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e = {1 : 2} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e > {1 : 4} AND e < {3 : 6} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS 2 ALLOW FILTERING");
+
+ assertInvalidMessage("Map-entry equality predicates on frozen map column e are not supported",
+ "SELECT * FROM %s WHERE e[1] = 6 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY null ALLOW FILTERING");
+ assertInvalidMessage("Map-entry equality predicates on frozen map column e are not supported",
+ "SELECT * FROM %s WHERE e[null] = 2 ALLOW FILTERING");
+ assertInvalidMessage("Map-entry equality predicates on frozen map column e are not supported",
+ "SELECT * FROM %s WHERE e[1] = null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Map-entry equality predicates on frozen map column e are not supported",
+ "SELECT * FROM %s WHERE e[?] = 2 ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Map-entry equality predicates on frozen map column e are not supported",
+ "SELECT * FROM %s WHERE e[1] = ? ALLOW FILTERING",
+ unset());
+ }
+
+ @Test
+ public void testFilteringOnCompactTablesWithoutIndicesAndWithLists() throws Throwable
+ {
+ //----------------------------------------------
+ // Test COMPACT table with clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int, b int, c frozen<list<int>>, PRIMARY KEY (a, b)) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, [4, 2])");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 3, [6, 2])");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 4, [4, 1])");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 3, [7, 1])");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE a = 1 AND b = 4 AND c = [4, 1] ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > [4, 2] ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE b <= 3 AND c < [6, 2] ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING");
+
+ assertInvalidMessage("Cannot use CONTAINS KEY on non-map column c",
+ "SELECT * FROM %s WHERE c CONTAINS KEY 2 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+
+ //----------------------------------------------
+ // Test COMPACT table without clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int PRIMARY KEY, b int, c frozen<list<int>>) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, [4, 2])");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 1, [6, 2])");
+ execute("INSERT INTO %s (a, b, c) VALUES (3, 2, [4, 1])");
+ execute("INSERT INTO %s (a, b, c) VALUES (4, 1, [7, 1])");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE a = 1 AND b = 2 AND c = [4, 2]");
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 1 AND b = 2 AND c = [4, 2] ALLOW FILTERING"),
+ row(1, 2, list(4, 2)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > [4, 2]");
+
+ assertRows(execute("SELECT * FROM %s WHERE c > [4, 2] ALLOW FILTERING"),
+ row(2, 1, list(6, 2)),
+ row(4, 1, list(7, 1)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE b < 3 AND c <= [4, 2]");
+
+ assertRows(execute("SELECT * FROM %s WHERE b < 3 AND c <= [4, 2] ALLOW FILTERING"),
+ row(1, 2, list(4, 2)),
+ row(3, 2, list(4, 1)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c >= [4, 3] AND c <= [7]");
+
+ assertRows(execute("SELECT * FROM %s WHERE c >= [4, 3] AND c <= [7] ALLOW FILTERING"),
+ row(2, 1, list(6, 2)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c CONTAINS 2");
+
+ assertRows(execute("SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING"),
+ row(1, 2, list(4, 2)),
+ row(2, 1, list(6, 2)));
+
+ assertInvalidMessage("Cannot use CONTAINS KEY on non-map column c",
+ "SELECT * FROM %s WHERE c CONTAINS KEY 2 ALLOW FILTERING");
+
+ assertRows(execute("SELECT * FROM %s WHERE c CONTAINS 2 AND c CONTAINS 6 ALLOW FILTERING"),
+ row(2, 1, list(6, 2)));
+
+ // Checks filtering with null
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c CONTAINS null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+ }
+
+ @Test
+ public void testFilteringOnCompactTablesWithoutIndicesAndWithSets() throws Throwable
+ {
+ //----------------------------------------------
+ // Test COMPACT table with clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int, b int, c frozen<set<int>>, PRIMARY KEY (a, b)) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, {4, 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 3, {6, 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 4, {4, 1})");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 3, {7, 1})");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE a = 1 AND b = 4 AND c = {4, 1} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > {4, 2} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c >= {4, 2} AND c <= {6, 4} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING");
+
+ assertInvalidMessage("Cannot use CONTAINS KEY on non-map column c",
+ "SELECT * FROM %s WHERE c CONTAINS KEY 2 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+
+ //----------------------------------------------
+ // Test COMPACT table without clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int PRIMARY KEY, b int, c frozen<set<int>>) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, {4, 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 1, {6, 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (3, 2, {4, 1})");
+ execute("INSERT INTO %s (a, b, c) VALUES (4, 1, {7, 1})");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE a = 1 AND b = 2 AND c = {4, 2}");
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 1 AND b = 2 AND c = {4, 2} ALLOW FILTERING"),
+ row(1, 2, set(4, 2)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > {4, 2}");
+
+ assertRows(execute("SELECT * FROM %s WHERE c > {4, 2} ALLOW FILTERING"),
+ row(2, 1, set(6, 2)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE b < 3 AND c <= {4, 2}");
+
+ assertRows(execute("SELECT * FROM %s WHERE b < 3 AND c <= {4, 2} ALLOW FILTERING"),
+ row(1, 2, set(4, 2)),
+ row(4, 1, set(1, 7)),
+ row(3, 2, set(4, 1)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c >= {4, 3} AND c <= {7}");
+
+ assertRows(execute("SELECT * FROM %s WHERE c >= {5, 2} AND c <= {7} ALLOW FILTERING"),
+ row(2, 1, set(6, 2)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c CONTAINS 2");
+
+ assertRows(execute("SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING"),
+ row(1, 2, set(4, 2)),
+ row(2, 1, set(6, 2)));
+
+ assertInvalidMessage("Cannot use CONTAINS KEY on non-map column c",
+ "SELECT * FROM %s WHERE c CONTAINS KEY 2 ALLOW FILTERING");
+
+ assertRows(execute("SELECT * FROM %s WHERE c CONTAINS 2 AND c CONTAINS 6 ALLOW FILTERING"),
+ row(2, 1, set(6, 2)));
+
+ // Checks filtering with null
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c CONTAINS null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+ }
+
+ @Test
+ public void testIndexQueryWithValueOver64K() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c blob, PRIMARY KEY (a, b))");
+ createIndex("CREATE INDEX test ON %s (c)");
+
- execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 0, ByteBufferUtil.bytes(1));
- execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 1, ByteBufferUtil.bytes(2));
++ execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 0, bytes(1));
++ execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 1, bytes(2));
+
+ assertInvalidMessage("Index expression values may not be larger than 64K",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING", TOO_BIG);
+ }
+
+ @Test
+ public void testFilteringOnCompactTablesWithoutIndicesAndWithMaps() throws Throwable
+ {
+ //----------------------------------------------
+ // Test COMPACT table with clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int, b int, c frozen<map<int, int>>, PRIMARY KEY (a, b)) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, {4 : 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 3, {6 : 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 4, {4 : 1})");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 3, {7 : 1})");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE a = 1 AND b = 4 AND c = {4 : 1} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > {4 : 2} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE b <= 3 AND c < {6 : 2} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS KEY null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS KEY ? ALLOW FILTERING",
+ unset());
+
+ //----------------------------------------------
+ // Test COMPACT table without clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int PRIMARY KEY, b int, c frozen<map<int, int>>) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, {4 : 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 1, {6 : 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (3, 2, {4 : 1})");
+ execute("INSERT INTO %s (a, b, c) VALUES (4, 1, {7 : 1})");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE a = 1 AND b = 2 AND c = {4 : 2}");
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 1 AND b = 2 AND c = {4 : 2} ALLOW FILTERING"),
+ row(1, 2, map(4, 2)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > {4 : 2}");
+
+ assertRows(execute("SELECT * FROM %s WHERE c > {4 : 2} ALLOW FILTERING"),
+ row(2, 1, map(6, 2)),
+ row(4, 1, map(7, 1)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE b < 3 AND c <= {4 : 2}");
+
+ assertRows(execute("SELECT * FROM %s WHERE b < 3 AND c <= {4 : 2} ALLOW FILTERING"),
+ row(1, 2, map(4, 2)),
+ row(3, 2, map(4, 1)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c >= {4 : 3} AND c <= {7 : 1}");
+
+ assertRows(execute("SELECT * FROM %s WHERE c >= {5 : 2} AND c <= {7 : 0} ALLOW FILTERING"),
+ row(2, 1, map(6, 2)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c CONTAINS 2");
+
+ assertRows(execute("SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING"),
+ row(1, 2, map(4, 2)),
+ row(2, 1, map(6, 2)));
+
+ assertRows(execute("SELECT * FROM %s WHERE c CONTAINS KEY 4 ALLOW FILTERING"),
+ row(1, 2, map(4, 2)),
+ row(3, 2, map(4, 1)));
+
+ assertRows(execute("SELECT * FROM %s WHERE c CONTAINS 2 AND c CONTAINS KEY 6 ALLOW FILTERING"),
+ row(2, 1, map(6, 2)));
+
+ // Checks filtering with null
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c CONTAINS null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c CONTAINS KEY null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS KEY null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS KEY ? ALLOW FILTERING",
+ unset());
+ }
+
+ /**
+ * Check select with and without compact storage, with different column
+ * order. See CASSANDRA-10988
+ */
+ @Test
+ public void testClusteringOrderWithSlice() throws Throwable
+ {
+ for (String compactOption : new String[] { "", " COMPACT STORAGE AND" })
+ {
+ // non-compound, ASC order
+ createTable("CREATE TABLE %s (a text, b int, PRIMARY KEY (a, b)) WITH" +
+ compactOption +
+ " CLUSTERING ORDER BY (b ASC)");
+
+ execute("INSERT INTO %s (a, b) VALUES ('a', 2)");
+ execute("INSERT INTO %s (a, b) VALUES ('a', 3)");
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0"),
+ row("a", 2),
+ row("a", 3));
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0 ORDER BY b DESC"),
+ row("a", 3),
+ row("a", 2));
+
+ // non-compound, DESC order
+ createTable("CREATE TABLE %s (a text, b int, PRIMARY KEY (a, b)) WITH" +
+ compactOption +
+ " CLUSTERING ORDER BY (b DESC)");
+
+ execute("INSERT INTO %s (a, b) VALUES ('a', 2)");
+ execute("INSERT INTO %s (a, b) VALUES ('a', 3)");
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0"),
+ row("a", 3),
+ row("a", 2));
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0 ORDER BY b ASC"),
+ row("a", 2),
+ row("a", 3));
+
+ // compound, first column DESC order
+ createTable("CREATE TABLE %s (a text, b int, c int, PRIMARY KEY (a, b, c)) WITH" +
+ compactOption +
+ " CLUSTERING ORDER BY (b DESC)"
+ );
+
+ execute("INSERT INTO %s (a, b, c) VALUES ('a', 2, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES ('a', 3, 5)");
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0"),
+ row("a", 3, 5),
+ row("a", 2, 4));
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0 ORDER BY b ASC"),
+ row("a", 2, 4),
+ row("a", 3, 5));
+
+ // compound, mixed order
+ createTable("CREATE TABLE %s (a text, b int, c int, PRIMARY KEY (a, b, c)) WITH" +
+ compactOption +
+ " CLUSTERING ORDER BY (b ASC, c DESC)"
+ );
+
+ execute("INSERT INTO %s (a, b, c) VALUES ('a', 2, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES ('a', 3, 5)");
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0"),
+ row("a", 2, 4),
+ row("a", 3, 5));
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0 ORDER BY b ASC"),
+ row("a", 2, 4),
+ row("a", 3, 5));
+ }
+ }
+
+ @Test
public void testOverlyLargeSelectPK() throws Throwable
{
createTable("CREATE TABLE %s (a text, b text, PRIMARY KEY ((a), b))");
@@@ -2339,4 -1330,375 +2342,367 @@@
row(1, 2, 3),
row(1, 1, 3));
}
+
+ @Test
+ public void testEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("2"), bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk = textAsBlob('');");
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk IN (textAsBlob(''), textAsBlob('1'));");
+
+ assertInvalidMessage("Key may not be empty",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ EMPTY_BYTE_BUFFER, bytes("2"), bytes("2"));
+
+ // Test clustering columns restrictions
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND c < textAsBlob('');"));
+ }
+
+ if (options.contains("COMPACT"))
+ {
+ assertInvalidMessage("Missing PRIMARY KEY part c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
-
- // Test restrictions on non-primary key value
- assertInvalidMessage("Predicates on the non-primary-key column (v) of a COMPACT table are not yet supported",
- "SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;");
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND c < textAsBlob('');"));
-
- // Test restrictions on non-primary key value
- assertInvalidMessage("No secondary indexes on the restricted columns support the provided operators",
- "SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;");
+ }
+ }
++
++ // Test restrictions on non-primary key value
++ assertInvalidMessage("Predicates on non-primary-key columns (v) are not yet supported for non secondary index queries",
++ "SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;");
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumns() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob('1'), textAsBlob(''));"));
+
- assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
++ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
- assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 = textAsBlob('1');"));
++ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
++ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob('1'), textAsBlob(''));"));
+ }
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
- assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
- row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
-
- assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
++ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');"),
++ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) < (textAsBlob(''), textAsBlob('1'));"));
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("1"),
+ bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("2"),
+ bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause));
-
+ }
+
+ if (options.contains("COMPACT"))
+ {
+ assertInvalidMessage("Missing PRIMARY KEY part c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ EMPTY_BYTE_BUFFER,
+ bytes("4"));
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumnsAndOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c1 DESC, c2 DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c1 DESC, c2 DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c1 DESC, c2 DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+ }
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
- assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
- row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
-
- assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
++ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1')" + orderingClause),
++ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+ }
+ }
+ }
}
[07/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0'
into cassandra-3.9
Posted by ms...@apache.org.
Merge branch 'cassandra-3.0' into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/2d769f0c
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/2d769f0c
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/2d769f0c
Branch: refs/heads/cassandra-3.8
Commit: 2d769f0c5721be06a7401c69f45cfddf4d8fc736
Parents: 8458e4e 465def8
Author: Tyler Hobbs <ty...@gmail.com>
Authored: Wed Aug 10 11:12:18 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Wed Aug 10 11:12:18 2016 -0500
----------------------------------------------------------------------
CHANGES.txt | 2 ++
src/java/org/apache/cassandra/net/MessageOut.java | 2 +-
src/java/org/apache/cassandra/net/MessagingService.java | 10 ++++++++++
3 files changed, 13 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2d769f0c/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 8fbd774,86746ad..9f7631d
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -22,48 -17,6 +22,50 @@@ Merged from 3.0
to connect with too low of a protocol version (CASSANDRA-11464)
* NullPointerExpception when reading/compacting table (CASSANDRA-11988)
* Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
+Merged from 2.2:
+ * Fix hanging stream session (CASSANDRA-10992)
+ * Fix INSERT JSON, fromJson() support of smallint, tinyint types (CASSANDRA-12371)
+ * Release sstables of failed stream sessions only when outgoing transfers are finished (CASSANDRA-11345)
+ * Wait for tracing events before returning response and query at same consistency level client side (CASSANDRA-11465)
+ * cqlsh copyutil should get host metadata by connected address (CASSANDRA-11979)
+ * Fixed cqlshlib.test.remove_test_db (CASSANDRA-12214)
+Merged from 2.1:
+ * cannot use cql since upgrading python to 2.7.11+ (CASSANDRA-11850)
+ * Allow STCS-in-L0 compactions to reduce scope with LCS (CASSANDRA-12040)
+
+
+3.8
+ * RTE from new CDC column breaks in flight queries (CASSANDRA-12236)
+ * Fix hdr logging for single operation workloads (CASSANDRA-12145)
+ * Fix SASI PREFIX search in CONTAINS mode with partial terms (CASSANDRA-12073)
+ * Increase size of flushExecutor thread pool (CASSANDRA-12071)
+ * Partial revert of CASSANDRA-11971, cannot recycle buffer in SP.sendMessagesToNonlocalDC (CASSANDRA-11950)
+ * Upgrade netty to 4.0.39 (CASSANDRA-12032, CASSANDRA-12034)
+ * Improve details in compaction log message (CASSANDRA-12080)
+ * Allow unset values in CQLSSTableWriter (CASSANDRA-11911)
+ * Chunk cache to request compressor-compatible buffers if pool space is exhausted (CASSANDRA-11993)
+ * Remove DatabaseDescriptor dependencies from SequentialWriter (CASSANDRA-11579)
+ * Move skip_stop_words filter before stemming (CASSANDRA-12078)
+ * Support seek() in EncryptedFileSegmentInputStream (CASSANDRA-11957)
+ * SSTable tools mishandling LocalPartitioner (CASSANDRA-12002)
+ * When SEPWorker assigned work, set thread name to match pool (CASSANDRA-11966)
+ * Add cross-DC latency metrics (CASSANDRA-11596)
+ * Allow terms in selection clause (CASSANDRA-10783)
+ * Add bind variables to trace (CASSANDRA-11719)
+ * Switch counter shards' clock to timestamps (CASSANDRA-9811)
+ * Introduce HdrHistogram and response/service/wait separation to stress tool (CASSANDRA-11853)
+ * entry-weighers in QueryProcessor should respect partitionKeyBindIndexes field (CASSANDRA-11718)
+ * Support older ant versions (CASSANDRA-11807)
+ * Estimate compressed on disk size when deciding if sstable size limit reached (CASSANDRA-11623)
+ * cassandra-stress profiles should support case sensitive schemas (CASSANDRA-11546)
+ * Remove DatabaseDescriptor dependency from FileUtils (CASSANDRA-11578)
+ * Faster streaming (CASSANDRA-9766)
+ * Add prepared query parameter to trace for "Execute CQL3 prepared query" session (CASSANDRA-11425)
+ * Add repaired percentage metric (CASSANDRA-11503)
+ * Add Change-Data-Capture (CASSANDRA-8844)
+Merged from 3.0:
++ * Fix potential bad messaging service message for paged range reads
++ within mixed-version 3.x clusters (CASSANDRA-12249)
* Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
* Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
* Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2d769f0c/src/java/org/apache/cassandra/net/MessageOut.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2d769f0c/src/java/org/apache/cassandra/net/MessagingService.java
----------------------------------------------------------------------
[32/50] [abbrv] cassandra git commit: Merge branch 'cassandra-2.2'
into cassandra-3.0
Posted by ms...@apache.org.
Merge branch 'cassandra-2.2' into cassandra-3.0
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/74ac3cef
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/74ac3cef
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/74ac3cef
Branch: refs/heads/cassandra-3.8
Commit: 74ac3cef1068d46046560b4bf80fd1f47a439510
Parents: 5fdb90e 315356c
Author: Jason Brown <ja...@gmail.com>
Authored: Sun Aug 14 08:44:04 2016 -0700
Committer: Jason Brown <ja...@gmail.com>
Committed: Sun Aug 14 08:44:32 2016 -0700
----------------------------------------------------------------------
.../unit/org/apache/cassandra/streaming/StreamingTransferTest.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/74ac3cef/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
----------------------------------------------------------------------
[39/50] [abbrv] cassandra git commit: Fix queries with empty
ByteBuffer values in clustering column restrictions
Posted by ms...@apache.org.
Fix queries with empty ByteBuffer values in clustering column restrictions
patch by Benjamin Lerer; reviewed by jason Brown and Tyler Hobbs for CASSANDRA-12127
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/527d1897
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/527d1897
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/527d1897
Branch: refs/heads/cassandra-3.8
Commit: 527d1897c0973491bb0db098ce5ea1e0bd78898f
Parents: bd66547
Author: Benjamin Lerer <b....@gmail.com>
Authored: Tue Aug 16 15:17:59 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Tue Aug 16 15:17:59 2016 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
NEWS.txt | 10 +
.../cql3/statements/SelectStatement.java | 90 ++++-
.../cql3/statements/UpdateStatement.java | 2 +-
.../cassandra/db/compaction/Scrubber.java | 157 ++++++--
.../cassandra/db/marshal/ReversedType.java | 10 -
.../validation/entities/SecondaryIndexTest.java | 142 ++++++-
.../cql3/validation/operations/DeleteTest.java | 42 +++
.../cql3/validation/operations/SelectTest.java | 373 +++++++++++++++++++
.../cassandra/db/marshal/ReversedTypeTest.java | 4 +-
10 files changed, 782 insertions(+), 49 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 1275631..dee669a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.1.16
+ * Fix queries with empty ByteBuffer values in clustering column restrictions (CASSANDRA-12127)
* Disable passing control to post-flush after flush failure to prevent data loss (CASSANDRA-11828)
* Allow STCS-in-L0 compactions to reduce scope with LCS (CASSANDRA-12040)
* cannot use cql since upgrading python to 2.7.11+ (CASSANDRA-11850)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/NEWS.txt
----------------------------------------------------------------------
diff --git a/NEWS.txt b/NEWS.txt
index 7f1b54a..6a70adc 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -13,6 +13,16 @@ restore snapshots created with the previous major version using the
'sstableloader' tool. You can upgrade the file format of your snapshots
using the provided 'sstableupgrade' tool.
+2.1.16
+======
+
+Upgrading
+---------
+ - The ReversedType behaviour has been corrected for clustering columns of
+ BYTES type containing empty value. Scrub should be run on the existing
+ SSTables containing a descending clustering column of BYTES type to correct
+ their ordering. See CASSANDRA-12127 for details.
+
2.1.15
======
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 245e64e..40f3f33 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -30,6 +30,7 @@ import com.google.common.collect.Iterators;
import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.statements.Restriction.Slice;
import org.apache.cassandra.cql3.statements.SingleColumnRestriction.Contains;
import org.apache.cassandra.db.composites.*;
import org.apache.cassandra.transport.messages.ResultMessage;
@@ -246,6 +247,9 @@ public class SelectStatement implements CQLStatement
private Pageable getPageableCommand(QueryOptions options, int limit, long now) throws RequestValidationException
{
+ if (isNotReturningAnyRows(options))
+ return null;
+
int limitForQuery = updateLimitForQuery(limit);
if (isKeyRange || usesSecondaryIndexing)
return getRangeCommand(options, limitForQuery, now);
@@ -254,6 +258,47 @@ public class SelectStatement implements CQLStatement
return commands == null ? null : new Pageable.ReadCommands(commands, limitForQuery);
}
+ /**
+ * Checks if the query will never return any rows.
+ *
+ * @param options the query options
+ * @return {@code true} if the query will never return any rows, {@false} otherwise
+ * @throws InvalidRequestException if the request is invalid
+ */
+ private boolean isNotReturningAnyRows(QueryOptions options) throws InvalidRequestException
+ {
+ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that:
+ // - any query with an EQ restriction containing an empty value will not return any result
+ // - any query with a slice restriction with an empty value for the END bound will not return any result
+
+ if (cfm.comparator.isDense() && !cfm.comparator.isCompound())
+ {
+ for (Restriction restriction : columnRestrictions)
+ {
+ if (restriction != null)
+ {
+ if (restriction.isEQ())
+ {
+ for (ByteBuffer value : restriction.values(options))
+ {
+ if (!value.hasRemaining())
+ return true;
+ }
+ }
+ else if (restriction.isSlice() && ((Slice) restriction).hasBound(Bound.END))
+ {
+ ByteBuffer value = restriction.isMultiColumn()
+ ? ((MultiColumnRestriction.Slice) restriction).componentBounds(Bound.END, options).get(0)
+ : ((Slice) restriction).bound(Bound.END, options);
+
+ return !value.hasRemaining();
+ }
+ }
+ }
+ }
+ return false;
+ }
+
public Pageable getPageableCommand(QueryOptions options) throws RequestValidationException
{
return getPageableCommand(options, getLimit(options), System.currentTimeMillis());
@@ -820,6 +865,9 @@ public class SelectStatement implements CQLStatement
if (val == null)
throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s", def.name));
+ if (ignoreInValue(val))
+ continue;
+
Composite prefix = builder.buildWith(val);
columns.addAll(addSelectedColumns(prefix));
}
@@ -836,6 +884,9 @@ public class SelectStatement implements CQLStatement
throw new InvalidRequestException("Invalid null value in condition for column "
+ cfm.clusteringColumns().get(i + def.position()).name);
+ if (ignoreInValue(components))
+ continue;
+
Composite prefix = builder.buildWith(components);
inValues.addAll(addSelectedColumns(prefix));
}
@@ -846,6 +897,32 @@ public class SelectStatement implements CQLStatement
return addSelectedColumns(builder.build());
}
+ /**
+ * Checks if we should ignore the specified IN value for a clustering column as it will not return any result.
+ *
+ * @param val the IN value to check
+ * @return {@code true} if we should ignore the value, {@code false} otherwise.
+ */
+ private boolean ignoreInValue(ByteBuffer val)
+ {
+ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that we can
+ // ignore any IN value which is an empty byte buffer an which otherwise will trigger an error.
+ return !cfm.comparator.isCompound() && !val.hasRemaining();
+ }
+
+ /**
+ * Checks if we should ignore the specified IN components for a clustering column as it will not return any result.
+ *
+ * @param components the IN components to check
+ * @return {@code true} if we should ignore the value, {@code false} otherwise.
+ */
+ private boolean ignoreInValue(List<ByteBuffer> components)
+ {
+ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that we can
+ // ignore any IN value which is an empty byte buffer an which otherwise will trigger an error.
+ return !cfm.comparator.isCompound() && !components.get(0).hasRemaining();
+ }
+
private SortedSet<CellName> addSelectedColumns(Composite prefix)
{
if (cfm.comparator.isDense())
@@ -1200,12 +1277,17 @@ public class SelectStatement implements CQLStatement
if (sliceRestriction.isInclusive(bound))
return null;
- if (sliceRestriction.isMultiColumn())
- return type.makeCellName(((MultiColumnRestriction.Slice) sliceRestriction).componentBounds(bound, options).toArray());
- else
- return type.makeCellName(sliceRestriction.bound(bound, options));
+ // We can only reach that point if cfm.comparator.isCompound() = false and the table has some clustering columns.
+ // By consequence, we know that the table is a COMPACT table with only one clustering column.
+ ByteBuffer value = sliceRestriction.isMultiColumn() ? ((MultiColumnRestriction.Slice) sliceRestriction).componentBounds(bound, options).get(0)
+ : sliceRestriction.bound(bound, options);
+
+ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, if the slice value is empty
+ // we know that we can treat the slice as inclusive.
+ return value.hasRemaining() ? type.makeCellName(value) : null;
}
+
private Iterator<Cell> applySliceRestriction(final Iterator<Cell> cells, final QueryOptions options) throws InvalidRequestException
{
assert sliceRestriction != null;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
index bf9a059..39e632a 100644
--- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
@@ -84,7 +84,7 @@ public class UpdateStatement extends ModificationStatement
if (cfm.comparator.isDense())
{
if (prefix.isEmpty())
- throw new InvalidRequestException(String.format("Missing PRIMARY KEY part %s", cfm.clusteringColumns().iterator().next()));
+ throw new InvalidRequestException(String.format("Missing PRIMARY KEY part %s", cfm.clusteringColumns().iterator().next().name));
// An empty name for the compact value is what we use to recognize the case where there is not column
// outside the PK, see CreateStatement.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/src/java/org/apache/cassandra/db/compaction/Scrubber.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/compaction/Scrubber.java b/src/java/org/apache/cassandra/db/compaction/Scrubber.java
index 8bfd37b..2df3665 100644
--- a/src/java/org/apache/cassandra/db/compaction/Scrubber.java
+++ b/src/java/org/apache/cassandra/db/compaction/Scrubber.java
@@ -23,9 +23,11 @@ import java.util.*;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
+import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Sets;
import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.columniterator.OnDiskAtomIterator;
import org.apache.cassandra.io.sstable.*;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.io.util.RandomAccessReader;
@@ -223,20 +225,8 @@ public class Scrubber implements Closeable
if (indexFile != null && dataSize != dataSizeFromIndex)
outputHandler.warn(String.format("Data file row size %d different from index file row size %d", dataSize, dataSizeFromIndex));
- SSTableIdentityIterator atoms = new SSTableIdentityIterator(sstable, dataFile, key, dataSize, validateColumns);
- if (prevKey != null && prevKey.compareTo(key) > 0)
- {
- saveOutOfOrderRow(prevKey, key, atoms);
- continue;
- }
-
- AbstractCompactedRow compactedRow = new LazilyCompactedRow(controller, Collections.singletonList(atoms));
- if (writer.tryAppend(compactedRow) == null)
- emptyRows++;
- else
- goodRows++;
-
- prevKey = key;
+ if (tryAppend(prevKey, key, dataSize, writer))
+ prevKey = key;
}
catch (Throwable th)
{
@@ -252,21 +242,8 @@ public class Scrubber implements Closeable
try
{
dataFile.seek(dataStartFromIndex);
-
- SSTableIdentityIterator atoms = new SSTableIdentityIterator(sstable, dataFile, key, dataSize, validateColumns);
- if (prevKey != null && prevKey.compareTo(key) > 0)
- {
- saveOutOfOrderRow(prevKey, key, atoms);
- continue;
- }
-
- AbstractCompactedRow compactedRow = new LazilyCompactedRow(controller, Collections.singletonList(atoms));
- if (writer.tryAppend(compactedRow) == null)
- emptyRows++;
- else
- goodRows++;
-
- prevKey = key;
+ if (tryAppend(prevKey, key, dataSize, writer))
+ prevKey = key;
}
catch (Throwable th2)
{
@@ -339,6 +316,32 @@ public class Scrubber implements Closeable
}
}
+ @SuppressWarnings("resource")
+ private boolean tryAppend(DecoratedKey prevKey, DecoratedKey key, long dataSize, SSTableRewriter writer)
+ {
+ // OrderCheckerIterator will check, at iteration time, that the cells are in the proper order. If it detects
+ // that one cell is out of order, it will stop returning them. The remaining cells will be sorted and added
+ // to the outOfOrderRows that will be later written to a new SSTable.
+ OrderCheckerIterator atoms = new OrderCheckerIterator(new SSTableIdentityIterator(sstable, dataFile, key, dataSize, validateColumns),
+ cfs.metadata.comparator.onDiskAtomComparator());
+ if (prevKey != null && prevKey.compareTo(key) > 0)
+ {
+ saveOutOfOrderRow(prevKey, key, atoms);
+ return false;
+ }
+
+ AbstractCompactedRow compactedRow = new LazilyCompactedRow(controller, Collections.singletonList(atoms));
+ if (writer.tryAppend(compactedRow) == null)
+ emptyRows++;
+ else
+ goodRows++;
+
+ if (atoms.hasOutOfOrderCells())
+ saveOutOfOrderRow(key, atoms);
+
+ return true;
+ }
+
private void updateIndexKey()
{
currentIndexKey = nextIndexKey;
@@ -385,12 +388,12 @@ public class Scrubber implements Closeable
}
}
- private void saveOutOfOrderRow(DecoratedKey prevKey, DecoratedKey key, SSTableIdentityIterator atoms)
+ private void saveOutOfOrderRow(DecoratedKey prevKey, DecoratedKey key, OnDiskAtomIterator atoms)
{
saveOutOfOrderRow(key, atoms, String.format("Out of order row detected (%s found after %s)", key, prevKey));
}
- void saveOutOfOrderRow(DecoratedKey key, SSTableIdentityIterator atoms, String message)
+ void saveOutOfOrderRow(DecoratedKey key, OnDiskAtomIterator atoms, String message)
{
// TODO bitch if the row is too large? if it is there's not much we can do ...
outputHandler.warn(message);
@@ -405,6 +408,12 @@ public class Scrubber implements Closeable
outOfOrderRows.add(new Row(key, cf));
}
+ void saveOutOfOrderRow(DecoratedKey key, OrderCheckerIterator atoms)
+ {
+ outputHandler.warn(String.format("Out of order cells found at key %s", key));
+ outOfOrderRows.add(new Row(key, atoms.getOutOfOrderCells()));
+ }
+
public SSTableReader getNewSSTable()
{
return newSstable;
@@ -506,4 +515,90 @@ public class Scrubber implements Closeable
this.emptyRows = scrubber.emptyRows;
}
}
+
+ /**
+ * In some case like CASSANDRA-12127 the cells might have been stored in the wrong order. This decorator check the
+ * cells order and collect the out of order cells to correct the problem.
+ */
+ private static final class OrderCheckerIterator extends AbstractIterator<OnDiskAtom> implements OnDiskAtomIterator
+ {
+ /**
+ * The decorated iterator.
+ */
+ private final OnDiskAtomIterator iterator;
+
+ /**
+ * The atom comparator.
+ */
+ private final Comparator<OnDiskAtom> comparator;
+
+ /**
+ * The Column family containing the cells which are out of order.
+ */
+ private ColumnFamily outOfOrderCells;
+
+ /**
+ * The previous atom returned
+ */
+ private OnDiskAtom previous;
+
+ public OrderCheckerIterator(OnDiskAtomIterator iterator, Comparator<OnDiskAtom> comparator)
+ {
+ this.iterator = iterator;
+ this.comparator = comparator;
+ }
+
+ public ColumnFamily getColumnFamily()
+ {
+ return iterator.getColumnFamily();
+ }
+
+ public DecoratedKey getKey()
+ {
+ return iterator.getKey();
+ }
+
+ public void close() throws IOException
+ {
+ iterator.close();
+ }
+
+ @Override
+ protected OnDiskAtom computeNext()
+ {
+ if (!iterator.hasNext())
+ return endOfData();
+
+ OnDiskAtom next = iterator.next();
+
+ // If we detect that some cells are out of order we will store and sort the remaining once to insert them
+ // in a separate SSTable.
+ if (previous != null && comparator.compare(next, previous) < 0)
+ {
+ outOfOrderCells = collectOutOfOrderCells(next, iterator);
+ return endOfData();
+ }
+ previous = next;
+ return next;
+ }
+
+ public boolean hasOutOfOrderCells()
+ {
+ return outOfOrderCells != null;
+ }
+
+ public ColumnFamily getOutOfOrderCells()
+ {
+ return outOfOrderCells;
+ }
+
+ private static ColumnFamily collectOutOfOrderCells(OnDiskAtom atom, OnDiskAtomIterator iterator)
+ {
+ ColumnFamily cf = iterator.getColumnFamily().cloneMeShallow(ArrayBackedSortedColumns.factory, false);
+ cf.addAtom(atom);
+ while (iterator.hasNext())
+ cf.addAtom(iterator.next());
+ return cf;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/src/java/org/apache/cassandra/db/marshal/ReversedType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/ReversedType.java b/src/java/org/apache/cassandra/db/marshal/ReversedType.java
index 0389a32..53798f8 100644
--- a/src/java/org/apache/cassandra/db/marshal/ReversedType.java
+++ b/src/java/org/apache/cassandra/db/marshal/ReversedType.java
@@ -60,16 +60,6 @@ public class ReversedType<T> extends AbstractType<T>
public int compare(ByteBuffer o1, ByteBuffer o2)
{
- // An empty byte buffer is always smaller
- if (o1.remaining() == 0)
- {
- return o2.remaining() == 0 ? 0 : -1;
- }
- if (o2.remaining() == 0)
- {
- return 1;
- }
-
return baseType.compare(o2, o1);
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
index a433d06..4a54a9a 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
@@ -34,6 +34,8 @@ import org.apache.cassandra.utils.FBUtilities;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
+import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
public class SecondaryIndexTest extends CQLTester
{
@@ -562,7 +564,7 @@ public class SecondaryIndexTest extends CQLTester
{
createTable("CREATE TABLE %s(a int, b frozen<map<int, blob>>, PRIMARY KEY (a))");
createIndex("CREATE INDEX ON %s(full(b))");
- Map<Integer, ByteBuffer> map = new HashMap();
+ Map<Integer, ByteBuffer> map = new HashMap<>();
map.put(0, ByteBuffer.allocate(1024 * 65));
failInsert("INSERT INTO %s (a, b) VALUES (0, ?)", map);
}
@@ -641,4 +643,142 @@ public class SecondaryIndexTest extends CQLTester
assertInvalid("CREATE INDEX ON %s (c)");
}
+ @Test
+ public void testWithEmptyRestrictionValueAndSecondaryIndex() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))");
+ createIndex("CREATE INDEX on %s(c)");
+ createIndex("CREATE INDEX on %s(v)");
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("1"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ // Test clustering columns restrictions
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob('')) AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+ }
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ // Test restrictions on non-primary key value
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"));
+ }
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER);
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"),
+ row(bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER));
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithSecondaryIndexAndCompactTables() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c)) WITH COMPACT STORAGE");
+ assertInvalidMessage("Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables",
+ "CREATE INDEX on %s(c)");
+
+ createTable("CREATE TABLE %s (pk blob PRIMARY KEY, v blob) WITH COMPACT STORAGE");
+ createIndex("CREATE INDEX on %s(v)");
+
+ execute("INSERT INTO %s (pk, v) VALUES (?, ?)", bytes("foo123"), bytes("1"));
+
+ // Test restrictions on non-primary key value
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"));
+
+ execute("INSERT INTO %s (pk, v) VALUES (?, ?)", bytes("foo124"), EMPTY_BYTE_BUFFER);
+
+ assertRows(execute("SELECT * FROM %s WHERE v = textAsBlob('');"),
+ row(bytes("foo124"), EMPTY_BYTE_BUFFER));
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
index 476ec83..6bd5f26 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
@@ -23,11 +23,15 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+
import org.junit.Test;
import org.apache.cassandra.cql3.CQLTester;
import static org.junit.Assert.assertEquals;
+import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
+import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
public class DeleteTest extends CQLTester
{
@@ -326,4 +330,42 @@ public class DeleteTest extends CQLTester
assertEmpty(execute("select * from %s where a=1 and b=1"));
}
+
+ @Test
+ public void testDeleteWithEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY (pk, c))" + options);
+
+ if (StringUtils.isEmpty(options))
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+ }
+ else
+ {
+ assertInvalid("Invalid empty or null value for column c",
+ "DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('')");
+ assertInvalid("Invalid empty or null value for column c",
+ "DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'))");
+ }
+ }
+ }
+
+ @Test
+ public void testDeleteWithMultipleClusteringColumnsAndEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
index 68cf6f8..cef4635 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
@@ -32,6 +32,8 @@ import org.apache.cassandra.exceptions.InvalidRequestException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
+import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
/**
* Test column ranges and ordering with static column in table
@@ -1328,4 +1330,375 @@ public class SelectTest extends CQLTester
row(1, 2, 3),
row(1, 1, 3));
}
+
+ @Test
+ public void testEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("2"), bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk = textAsBlob('');");
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk IN (textAsBlob(''), textAsBlob('1'));");
+
+ assertInvalidMessage("Key may not be empty",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ EMPTY_BYTE_BUFFER, bytes("2"), bytes("2"));
+
+ // Test clustering columns restrictions
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND c < textAsBlob('');"));
+ }
+
+ if (options.contains("COMPACT"))
+ {
+ assertInvalidMessage("Missing PRIMARY KEY part c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
+ // Test restrictions on non-primary key value
+ assertInvalidMessage("Predicates on the non-primary-key column (v) of a COMPACT table are not yet supported",
+ "SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;");
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND c < textAsBlob('');"));
+
+ // Test restrictions on non-primary key value
+ assertInvalidMessage("No secondary indexes on the restricted columns support the provided operators",
+ "SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;");
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumns() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob('1'), textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 = textAsBlob('1');"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob('1'), textAsBlob(''));"));
+ }
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) < (textAsBlob(''), textAsBlob('1'));"));
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("1"),
+ bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("2"),
+ bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause));
+
+ }
+
+ if (options.contains("COMPACT"))
+ {
+ assertInvalidMessage("Missing PRIMARY KEY part c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ EMPTY_BYTE_BUFFER,
+ bytes("4"));
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumnsAndOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c1 DESC, c2 DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c1 DESC, c2 DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c1 DESC, c2 DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+ }
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+ }
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/test/unit/org/apache/cassandra/db/marshal/ReversedTypeTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/marshal/ReversedTypeTest.java b/test/unit/org/apache/cassandra/db/marshal/ReversedTypeTest.java
index 1553a20..7dfbc27 100644
--- a/test/unit/org/apache/cassandra/db/marshal/ReversedTypeTest.java
+++ b/test/unit/org/apache/cassandra/db/marshal/ReversedTypeTest.java
@@ -34,7 +34,7 @@ public class ReversedTypeTest
assert t.compare(bytes(4L), bytes(2L)) < 0;
// the empty byte buffer is always the smaller
- assert t.compare(EMPTY_BYTE_BUFFER, bytes(2L)) < 0;
- assert t.compare(bytes(2L), EMPTY_BYTE_BUFFER) > 0;
+ assert t.compare(EMPTY_BYTE_BUFFER, bytes(2L)) > 0;
+ assert t.compare(bytes(2L), EMPTY_BYTE_BUFFER) < 0;
}
}
[33/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0'
into cassandra-3.9
Posted by ms...@apache.org.
Merge branch 'cassandra-3.0' into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/903ac4d4
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/903ac4d4
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/903ac4d4
Branch: refs/heads/cassandra-3.8
Commit: 903ac4d4f0944c17e6ec923ffd4cdcaee65eee8d
Parents: d597910 74ac3ce
Author: Jason Brown <ja...@gmail.com>
Authored: Sun Aug 14 08:45:15 2016 -0700
Committer: Jason Brown <ja...@gmail.com>
Committed: Sun Aug 14 08:45:25 2016 -0700
----------------------------------------------------------------------
.../unit/org/apache/cassandra/streaming/StreamingTransferTest.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/903ac4d4/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
----------------------------------------------------------------------
[35/50] [abbrv] cassandra git commit: Merge branch 'cassandra-2.2'
into cassandra-3.0
Posted by ms...@apache.org.
Merge branch 'cassandra-2.2' into cassandra-3.0
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/e6dd1524
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/e6dd1524
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/e6dd1524
Branch: refs/heads/cassandra-3.8
Commit: e6dd15245b87480cfda6b34f9834f341d161ee34
Parents: 74ac3ce 88b3cfc
Author: Yuki Morishita <yu...@apache.org>
Authored: Mon Aug 15 10:09:46 2016 -0500
Committer: Yuki Morishita <yu...@apache.org>
Committed: Mon Aug 15 10:09:46 2016 -0500
----------------------------------------------------------------------
.../apache/cassandra/streaming/messages/OutgoingFileMessage.java | 3 +++
1 file changed, 3 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/e6dd1524/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java
----------------------------------------------------------------------
[11/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0'
into cassandra-3.9
Posted by ms...@apache.org.
Merge branch 'cassandra-3.0' into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/1fff069a
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/1fff069a
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/1fff069a
Branch: refs/heads/cassandra-3.8
Commit: 1fff069ad90b33dcda6d231ca5e327758f844388
Parents: c3e636c 2691c9e
Author: T Jake Luciani <ja...@apache.org>
Authored: Wed Aug 10 14:43:13 2016 -0400
Committer: T Jake Luciani <ja...@apache.org>
Committed: Wed Aug 10 14:43:13 2016 -0400
----------------------------------------------------------------------
CHANGES.txt | 3 +
.../metrics/CassandraMetricsRegistry.java | 4 +-
.../cassandra/metrics/ClearableHistogram.java | 4 +-
.../DecayingEstimatedHistogramReservoir.java | 549 +++++++++++++++++++
.../metrics/EstimatedHistogramReservoir.java | 111 ----
.../cassandra/utils/EstimatedHistogram.java | 2 +-
...DecayingEstimatedHistogramReservoirTest.java | 381 +++++++++++++
7 files changed, 938 insertions(+), 116 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/1fff069a/CHANGES.txt
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/1fff069a/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
----------------------------------------------------------------------
[06/50] [abbrv] cassandra git commit: Always use RANGE_SLICE verb for
3.x messages
Posted by ms...@apache.org.
Always use RANGE_SLICE verb for 3.x messages
Patch by Tyler Hobbs; reviewed by Benjamin Lerer for CASSANDRA-12249
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/465def8e
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/465def8e
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/465def8e
Branch: refs/heads/cassandra-3.8
Commit: 465def8e295b453dc22430dc4bb7039f6921151e
Parents: 62ef861
Author: Tyler Hobbs <ty...@gmail.com>
Authored: Wed Aug 10 11:09:38 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Wed Aug 10 11:09:38 2016 -0500
----------------------------------------------------------------------
CHANGES.txt | 2 ++
src/java/org/apache/cassandra/net/MessageOut.java | 2 +-
src/java/org/apache/cassandra/net/MessagingService.java | 10 ++++++++++
3 files changed, 13 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/465def8e/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index f613c5f..86746ad 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,6 @@
3.0.9
+ * Fix potential bad messaging service message for paged range reads
+ within mixed-version 3.x clusters (CASSANDRA-12249)
* Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
* NullPointerException during compaction on table with static columns (CASSANDRA-12336)
* Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/465def8e/src/java/org/apache/cassandra/net/MessageOut.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/net/MessageOut.java b/src/java/org/apache/cassandra/net/MessageOut.java
index a524e7a..ce190cb 100644
--- a/src/java/org/apache/cassandra/net/MessageOut.java
+++ b/src/java/org/apache/cassandra/net/MessageOut.java
@@ -109,7 +109,7 @@ public class MessageOut<T>
{
CompactEndpointSerializationHelper.serialize(from, out);
- out.writeInt(verb.ordinal());
+ out.writeInt(MessagingService.Verb.convertForMessagingServiceVersion(verb, version).ordinal());
out.writeInt(parameters.size());
for (Map.Entry<String, byte[]> entry : parameters.entrySet())
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/465def8e/src/java/org/apache/cassandra/net/MessagingService.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/net/MessagingService.java b/src/java/org/apache/cassandra/net/MessagingService.java
index d01419f..45cfb8a 100644
--- a/src/java/org/apache/cassandra/net/MessagingService.java
+++ b/src/java/org/apache/cassandra/net/MessagingService.java
@@ -148,6 +148,16 @@ public final class MessagingService implements MessagingServiceMBean
UNUSED_4,
UNUSED_5,
;
+
+ // This is to support a "late" choice of the verb based on the messaging service version.
+ // See CASSANDRA-12249 for more details.
+ public static Verb convertForMessagingServiceVersion(Verb verb, int version)
+ {
+ if (verb == PAGED_RANGE && version >= VERSION_30)
+ return RANGE_SLICE;
+
+ return verb;
+ }
}
public static final EnumMap<MessagingService.Verb, Stage> verbStages = new EnumMap<MessagingService.Verb, Stage>(MessagingService.Verb.class)
[05/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0'
into cassandra-3.9
Posted by ms...@apache.org.
Merge branch 'cassandra-3.0' into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/8458e4e3
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/8458e4e3
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/8458e4e3
Branch: refs/heads/cassandra-3.8
Commit: 8458e4e30744e8df6fcbe12aec286db047411385
Parents: ee60941 62ef861
Author: Yuki Morishita <yu...@apache.org>
Authored: Tue Aug 9 16:56:29 2016 -0500
Committer: Yuki Morishita <yu...@apache.org>
Committed: Tue Aug 9 16:56:29 2016 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../org/apache/cassandra/config/Config.java | 4 ++
.../cassandra/config/DatabaseDescriptor.java | 5 --
.../cassandra/streaming/StreamReader.java | 26 +---------
.../cassandra/streaming/StreamSession.java | 36 +-------------
.../compress/CompressedInputStream.java | 21 +++++++-
.../compress/CompressedStreamReader.java | 10 ++--
.../streaming/messages/IncomingFileMessage.java | 22 ++-------
.../streaming/messages/RetryMessage.java | 4 ++
.../org/apache/cassandra/utils/Throwables.java | 14 ++++++
.../compression/CompressedInputStreamTest.java | 52 +++++++++++++++++---
11 files changed, 100 insertions(+), 95 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index b7bbf72,f613c5f..8fbd774
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -22,47 -15,6 +22,48 @@@ Merged from 3.0
to connect with too low of a protocol version (CASSANDRA-11464)
* NullPointerExpception when reading/compacting table (CASSANDRA-11988)
* Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
+Merged from 2.2:
++ * Fix hanging stream session (CASSANDRA-10992)
+ * Fix INSERT JSON, fromJson() support of smallint, tinyint types (CASSANDRA-12371)
+ * Release sstables of failed stream sessions only when outgoing transfers are finished (CASSANDRA-11345)
+ * Wait for tracing events before returning response and query at same consistency level client side (CASSANDRA-11465)
+ * cqlsh copyutil should get host metadata by connected address (CASSANDRA-11979)
+ * Fixed cqlshlib.test.remove_test_db (CASSANDRA-12214)
+Merged from 2.1:
+ * cannot use cql since upgrading python to 2.7.11+ (CASSANDRA-11850)
+ * Allow STCS-in-L0 compactions to reduce scope with LCS (CASSANDRA-12040)
+
+
+3.8
+ * RTE from new CDC column breaks in flight queries (CASSANDRA-12236)
+ * Fix hdr logging for single operation workloads (CASSANDRA-12145)
+ * Fix SASI PREFIX search in CONTAINS mode with partial terms (CASSANDRA-12073)
+ * Increase size of flushExecutor thread pool (CASSANDRA-12071)
+ * Partial revert of CASSANDRA-11971, cannot recycle buffer in SP.sendMessagesToNonlocalDC (CASSANDRA-11950)
+ * Upgrade netty to 4.0.39 (CASSANDRA-12032, CASSANDRA-12034)
+ * Improve details in compaction log message (CASSANDRA-12080)
+ * Allow unset values in CQLSSTableWriter (CASSANDRA-11911)
+ * Chunk cache to request compressor-compatible buffers if pool space is exhausted (CASSANDRA-11993)
+ * Remove DatabaseDescriptor dependencies from SequentialWriter (CASSANDRA-11579)
+ * Move skip_stop_words filter before stemming (CASSANDRA-12078)
+ * Support seek() in EncryptedFileSegmentInputStream (CASSANDRA-11957)
+ * SSTable tools mishandling LocalPartitioner (CASSANDRA-12002)
+ * When SEPWorker assigned work, set thread name to match pool (CASSANDRA-11966)
+ * Add cross-DC latency metrics (CASSANDRA-11596)
+ * Allow terms in selection clause (CASSANDRA-10783)
+ * Add bind variables to trace (CASSANDRA-11719)
+ * Switch counter shards' clock to timestamps (CASSANDRA-9811)
+ * Introduce HdrHistogram and response/service/wait separation to stress tool (CASSANDRA-11853)
+ * entry-weighers in QueryProcessor should respect partitionKeyBindIndexes field (CASSANDRA-11718)
+ * Support older ant versions (CASSANDRA-11807)
+ * Estimate compressed on disk size when deciding if sstable size limit reached (CASSANDRA-11623)
+ * cassandra-stress profiles should support case sensitive schemas (CASSANDRA-11546)
+ * Remove DatabaseDescriptor dependency from FileUtils (CASSANDRA-11578)
+ * Faster streaming (CASSANDRA-9766)
+ * Add prepared query parameter to trace for "Execute CQL3 prepared query" session (CASSANDRA-11425)
+ * Add repaired percentage metric (CASSANDRA-11503)
+ * Add Change-Data-Capture (CASSANDRA-8844)
+Merged from 3.0:
* Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
* Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
* Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/src/java/org/apache/cassandra/config/Config.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/src/java/org/apache/cassandra/streaming/StreamReader.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/streaming/StreamReader.java
index 0cd6329,4ca7937..816f028
--- a/src/java/org/apache/cassandra/streaming/StreamReader.java
+++ b/src/java/org/apache/cassandra/streaming/StreamReader.java
@@@ -154,32 -153,12 +151,13 @@@ public class StreamReade
{
Directories.DataDirectory localDir = cfs.getDirectories().getWriteableLocation(totalSize);
if (localDir == null)
- throw new IOException("Insufficient disk space to store " + totalSize + " bytes");
- desc = Descriptor.fromFilename(cfs.getSSTablePath(cfs.getDirectories().getLocationForDisk(localDir), format));
+ throw new IOException(String.format("Insufficient disk space to store %s", FBUtilities.prettyPrintMemory(totalSize)));
- return cfs.createSSTableMultiWriter(desc, estimatedKeys, repairedAt, sstableLevel, getHeader(cfs.metadata), session.getTransaction(cfId));
+ RangeAwareSSTableWriter writer = new RangeAwareSSTableWriter(cfs, estimatedKeys, repairedAt, format, sstableLevel, totalSize, session.getTransaction(cfId), getHeader(cfs.metadata));
+ StreamHook.instance.reportIncomingFile(cfs, writer, session, fileSeqNum);
+ return writer;
}
- protected void drain(InputStream dis, long bytesRead) throws IOException
- {
- long toSkip = totalSize() - bytesRead;
-
- // InputStream.skip can return -1 if dis is inaccessible.
- long skipped = dis.skip(toSkip);
- if (skipped == -1)
- return;
-
- toSkip = toSkip - skipped;
- while (toSkip > 0)
- {
- skipped = dis.skip(toSkip);
- if (skipped == -1)
- break;
- toSkip = toSkip - skipped;
- }
- }
-
protected long totalSize()
{
long size = 0;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/src/java/org/apache/cassandra/streaming/StreamSession.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
index 3aaa1a3,d59849f..81abefa
--- a/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
+++ b/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
@@@ -79,31 -90,32 +88,37 @@@ public class CompressedInputStream exte
// buffer is limited to store up to 1024 chunks
this.dataBuffer = new ArrayBlockingQueue<>(Math.min(info.chunks.length, 1024));
this.crcCheckChanceSupplier = crcCheckChanceSupplier;
+ this.checksumType = checksumType;
- new Thread(new Reader(source, info, dataBuffer)).start();
+ new FastThreadLocalThread(new Reader(source, info, dataBuffer)).start();
}
- public int read() throws IOException
+ private void decompressNextChunk() throws IOException
{
+ if (readException != null)
+ throw readException;
+
- if (current >= bufferOffset + buffer.length || validBufferBytes == -1)
+ try
{
- try
+ byte[] compressedWithCRC = dataBuffer.take();
+ if (compressedWithCRC == POISON_PILL)
- throw new EOFException("No chunk available");
+ {
- byte[] compressedWithCRC = dataBuffer.take();
- if (compressedWithCRC == POISON_PILL)
- {
- assert readException != null;
- throw readException;
- }
- decompress(compressedWithCRC);
- }
- catch (InterruptedException e)
- {
- throw new EOFException("No chunk available");
++ assert readException != null;
++ throw readException;
+ }
+ decompress(compressedWithCRC);
+ }
+ catch (InterruptedException e)
+ {
+ throw new EOFException("No chunk available");
}
+ }
+
+ @Override
+ public int read() throws IOException
+ {
+ if (current >= bufferOffset + buffer.length || validBufferBytes == -1)
+ decompressNextChunk();
assert current >= bufferOffset && current < bufferOffset + validBufferBytes;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
index 8dafa9c,bc87c8f..70b5765
--- a/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
+++ b/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
@@@ -35,9 -36,11 +35,11 @@@ import org.apache.cassandra.streaming.P
import org.apache.cassandra.streaming.StreamReader;
import org.apache.cassandra.streaming.StreamSession;
import org.apache.cassandra.streaming.messages.FileMessageHeader;
-import org.apache.cassandra.io.util.TrackedInputStream;
+import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Pair;
+ import static org.apache.cassandra.utils.Throwables.extractIOExceptionCause;
+
/**
* StreamReader that reads from streamed compressed SSTable
*/
http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/test/unit/org/apache/cassandra/streaming/compression/CompressedInputStreamTest.java
----------------------------------------------------------------------
[49/50] [abbrv] cassandra git commit: Merge branch cassandra-3.0 into
cassandra-3.9
Posted by ms...@apache.org.
Merge branch cassandra-3.0 into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/cb97649f
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/cb97649f
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/cb97649f
Branch: refs/heads/cassandra-3.8
Commit: cb97649f51bb01285e813daae65cc0287e33b6c2
Parents: de2c449 bb56193
Author: Benjamin Lerer <b....@gmail.com>
Authored: Tue Aug 16 15:39:11 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Tue Aug 16 15:41:34 2016 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
NEWS.txt | 9 +
.../apache/cassandra/db/BufferClustering.java | 4 -
.../columniterator/AbstractSSTableIterator.java | 24 +-
.../db/columniterator/SSTableIterator.java | 17 +
.../columniterator/SSTableReversedIterator.java | 19 +-
.../cassandra/db/compaction/Scrubber.java | 169 +++-
.../cassandra/db/marshal/ReversedType.java | 10 -
.../db/partitions/AbstractBTreePartition.java | 7 +-
.../db/partitions/ImmutableBTreePartition.java | 34 +-
.../db/rows/UnfilteredRowIterators.java | 28 +
.../validation/entities/SecondaryIndexTest.java | 131 ++++
.../cql3/validation/operations/DeleteTest.java | 113 +++
.../operations/SelectOrderByTest.java | 765 ++++++++++---------
.../cql3/validation/operations/SelectTest.java | 355 ++++++++-
.../cassandra/db/marshal/ReversedTypeTest.java | 4 +-
16 files changed, 1268 insertions(+), 422 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/CHANGES.txt
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/NEWS.txt
----------------------------------------------------------------------
diff --cc NEWS.txt
index 249077d,0bd3920..2edff8e
--- a/NEWS.txt
+++ b/NEWS.txt
@@@ -13,35 -13,18 +13,44 @@@ restore snapshots created with the prev
'sstableloader' tool. You can upgrade the file format of your snapshots
using the provided 'sstableupgrade' tool.
-3.0.9
-=====
++3.9
++===
+
+ Upgrading
+ ---------
+ - The ReversedType behaviour has been corrected for clustering columns of
+ BYTES type containing empty value. Scrub should be run on the existing
+ SSTables containing a descending clustering column of BYTES type to correct
+ their ordering. See CASSANDRA-12127 for more details.
-3.0.8
-=====
+3.8
+===
+
+New features
+------------
+ - Shared pool threads are now named according to the stage they are executing
+ tasks for. Thread names mentioned in traced queries change accordingly.
+ - A new option has been added to cassandra-stress "-rate fixed={number}/s"
+ that forces a scheduled rate of operations/sec over time. Using this, stress can
+ accurately account for coordinated ommission from the stress process.
+ - The cassandra-stress "-rate limit=" option has been renamed to "-rate throttle="
+ - hdr histograms have been added to stress runs, it's output can be saved to disk using:
+ "-log hdrfile=" option. This histogram includes response/service/wait times when used with the
+ fixed or throttle rate options. The histogram file can be plotted on
+ http://hdrhistogram.github.io/HdrHistogram/plotFiles.html
+ - TimeWindowCompactionStrategy has been added. This has proven to be a better approach
+ to time series compaction and new tables should use this instead of DTCS. See
+ CASSANDRA-9666 for details.
+ - Change-Data-Capture is now available. See cassandra.yaml and for cdc-specific flags and
+ a brief explanation of on-disk locations for archived data in CommitLog form. This can
+ be enabled via ALTER TABLE ... WITH cdc=true.
+ Upon flush, CommitLogSegments containing data for CDC-enabled tables are moved to
+ the data/cdc_raw directory until removed by the user and writes to CDC-enabled tables
+ will be rejected with a WriteTimeoutException once cdc_total_space_in_mb is reached
+ between unflushed CommitLogSegments and cdc_raw.
+ NOTE: CDC is disabled by default in the .yaml file. Do not enable CDC on a mixed-version
+ cluster as it will lead to exceptions which can interrupt traffic. Once all nodes
+ have been upgraded to 3.8 it is safe to enable this feature and restart the cluster.
Upgrading
---------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/src/java/org/apache/cassandra/db/BufferClustering.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/BufferClustering.java
index 7c6bb20,0000000..df6a473
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/db/BufferClustering.java
+++ b/src/java/org/apache/cassandra/db/BufferClustering.java
@@@ -1,44 -1,0 +1,40 @@@
+/*
+ * 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.db;
+
+import java.nio.ByteBuffer;
+
- import org.apache.cassandra.config.CFMetaData;
- import org.apache.cassandra.config.ColumnDefinition;
- import org.apache.cassandra.utils.memory.AbstractAllocator;
-
+/**
+ * The clustering column values for a row.
+ * <p>
+ * A {@code Clustering} is a {@code ClusteringPrefix} that must always be "complete", i.e. have
+ * as many values as there is clustering columns in the table it is part of. It is the clustering
+ * prefix used by rows.
+ * <p>
+ * Note however that while it's size must be equal to the table clustering size, a clustering can have
+ * {@code null} values, and this mostly for thrift backward compatibility (in practice, if a value is null,
+ * all of the following ones will be too because that's what thrift allows, but it's never assumed by the
+ * code so we could start generally allowing nulls for clustering columns if we wanted to).
+ */
+public class BufferClustering extends AbstractBufferClusteringPrefix implements Clustering
+{
+ BufferClustering(ByteBuffer... values)
+ {
+ super(Kind.CLUSTERING, values);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/src/java/org/apache/cassandra/db/columniterator/AbstractSSTableIterator.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/columniterator/AbstractSSTableIterator.java
index 005bb2c,d57e6bc..5959a20
--- a/src/java/org/apache/cassandra/db/columniterator/AbstractSSTableIterator.java
+++ b/src/java/org/apache/cassandra/db/columniterator/AbstractSSTableIterator.java
@@@ -47,13 -46,8 +47,12 @@@ public abstract class AbstractSSTableIt
private final boolean isForThrift;
+ protected final SegmentedFile ifile;
+
private boolean isClosed;
+ protected final Slices slices;
- protected int slice;
+
@SuppressWarnings("resource") // We need this because the analysis is not able to determine that we do close
// file on every path where we created it.
protected AbstractSSTableIterator(SSTableReader sstable,
@@@ -119,9 -109,6 +118,9 @@@
this.reader = needsReader ? createReader(indexEntry, file, shouldCloseFile) : null;
}
+ if (reader != null && !slices.isEmpty())
- reader.setForSlice(slices.get(0));
++ reader.setForSlice(nextSlice());
+
if (reader == null && file != null && shouldCloseFile)
file.close();
}
@@@ -145,6 -132,6 +144,23 @@@
}
}
++ private Slice nextSlice()
++ {
++ return slices.get(nextSliceIndex());
++ }
++
++ /**
++ * Returns the index of the next slice to process.
++ * @return the index of the next slice to process
++ */
++ protected abstract int nextSliceIndex();
++
++ /**
++ * Checks if there are more slice to process.
++ * @return {@code true} if there are more slice to process, {@code false} otherwise.
++ */
++ protected abstract boolean hasMoreSlices();
++
private static Row readStaticRow(SSTableReader sstable,
FileDataInput file,
SerializationHelper helper,
@@@ -233,19 -216,7 +249,19 @@@
public boolean hasNext()
{
- return reader != null && reader.hasNext();
+ while (true)
+ {
+ if (reader == null)
+ return false;
+
+ if (reader.hasNext())
+ return true;
+
- if (++slice >= slices.size())
++ if (!hasMoreSlices())
+ return false;
+
- slice(slices.get(slice));
++ slice(nextSlice());
+ }
}
public Unfiltered next()
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/src/java/org/apache/cassandra/db/columniterator/SSTableIterator.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/columniterator/SSTableIterator.java
index e4f6700,0409310..56ba8f4
--- a/src/java/org/apache/cassandra/db/columniterator/SSTableIterator.java
+++ b/src/java/org/apache/cassandra/db/columniterator/SSTableIterator.java
@@@ -32,6 -31,11 +32,11 @@@ import org.apache.cassandra.io.util.Seg
*/
public class SSTableIterator extends AbstractSSTableIterator
{
- public SSTableIterator(SSTableReader sstable, DecoratedKey key, ColumnFilter columns, boolean isForThrift)
- {
- this(sstable, null, key, sstable.getPosition(key, SSTableReader.Operator.EQ), columns, isForThrift);
- }
++ /**
++ * The index of the slice being processed.
++ */
++ private int slice;
+
public SSTableIterator(SSTableReader sstable,
FileDataInput file,
DecoratedKey key,
@@@ -51,6 -53,6 +56,18 @@@
: new ForwardReader(file, shouldCloseFile);
}
++ protected int nextSliceIndex()
++ {
++ int next = slice;
++ slice++;
++ return next;
++ }
++
++ protected boolean hasMoreSlices()
++ {
++ return slice < slices.size();
++ }
++
public boolean isReverseOrder()
{
return false;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/src/java/org/apache/cassandra/db/columniterator/SSTableReversedIterator.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/columniterator/SSTableReversedIterator.java
index 903fd59,3e49a3a..b8b6f76
--- a/src/java/org/apache/cassandra/db/columniterator/SSTableReversedIterator.java
+++ b/src/java/org/apache/cassandra/db/columniterator/SSTableReversedIterator.java
@@@ -35,6 -34,11 +35,11 @@@ import org.apache.cassandra.utils.btree
*/
public class SSTableReversedIterator extends AbstractSSTableIterator
{
- public SSTableReversedIterator(SSTableReader sstable, DecoratedKey key, ColumnFilter columns, boolean isForThrift)
- {
- this(sstable, null, key, sstable.getPosition(key, SSTableReader.Operator.EQ), columns, isForThrift);
- }
++ /**
++ * The index of the slice being processed.
++ */
++ private int slice;
+
public SSTableReversedIterator(SSTableReader sstable,
FileDataInput file,
DecoratedKey key,
@@@ -59,6 -61,6 +64,18 @@@
return true;
}
++ protected int nextSliceIndex()
++ {
++ int next = slice;
++ slice++;
++ return slices.size() - (next + 1);
++ }
++
++ protected boolean hasMoreSlices()
++ {
++ return slice < slices.size();
++ }
++
private class ReverseReader extends Reader
{
protected ReusablePartitionData buffer;
@@@ -106,7 -108,7 +123,7 @@@
buffer = createBuffer(1);
// Note that we can reuse that buffer between slices (we could alternatively re-read from disk
// every time, but that feels more wasteful) so we want to include everything from the beginning.
-- // We can stop at the slice end however since any following slice will be before that.
++ // We can stop at the last slice end however since any following slice will be before that.
loadFromDisk(null, slice.end(), true);
}
setIterator(slice);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/src/java/org/apache/cassandra/db/compaction/Scrubber.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
index 7fb01a3,c63acc2..4a8e4f2
--- a/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
+++ b/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
@@@ -270,8 -282,47 +270,13 @@@ public abstract class AbstractBTreePart
}
}
- public class SliceableIterator extends AbstractIterator implements SliceableUnfilteredRowIterator
- {
- private Iterator<Unfiltered> iterator;
-
- protected SliceableIterator(ColumnFilter selection, boolean isReversed)
- {
- super(selection, isReversed);
- }
-
- protected Unfiltered computeNext()
- {
- if (iterator == null)
- iterator = unfilteredIterator(selection, Slices.ALL, isReverseOrder);
- if (!iterator.hasNext())
- return endOfData();
- return iterator.next();
- }
-
- public Iterator<Unfiltered> slice(Slice slice)
- {
- return sliceIterator(selection, slice, isReverseOrder, current, staticRow);
- }
- }
-
- public SliceableUnfilteredRowIterator sliceableUnfilteredIterator(ColumnFilter columns, boolean reversed)
- {
- return new SliceableIterator(columns, reversed);
- }
-
- protected SliceableUnfilteredRowIterator sliceableUnfilteredIterator()
- {
- return sliceableUnfilteredIterator(ColumnFilter.all(metadata), false);
- }
-
protected static Holder build(UnfilteredRowIterator iterator, int initialRowCapacity)
{
+ return build(iterator, initialRowCapacity, true);
+ }
+
+ protected static Holder build(UnfilteredRowIterator iterator, int initialRowCapacity, boolean ordered)
+ {
CFMetaData metadata = iterator.metadata();
PartitionColumns columns = iterator.columns();
boolean reversed = iterator.isReverseOrder();
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
index d324190,43653a9..46447ec
--- a/src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
+++ b/src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
@@@ -230,6 -213,60 +230,34 @@@ public abstract class UnfilteredRowIter
}
/**
+ * Returns an iterator that concatenate the specified atom with the iterator.
+ */
+ public static UnfilteredRowIterator concat(final Unfiltered first, final UnfilteredRowIterator rest)
+ {
+ return new WrappingUnfilteredRowIterator(rest)
+ {
+ private boolean hasReturnedFirst;
+
+ @Override
+ public boolean hasNext()
+ {
+ return hasReturnedFirst ? super.hasNext() : true;
+ }
+
+ @Override
+ public Unfiltered next()
+ {
+ if (!hasReturnedFirst)
+ {
+ hasReturnedFirst = true;
+ return first;
+ }
+ return super.next();
+ }
+ };
+ }
+
- public static UnfilteredRowIterator cloningIterator(UnfilteredRowIterator iterator, final AbstractAllocator allocator)
- {
- class Cloner extends Transformation
- {
- private final Row.Builder builder = allocator.cloningBTreeRowBuilder();
-
- public Row applyToStatic(Row row)
- {
- return Rows.copy(row, builder).build();
- }
-
- @Override
- public Row applyToRow(Row row)
- {
- return Rows.copy(row, builder).build();
- }
-
- @Override
- public RangeTombstoneMarker applyToMarker(RangeTombstoneMarker marker)
- {
- return marker.copy(allocator);
- }
- }
- return Transformation.apply(iterator, new Cloner());
- }
-
+ /**
* Validate that the data of the provided iterator is valid, that is that the values
* it contains are valid for the type they represent, and more generally that the
* infos stored are sensible.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
index 9b92ebb,b9ccafb..118a1e2
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
@@@ -1107,28 -1058,142 +1112,136 @@@ public class DeleteTest extends CQLTest
}
@Test
+ public void testDeleteAndReverseQueries() throws Throwable
+ {
+ // This test insert rows in one sstable and a range tombstone covering some of those rows in another, and it
+ // validates we correctly get only the non-removed rows when doing reverse queries.
+
+ createTable("CREATE TABLE %s (k text, i int, PRIMARY KEY (k, i))");
+
+ for (int i = 0; i < 10; i++)
+ execute("INSERT INTO %s(k, i) values (?, ?)", "a", i);
+
+ flush();
+
+ execute("DELETE FROM %s WHERE k = ? AND i >= ? AND i <= ?", "a", 2, 7);
+
+ assertRows(execute("SELECT i FROM %s WHERE k = ? ORDER BY i DESC", "a"),
+ row(9), row(8), row(1), row(0)
+ );
+
+ flush();
+
+ assertRows(execute("SELECT i FROM %s WHERE k = ? ORDER BY i DESC", "a"),
+ row(9), row(8), row(1), row(0)
+ );
+ }
++
++ @Test
+ public void testDeleteWithEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY (pk, c))" + options);
+
+ if (StringUtils.isEmpty(options))
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("2"));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')");
+
+ assertRows(execute("SELECT * FROM %s"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+ }
+ else
+ {
+ assertInvalid("Invalid empty or null value for column c",
+ "DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('')");
+ assertInvalid("Invalid empty or null value for column c",
+ "DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'))");
+ }
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("2"));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("2"));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')");
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')");
+
+ assertRows(execute("SELECT * FROM %s"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+ }
+ }
+
+ @Test
+ public void testDeleteWithMultipleClusteringColumnsAndEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("0"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("3"));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')");
+
+ assertRows(execute("SELECT * FROM %s"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("0")));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 >= textAsBlob('')");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("3"));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("3"));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 <= textAsBlob('')");
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 < textAsBlob('')");
+
+ assertRows(execute("SELECT * FROM %s"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("3")));
+ }
+ }
-
- private void flush(boolean forceFlush)
- {
- if (forceFlush)
- flush();
- }
-
- @Test
- public void testDeleteAndReverseQueries() throws Throwable
- {
- // This test insert rows in one sstable and a range tombstone covering some of those rows in another, and it
- // validates we correctly get only the non-removed rows when doing reverse queries.
-
- createTable("CREATE TABLE %s (k text, i int, PRIMARY KEY (k, i))");
-
- for (int i = 0; i < 10; i++)
- execute("INSERT INTO %s(k, i) values (?, ?)", "a", i);
-
- flush();
-
- execute("DELETE FROM %s WHERE k = ? AND i >= ? AND i <= ?", "a", 2, 7);
-
- assertRows(execute("SELECT i FROM %s WHERE k = ? ORDER BY i DESC", "a"),
- row(9), row(8), row(1), row(0)
- );
-
- flush();
-
- assertRows(execute("SELECT i FROM %s WHERE k = ? ORDER BY i DESC", "a"),
- row(9), row(8), row(1), row(0)
- );
- }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
index 6e06419,bb35458..bfc0c86
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
@@@ -105,15 -109,17 +109,17 @@@ public class SelectOrderByTest extends
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, {a: ?})", 0, 1, 1);
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, {a: ?})", 0, 2, 2);
- // order by a column not in the selection
- assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
+ beforeAndAfterFlush(() -> {
+ // order by a column not in the selection
+ assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
-
++ row(0), row(1), row(2));
+
- assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
+ assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
-
++ row(2), row(1), row(0));
+
- assertRows(execute("SELECT blobAsInt(intAsBlob(c.a)) FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c.a)) FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0));
+ });
dropTable("DROP TABLE %s");
}
}
@@@ -351,34 -368,36 +368,36 @@@
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 3, 3, "C");
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 1, 4, "D");
- assertRows(execute("SELECT v, ttl(v), c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B", null, 1),
- row("A", null, 2),
- row("D", null, 4));
-
- assertRows(execute("SELECT v, ttl(v), c as name_1 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B", null, 1),
- row("A", null, 2),
- row("D", null, 4));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 2; ", 1, 1, 2),
- row("B"),
- row("A"));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 10; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
-
- assertRows(execute("SELECT v as c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
+ beforeAndAfterFlush(() -> {
- assertRows(execute("SELECT v, ttl(v), c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B", null, 1),
- row("A", null, 2),
- row("D", null, 4));
-
- assertRows(execute("SELECT v, ttl(v), c as name_1 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B", null, 1),
- row("A", null, 2),
- row("D", null, 4));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 2; ", 1, 1, 2),
- row("B"),
- row("A"));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 10; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
-
- assertRows(execute("SELECT v as c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
++ assertRows(execute("SELECT v, ttl(v), c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B", null, 1),
++ row("A", null, 2),
++ row("D", null, 4));
++
++ assertRows(execute("SELECT v, ttl(v), c as name_1 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B", null, 1),
++ row("A", null, 2),
++ row("D", null, 4));
++
++ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B"),
++ row("A"),
++ row("D"));
++
++ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 2; ", 1, 1, 2),
++ row("B"),
++ row("A"));
++
++ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 10; ", 1, 1, 2),
++ row("B"),
++ row("A"),
++ row("D"));
++
++ assertRows(execute("SELECT v as c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B"),
++ row("A"),
++ row("D"));
+ });
createTable("CREATE TABLE %s (pk1 int, pk2 int, c1 int, c2 int, v text, PRIMARY KEY ((pk1, pk2), c1, c2) )");
execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", 1, 1, 4, 4, "A");
@@@ -534,22 -561,24 +561,24 @@@
{
createTable("CREATE TABLE %s (k text, c1 int, c2 int, PRIMARY KEY (k, c1, c2) ) WITH CLUSTERING ORDER BY (c1 ASC, c2 DESC)");
- beforeAndAfterFlush(() -> {
- for (int i = 0; i < 2; i++)
- for (int j = 0; j < 2; j++)
- execute("INSERT INTO %s (k, c1, c2) VALUES ('foo', ?, ?)", i, j);
+ for (int i = 0; i < 2; i++)
+ for (int j = 0; j < 2; j++)
+ execute("INSERT INTO %s (k, c1, c2) VALUES ('foo', ?, ?)", i, j);
- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo'"),
- row(0, 1), row(0, 0), row(1, 1), row(1, 0));
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo'"),
+ row(0, 1), row(0, 0), row(1, 1), row(1, 0));
- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 DESC"),
- row(0, 1), row(0, 0), row(1, 1), row(1, 0));
+ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 DESC"),
+ row(0, 1), row(0, 0), row(1, 1), row(1, 0));
- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 DESC, c2 ASC"),
- row(1, 0), row(1, 1), row(0, 0), row(0, 1));
+ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 DESC, c2 ASC"),
+ row(1, 0), row(1, 1), row(0, 0), row(0, 1));
- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 DESC");
- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 ASC");
- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 ASC");
+ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 DESC");
+ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 ASC");
+ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 ASC");
+ });
}
/**
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
index 9a1493b,a5f153e..db0a4cd
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
@@@ -21,16 -21,18 +21,18 @@@ import java.nio.ByteBuffer
import java.util.UUID;
import org.junit.Test;
++import org.junit.Assert;
- import junit.framework.Assert;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.cql3.UntypedResultSet;
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
import org.apache.cassandra.exceptions.InvalidRequestException;
- import org.apache.cassandra.utils.ByteBufferUtil;
-import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
-import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
++import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
++import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+
-import junit.framework.Assert;
/**
* Test column ranges and ordering with static column in table
@@@ -2973,22 -2571,356 +2975,367 @@@ public class SelectTest extends CQLTest
assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND c1 IN(0,1,2) AND c2 = 1 AND v = 3"),
row(1, 1, 1, 3, 3));
+ }
- assertInvalidMessage("Clustering column \"c2\" cannot be restricted (preceding column \"c1\" is restricted by a non-EQ relation)",
- "SELECT * FROM %s WHERE pk = 1 AND c1 > 0 AND c1 < 5 AND c2 = 1 ALLOW FILTERING;");
+ @Test
+ public void testIndexQueryWithCompositePartitionKey() throws Throwable
+ {
+ createTable("CREATE TABLE %s (p1 int, p2 int, v int, PRIMARY KEY ((p1, p2)))");
+ assertInvalidMessage("Partition key parts: p2 must be restricted as other parts are",
+ "SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW FILTERING");
+ createIndex("CREATE INDEX ON %s(v)");
- assertInvalidMessage("PRIMARY KEY column \"c2\" cannot be restricted as preceding column \"c1\" is not restricted",
- "SELECT * FROM %s WHERE pk = 1 AND c2 = 1 ALLOW FILTERING;");
+ execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 1, 3);
+ execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 2, 3);
+ execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 2, 1, 3);
+
+ assertRows(execute("SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW FILTERING"),
+ row(1, 2, 3),
+ row(1, 1, 3));
}
+
+ @Test
+ public void testEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("2"), bytes("2"));
+
+ beforeAndAfterFlush(() -> {
+
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk = textAsBlob('');");
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk IN (textAsBlob(''), textAsBlob('1'));");
+
+ assertInvalidMessage("Key may not be empty",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ EMPTY_BYTE_BUFFER, bytes("2"), bytes("2"));
+
+ // Test clustering columns restrictions
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND c < textAsBlob('');"));
+ });
+
+ if (options.contains("COMPACT"))
+ {
+ assertInvalidMessage("Invalid empty or null value for column c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
+ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND c < textAsBlob('');"));
+ });
+ }
+
+ // Test restrictions on non-primary key value
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;"));
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER);
+
+ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER));
+ });
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumns() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
+ beforeAndAfterFlush(() -> {
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob('1'), textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob('1'), textAsBlob(''));"));
+ });
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
+ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) < (textAsBlob(''), textAsBlob('1'));"));
+ });
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("1"),
+ bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("2"),
+ bytes("2"));
+
+ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause));
+
+ });
+
+ if (options.contains("COMPACT"))
+ {
+ assertInvalidMessage("Invalid empty or null value for column c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ EMPTY_BYTE_BUFFER,
+ bytes("4"));
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
+ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+ });
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumnsAndOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c1 DESC, c2 DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c1 DESC, c2 DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c1 DESC, c2 DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
+ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+ });
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
+ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+ });
+ }
+ }
}
[30/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0'
into cassandra-3.9
Posted by ms...@apache.org.
Merge branch 'cassandra-3.0' into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/d597910b
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/d597910b
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/d597910b
Branch: refs/heads/cassandra-3.8
Commit: d597910b2e5438962eefb5217887e844e011063a
Parents: d052c90 5fdb90e
Author: Tyler Hobbs <ty...@gmail.com>
Authored: Fri Aug 12 18:34:44 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Fri Aug 12 18:34:44 2016 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
NEWS.txt | 2 ++
.../cassandra/config/DatabaseDescriptor.java | 6 ++--
.../cassandra/locator/Ec2MultiRegionSnitch.java | 6 +++-
.../cassandra/service/StorageService.java | 4 +--
.../org/apache/cassandra/transport/Server.java | 2 +-
.../org/apache/cassandra/utils/FBUtilities.java | 19 ++++++++++++
.../apache/cassandra/utils/FBUtilitiesTest.java | 32 ++++++++++++++++++++
8 files changed, 66 insertions(+), 6 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d597910b/CHANGES.txt
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d597910b/NEWS.txt
----------------------------------------------------------------------
diff --cc NEWS.txt
index d8d84f5,6b8dfd3..249077d
--- a/NEWS.txt
+++ b/NEWS.txt
@@@ -45,12 -26,11 +45,14 @@@ New feature
Upgrading
---------
- - A maximum size for SSTables values has been introduced, to prevent out of memory
- exceptions when reading corrupt SSTables. This maximum size can be set via
- max_value_size_in_mb in cassandra.yaml. The default is 256MB, which matches the default
- value of native_transport_max_frame_size_in_mb. SSTables will be considered corrupt if
- they contain values whose size exceeds this limit. See CASSANDRA-9530 for more details.
++ - Ec2MultiRegionSnitch will no longer automatically set broadcast_rpc_address
++ to the public instance IP if this property is defined on cassandra.yaml.
+ - The name "json" and "distinct" are not valid anymore a user-defined function
+ names (they are still valid as column name however). In the unlikely case where
+ you had defined functions with such names, you will need to recreate
+ those under a different name, change your code to use the new names and
+ drop the old versions, and this _before_ upgrade (see CASSANDRA-10783 for more
+ details).
Deprecation
-----------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d597910b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d597910b/src/java/org/apache/cassandra/service/StorageService.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/service/StorageService.java
index 7500593,0c91784..bc69d93
--- a/src/java/org/apache/cassandra/service/StorageService.java
+++ b/src/java/org/apache/cassandra/service/StorageService.java
@@@ -785,14 -740,9 +785,14 @@@ public class StorageService extends Not
getTokenMetadata().updateHostId(localHostId, FBUtilities.getBroadcastAddress());
appStates.put(ApplicationState.NET_VERSION, valueFactory.networkVersion());
appStates.put(ApplicationState.HOST_ID, valueFactory.hostId(localHostId));
- appStates.put(ApplicationState.RPC_ADDRESS, valueFactory.rpcaddress(DatabaseDescriptor.getBroadcastRpcAddress()));
+ appStates.put(ApplicationState.RPC_ADDRESS, valueFactory.rpcaddress(FBUtilities.getBroadcastRpcAddress()));
appStates.put(ApplicationState.RELEASE_VERSION, valueFactory.releaseVersion());
+
+ // load the persisted ring state. This used to be done earlier in the init process,
+ // but now we always perform a shadow round when preparing to join and we have to
+ // clear endpoint states after doing that.
+ loadRingState();
+
logger.info("Starting up server gossip");
Gossiper.instance.register(this);
Gossiper.instance.start(SystemKeyspace.incrementAndGetGeneration(), appStates); // needed for node-ring gathering.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d597910b/src/java/org/apache/cassandra/transport/Server.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d597910b/src/java/org/apache/cassandra/utils/FBUtilities.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/utils/FBUtilities.java
index af2cb1b,ca2775f..c76d8e1
--- a/src/java/org/apache/cassandra/utils/FBUtilities.java
+++ b/src/java/org/apache/cassandra/utils/FBUtilities.java
@@@ -892,21 -872,12 +903,29 @@@ public class FBUtilitie
throw new RuntimeException(e);
}
}
+
+ public static void sleepQuietly(long millis)
+ {
+ try
+ {
+ Thread.sleep(millis);
+ }
+ catch (InterruptedException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static long align(long val, int boundary)
+ {
+ return (val + boundary) & ~(boundary - 1);
+ }
+
+ @VisibleForTesting
+ protected static void reset()
+ {
+ localInetAddress = null;
+ broadcastInetAddress = null;
+ broadcastRpcAddress = null;
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d597910b/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
index 3c1ea74,acd68eb..b8d2633
--- a/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
+++ b/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
@@@ -22,18 -23,16 +23,21 @@@ import java.net.InetAddress
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.Optional;
+import java.util.TreeMap;
import com.google.common.primitives.Ints;
+
+import org.junit.Assert;
import org.junit.Test;
-import java.util.Map;
-import java.util.TreeMap;
+import org.apache.cassandra.db.marshal.*;
+import org.apache.cassandra.dht.*;
+ import org.apache.cassandra.config.Config;
+ import org.apache.cassandra.config.DatabaseDescriptor;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@@ -101,50 -100,31 +105,78 @@@ public class FBUtilitiesTes
ByteBufferUtil.string(bytes, StandardCharsets.UTF_8);
}
+ private static void assertPartitioner(String name, Class expected)
+ {
+ Assert.assertTrue(String.format("%s != %s", name, expected.toString()),
+ expected.isInstance(FBUtilities.newPartitioner(name)));
+ }
+
+ /**
+ * Check that given a name, the correct partitioner instance is created.
+ *
+ * If the assertions in this test start failing, it likely means the sstabledump/sstablemetadata tools will
+ * also fail to read existing sstables.
+ */
+ @Test
+ public void testNewPartitionerNoArgConstructors()
+ {
+ assertPartitioner("ByteOrderedPartitioner", ByteOrderedPartitioner.class);
+ assertPartitioner("LengthPartitioner", LengthPartitioner.class);
+ assertPartitioner("Murmur3Partitioner", Murmur3Partitioner.class);
+ assertPartitioner("OrderPreservingPartitioner", OrderPreservingPartitioner.class);
+ assertPartitioner("RandomPartitioner", RandomPartitioner.class);
+ assertPartitioner("org.apache.cassandra.dht.ByteOrderedPartitioner", ByteOrderedPartitioner.class);
+ assertPartitioner("org.apache.cassandra.dht.LengthPartitioner", LengthPartitioner.class);
+ assertPartitioner("org.apache.cassandra.dht.Murmur3Partitioner", Murmur3Partitioner.class);
+ assertPartitioner("org.apache.cassandra.dht.OrderPreservingPartitioner", OrderPreservingPartitioner.class);
+ assertPartitioner("org.apache.cassandra.dht.RandomPartitioner", RandomPartitioner.class);
+ }
+
+ /**
+ * Check that we can instantiate local partitioner correctly and that we can pass the correct type
+ * to it as a constructor argument.
+ *
+ * If the assertions in this test start failing, it likely means the sstabledump/sstablemetadata tools will
+ * also fail to read existing sstables.
+ */
+ @Test
+ public void testNewPartitionerLocalPartitioner()
+ {
+ for (String name : new String[] {"LocalPartitioner", "org.apache.cassandra.dht.LocalPartitioner"})
+ for (AbstractType<?> type : new AbstractType<?>[] {UUIDType.instance, ListType.getInstance(Int32Type.instance, true)})
+ {
+ IPartitioner partitioner = FBUtilities.newPartitioner(name, Optional.of(type));
+ Assert.assertTrue(String.format("%s != LocalPartitioner", partitioner.toString()),
+ LocalPartitioner.class.isInstance(partitioner));
+ Assert.assertEquals(partitioner.partitionOrdering(), type);
+ }
+ }
++
+ @Test
+ public void testGetBroadcastRpcAddress() throws Exception
+ {
+ //When both rpc_address and broadcast_rpc_address are null, it should return the local address (from DD.applyAddressConfig)
+ FBUtilities.reset();
+ Config testConfig = DatabaseDescriptor.loadConfig();
+ testConfig.rpc_address = null;
+ testConfig.broadcast_rpc_address = null;
+ DatabaseDescriptor.applyAddressConfig(testConfig);
+ assertEquals(FBUtilities.getLocalAddress(), FBUtilities.getBroadcastRpcAddress());
+
+ //When rpc_address is defined and broadcast_rpc_address is null, it should return the rpc_address
+ FBUtilities.reset();
+ testConfig.rpc_address = "127.0.0.2";
+ testConfig.broadcast_rpc_address = null;
+ DatabaseDescriptor.applyAddressConfig(testConfig);
+ assertEquals(InetAddress.getByName("127.0.0.2"), FBUtilities.getBroadcastRpcAddress());
+
+ //When both rpc_address and broadcast_rpc_address are defined, it should return broadcast_rpc_address
+ FBUtilities.reset();
+ testConfig.rpc_address = "127.0.0.2";
+ testConfig.broadcast_rpc_address = "127.0.0.3";
+ DatabaseDescriptor.applyAddressConfig(testConfig);
+ assertEquals(InetAddress.getByName("127.0.0.3"), FBUtilities.getBroadcastRpcAddress());
+
+ FBUtilities.reset();
+ }
}
[44/50] [abbrv] cassandra git commit: Merge branch cassandra-2.1 into
cassandra-2.2
Posted by ms...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
index 735a2e2,0000000..1f4960b
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
@@@ -1,626 -1,0 +1,669 @@@
+/*
+ * 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.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+import com.google.common.collect.Iterables;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.Term.Terminal;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.composites.CompositesBuilder;
+import org.apache.cassandra.db.index.SecondaryIndex;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
+import org.apache.cassandra.db.marshal.CompositeType;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkBindValueSet;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
+import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
+
+public abstract class SingleColumnRestriction extends AbstractRestriction
+{
+ /**
+ * The definition of the column to which apply the restriction.
+ */
+ protected final ColumnDefinition columnDef;
+
+ public SingleColumnRestriction(ColumnDefinition columnDef)
+ {
+ this.columnDef = columnDef;
+ }
+
+ @Override
+ public List<ColumnDefinition> getColumnDefs()
+ {
+ return Collections.singletonList(columnDef);
+ }
+
+ @Override
+ public ColumnDefinition getFirstColumn()
+ {
+ return columnDef;
+ }
+
+ @Override
+ public ColumnDefinition getLastColumn()
+ {
+ return columnDef;
+ }
+
+ @Override
+ public boolean hasSupportingIndex(SecondaryIndexManager indexManager)
+ {
+ SecondaryIndex index = indexManager.getIndexForColumn(columnDef.name.bytes);
+ return index != null && isSupportedBy(index);
+ }
+
+ @Override
+ public final Restriction mergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ // We want to allow query like: b > ? AND (b,c) < (?, ?)
+ if (otherRestriction.isMultiColumn() && canBeConvertedToMultiColumnRestriction())
+ {
+ return toMultiColumnRestriction().mergeWith(otherRestriction);
+ }
+
+ return doMergeWith(otherRestriction);
+ }
+
+ protected abstract Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException;
+
+ /**
+ * Converts this <code>SingleColumnRestriction</code> into a {@link MultiColumnRestriction}
+ *
+ * @return the <code>MultiColumnRestriction</code> corresponding to this
+ */
+ abstract MultiColumnRestriction toMultiColumnRestriction();
+
+ /**
+ * Checks if this <code>Restriction</code> can be converted into a {@link MultiColumnRestriction}
+ *
+ * @return <code>true</code> if this <code>Restriction</code> can be converted into a
+ * {@link MultiColumnRestriction}, <code>false</code> otherwise.
+ */
+ boolean canBeConvertedToMultiColumnRestriction()
+ {
+ return true;
+ }
+
+ /**
+ * Check if this type of restriction is supported by the specified index.
+ *
+ * @param index the Secondary index
+ * @return <code>true</code> this type of restriction is supported by the specified index,
+ * <code>false</code> otherwise.
+ */
+ protected abstract boolean isSupportedBy(SecondaryIndex index);
+
+ public static final class EQ extends SingleColumnRestriction
+ {
+ private final Term value;
+
+ public EQ(ColumnDefinition columnDef, Term value)
+ {
+ super(columnDef);
+ this.value = value;
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return value.getFunctions();
+ }
+
+ @Override
+ public boolean isEQ()
+ {
+ return true;
+ }
+
+ @Override
+ MultiColumnRestriction toMultiColumnRestriction()
+ {
+ return new MultiColumnRestriction.EQ(Collections.singletonList(columnDef), value);
+ }
+
+ @Override
+ public void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ ByteBuffer buffer = validateIndexedValue(columnDef, value.bindAndGet(options));
+ expressions.add(new IndexExpression(columnDef.name.bytes, Operator.EQ, buffer));
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
+ builder.addElementToAll(value.bindAndGet(options));
+ checkFalse(builder.containsNull(), "Invalid null value in condition for column %s", columnDef.name);
+ checkFalse(builder.containsUnset(), "Invalid unset value for column %s", columnDef.name);
+ return builder;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("EQ(%s)", value);
+ }
+
+ @Override
+ public Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ throw invalidRequest("%s cannot be restricted by more than one relation if it includes an Equal", columnDef.name);
+ }
+
+ @Override
+ protected boolean isSupportedBy(SecondaryIndex index)
+ {
+ return index.supportsOperator(Operator.EQ);
+ }
++
++ @Override
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ assert columnDef.isClusteringColumn();
++
++ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that
++ // any query with an EQ restriction containing an empty value will not return any results.
++ return !cfm.comparator.isCompound() && !value.bindAndGet(options).hasRemaining();
++ }
+ }
+
+ public static abstract class IN extends SingleColumnRestriction
+ {
+ public IN(ColumnDefinition columnDef)
+ {
+ super(columnDef);
+ }
+
+ @Override
+ public final boolean isIN()
+ {
+ return true;
+ }
+
+ @Override
+ public final Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ throw invalidRequest("%s cannot be restricted by more than one relation if it includes a IN", columnDef.name);
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
- builder.addEachElementToAll(getValues(options));
++ List<ByteBuffer> values = filterValuesIfNeeded(cfm, getValues(options));
++
++ builder.addEachElementToAll(values);
+ checkFalse(builder.containsNull(), "Invalid null value in condition for column %s", columnDef.name);
+ checkFalse(builder.containsUnset(), "Invalid unset value for column %s", columnDef.name);
+ return builder;
+ }
+
++ private List<ByteBuffer> filterValuesIfNeeded(CFMetaData cfm, List<ByteBuffer> values)
++ {
++ if (!columnDef.isClusteringColumn() || cfm.comparator.isCompound())
++ return values;
++
++ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that we can
++ // ignore any IN value which is an empty byte buffer an which otherwise will trigger an error.
++
++ // As some List implementations do not support remove, we copy the list to be on the safe side.
++ List<ByteBuffer> filteredValues = new ArrayList<>(values.size());
++ for (ByteBuffer value : values)
++ {
++ if (value.hasRemaining())
++ filteredValues.add(value);
++ }
++ return filteredValues;
++ }
++
+ @Override
+ public void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ List<ByteBuffer> values = getValues(options);
+ checkTrue(values.size() == 1, "IN restrictions are not supported on indexed columns");
+
+ ByteBuffer value = validateIndexedValue(columnDef, values.get(0));
+ expressions.add(new IndexExpression(columnDef.name.bytes, Operator.EQ, value));
+ }
+
+ @Override
+ protected final boolean isSupportedBy(SecondaryIndex index)
+ {
+ return index.supportsOperator(Operator.IN);
+ }
+
+ protected abstract List<ByteBuffer> getValues(QueryOptions options) throws InvalidRequestException;
+ }
+
+ public static class InWithValues extends IN
+ {
+ protected final List<Term> values;
+
+ public InWithValues(ColumnDefinition columnDef, List<Term> values)
+ {
+ super(columnDef);
+ this.values = values;
+ }
+
+ @Override
+ MultiColumnRestriction toMultiColumnRestriction()
+ {
+ return new MultiColumnRestriction.InWithValues(Collections.singletonList(columnDef), values);
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return Terms.getFunctions(values);
+ }
+
+ @Override
+ protected List<ByteBuffer> getValues(QueryOptions options) throws InvalidRequestException
+ {
+ List<ByteBuffer> buffers = new ArrayList<>(values.size());
+ for (Term value : values)
+ buffers.add(value.bindAndGet(options));
+ return buffers;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("IN(%s)", values);
+ }
+ }
+
+ public static class InWithMarker extends IN
+ {
+ protected final AbstractMarker marker;
+
+ public InWithMarker(ColumnDefinition columnDef, AbstractMarker marker)
+ {
+ super(columnDef);
+ this.marker = marker;
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return Collections.emptySet();
+ }
+
+ @Override
+ MultiColumnRestriction toMultiColumnRestriction()
+ {
+ return new MultiColumnRestriction.InWithMarker(Collections.singletonList(columnDef), marker);
+ }
+
+ @Override
+ protected List<ByteBuffer> getValues(QueryOptions options) throws InvalidRequestException
+ {
+ Terminal term = marker.bind(options);
+ checkNotNull(term, "Invalid null value for column %s", columnDef.name);
+ checkFalse(term == Constants.UNSET_VALUE, "Invalid unset value for column %s", columnDef.name);
+ Term.MultiItemTerminal lval = (Term.MultiItemTerminal) term;
+ return lval.getElements();
+ }
+
+ @Override
+ public String toString()
+ {
+ return "IN ?";
+ }
+ }
+
+ public static final class Slice extends SingleColumnRestriction
+ {
+ private final TermSlice slice;
+
+ public Slice(ColumnDefinition columnDef, Bound bound, boolean inclusive, Term term)
+ {
+ super(columnDef);
+ slice = TermSlice.newInstance(bound, inclusive, term);
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return slice.getFunctions();
+ }
+
+ @Override
+ MultiColumnRestriction toMultiColumnRestriction()
+ {
+ return new MultiColumnRestriction.Slice(Collections.singletonList(columnDef), slice);
+ }
+
+ @Override
+ public boolean isSlice()
+ {
+ return true;
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasBound(Bound b)
+ {
+ return slice.hasBound(b);
+ }
+
+ @Override
- public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
++ public CompositesBuilder appendBoundTo(CFMetaData cfm, CompositesBuilder builder, Bound bound, QueryOptions options)
+ {
+ Bound b = reverseBoundIfNeeded(getFirstColumn(), bound);
+
+ if (!hasBound(b))
+ return builder;
+
+ ByteBuffer value = slice.bound(b).bindAndGet(options);
+ checkBindValueSet(value, "Invalid unset value for column %s", columnDef.name);
+ return builder.addElementToAll(value);
+
+ }
+
+ @Override
+ public boolean isInclusive(Bound b)
+ {
+ return slice.isInclusive(b);
+ }
+
+ @Override
+ public Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ checkTrue(otherRestriction.isSlice(),
+ "Column \"%s\" cannot be restricted by both an equality and an inequality relation",
+ columnDef.name);
+
+ SingleColumnRestriction.Slice otherSlice = (SingleColumnRestriction.Slice) otherRestriction;
+
+ checkFalse(hasBound(Bound.START) && otherSlice.hasBound(Bound.START),
+ "More than one restriction was found for the start bound on %s", columnDef.name);
+
+ checkFalse(hasBound(Bound.END) && otherSlice.hasBound(Bound.END),
+ "More than one restriction was found for the end bound on %s", columnDef.name);
+
+ return new Slice(columnDef, slice.merge(otherSlice.slice));
+ }
+
+ @Override
+ public void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ for (Bound b : Bound.values())
+ {
+ if (hasBound(b))
+ {
+ ByteBuffer value = validateIndexedValue(columnDef, slice.bound(b).bindAndGet(options));
+ Operator op = slice.getIndexOperator(b);
+ // If the underlying comparator for name is reversed, we need to reverse the IndexOperator: user operation
+ // always refer to the "forward" sorting even if the clustering order is reversed, but the 2ndary code does
+ // use the underlying comparator as is.
+ op = columnDef.isReversedType() ? op.reverse() : op;
+ expressions.add(new IndexExpression(columnDef.name.bytes, op, value));
+ }
+ }
+ }
+
+ @Override
+ protected boolean isSupportedBy(SecondaryIndex index)
+ {
+ return slice.isSupportedBy(index);
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("SLICE%s", slice);
+ }
+
++ @Override
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ assert columnDef.isClusteringColumn();
++
++ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that
++ // any query with a slice restriction with an empty value for the END bound will not return any results.
++ return !cfm.comparator.isCompound()
++ && hasBound(Bound.END)
++ && !slice.bound(Bound.END).bindAndGet(options).hasRemaining();
++ }
++
+ private Slice(ColumnDefinition columnDef, TermSlice slice)
+ {
+ super(columnDef);
+ this.slice = slice;
+ }
+ }
+
+ // This holds CONTAINS, CONTAINS_KEY, and map[key] = value restrictions because we might want to have any combination of them.
+ public static final class Contains extends SingleColumnRestriction
+ {
+ private List<Term> values = new ArrayList<>(); // for CONTAINS
+ private List<Term> keys = new ArrayList<>(); // for CONTAINS_KEY
+ private List<Term> entryKeys = new ArrayList<>(); // for map[key] = value
+ private List<Term> entryValues = new ArrayList<>(); // for map[key] = value
+
+ public Contains(ColumnDefinition columnDef, Term t, boolean isKey)
+ {
+ super(columnDef);
+ if (isKey)
+ keys.add(t);
+ else
+ values.add(t);
+ }
+
+ public Contains(ColumnDefinition columnDef, Term mapKey, Term mapValue)
+ {
+ super(columnDef);
+ entryKeys.add(mapKey);
+ entryValues.add(mapValue);
+ }
+
+ @Override
+ MultiColumnRestriction toMultiColumnRestriction()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ boolean canBeConvertedToMultiColumnRestriction()
+ {
+ return false;
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isContains()
+ {
+ return true;
+ }
+
+ @Override
+ public Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ checkTrue(otherRestriction.isContains(),
+ "Collection column %s can only be restricted by CONTAINS, CONTAINS KEY, or map-entry equality",
+ columnDef.name);
+
+ SingleColumnRestriction.Contains newContains = new Contains(columnDef);
+
+ copyKeysAndValues(this, newContains);
+ copyKeysAndValues((Contains) otherRestriction, newContains);
+
+ return newContains;
+ }
+
+ @Override
+ public void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options)
+ throws InvalidRequestException
+ {
+ addExpressionsFor(expressions, bindAndGet(values, options), Operator.CONTAINS);
+ addExpressionsFor(expressions, bindAndGet(keys, options), Operator.CONTAINS_KEY);
+ addExpressionsFor(expressions, entries(options), Operator.EQ);
+ }
+
+ private void addExpressionsFor(List<IndexExpression> target, List<ByteBuffer> values,
+ Operator op) throws InvalidRequestException
+ {
+ for (ByteBuffer value : values)
+ {
+ validateIndexedValue(columnDef, value);
+ target.add(new IndexExpression(columnDef.name.bytes, op, value));
+ }
+ }
+
+ @Override
+ protected boolean isSupportedBy(SecondaryIndex index)
+ {
+ boolean supported = false;
+
+ if (numberOfValues() > 0)
+ supported |= index.supportsOperator(Operator.CONTAINS);
+
+ if (numberOfKeys() > 0)
+ supported |= index.supportsOperator(Operator.CONTAINS_KEY);
+
+ if (numberOfEntries() > 0)
+ supported |= index.supportsOperator(Operator.EQ);
+
+ return supported;
+ }
+
+ public int numberOfValues()
+ {
+ return values.size();
+ }
+
+ public int numberOfKeys()
+ {
+ return keys.size();
+ }
+
+ public int numberOfEntries()
+ {
+ return entryKeys.size();
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return Iterables.concat(Terms.getFunctions(values),
+ Terms.getFunctions(keys),
+ Terms.getFunctions(entryKeys),
+ Terms.getFunctions(entryValues));
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("CONTAINS(values=%s, keys=%s, entryKeys=%s, entryValues=%s)", values, keys, entryKeys, entryValues);
+ }
+
+ @Override
+ public boolean hasBound(Bound b)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
- public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
++ public CompositesBuilder appendBoundTo(CFMetaData cfm, CompositesBuilder builder, Bound bound, QueryOptions options)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isInclusive(Bound b)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ private List<ByteBuffer> entries(QueryOptions options) throws InvalidRequestException
+ {
+ List<ByteBuffer> entryBuffers = new ArrayList<>(entryKeys.size());
+ List<ByteBuffer> keyBuffers = bindAndGet(entryKeys, options);
+ List<ByteBuffer> valueBuffers = bindAndGet(entryValues, options);
+ for (int i = 0; i < entryKeys.size(); i++)
+ {
+ if (valueBuffers.get(i) == null)
+ throw new InvalidRequestException("Unsupported null value for map-entry equality");
+ entryBuffers.add(CompositeType.build(keyBuffers.get(i), valueBuffers.get(i)));
+ }
+ return entryBuffers;
+ }
+
+ /**
+ * Binds the query options to the specified terms and returns the resulting values.
+ *
+ * @param terms the terms
+ * @param options the query options
+ * @return the value resulting from binding the query options to the specified terms
+ * @throws InvalidRequestException if a problem occurs while binding the query options
+ */
+ private static List<ByteBuffer> bindAndGet(List<Term> terms, QueryOptions options) throws InvalidRequestException
+ {
+ List<ByteBuffer> buffers = new ArrayList<>(terms.size());
+ for (Term value : terms)
+ buffers.add(value.bindAndGet(options));
+ return buffers;
+ }
+
+ /**
+ * Copies the keys and value from the first <code>Contains</code> to the second one.
+ *
+ * @param from the <code>Contains</code> to copy from
+ * @param to the <code>Contains</code> to copy to
+ */
+ private static void copyKeysAndValues(Contains from, Contains to)
+ {
+ to.values.addAll(from.values);
+ to.keys.addAll(from.keys);
+ to.entryKeys.addAll(from.entryKeys);
+ to.entryValues.addAll(from.entryValues);
+ }
+
+ private Contains(ColumnDefinition columnDef)
+ {
+ super(columnDef);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
index 1547210,0000000..8035877
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
@@@ -1,645 -1,0 +1,656 @@@
+/*
+ * 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.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
+import org.apache.cassandra.dht.*;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.service.StorageService;
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+import static org.apache.cassandra.config.ColumnDefinition.toIdentifiers;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
+import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
+
+/**
+ * The restrictions corresponding to the relations specified on the where-clause of CQL query.
+ */
+public final class StatementRestrictions
+{
+ public static final String REQUIRES_ALLOW_FILTERING_MESSAGE =
+ "Cannot execute this query as it might involve data filtering and " +
+ "thus may have unpredictable performance. If you want to execute " +
+ "this query despite the performance unpredictability, use ALLOW FILTERING";
+
+ /**
+ * The Column Family meta data
+ */
+ public final CFMetaData cfm;
+
+ /**
+ * Restrictions on partitioning columns
+ */
+ private PrimaryKeyRestrictions partitionKeyRestrictions;
+
+ /**
+ * Restrictions on clustering columns
+ */
+ private PrimaryKeyRestrictions clusteringColumnsRestrictions;
+
+ /**
+ * Restriction on non-primary key columns (i.e. secondary index restrictions)
+ */
+ private RestrictionSet nonPrimaryKeyRestrictions;
+
+ /**
+ * The restrictions used to build the index expressions
+ */
+ private final List<Restrictions> indexRestrictions = new ArrayList<>();
+
+ /**
+ * <code>true</code> if the secondary index need to be queried, <code>false</code> otherwise
+ */
+ private boolean usesSecondaryIndexing;
+
+ /**
+ * Specify if the query will return a range of partition keys.
+ */
+ private boolean isKeyRange;
+
+ /**
+ * Creates a new empty <code>StatementRestrictions</code>.
+ *
+ * @param cfm the column family meta data
+ * @return a new empty <code>StatementRestrictions</code>.
+ */
+ public static StatementRestrictions empty(CFMetaData cfm)
+ {
+ return new StatementRestrictions(cfm);
+ }
+
+ private StatementRestrictions(CFMetaData cfm)
+ {
+ this.cfm = cfm;
+ this.partitionKeyRestrictions = new PrimaryKeyRestrictionSet(cfm.getKeyValidatorAsCType());
+ this.clusteringColumnsRestrictions = new PrimaryKeyRestrictionSet(cfm.comparator);
+ this.nonPrimaryKeyRestrictions = new RestrictionSet();
+ }
+
+ public StatementRestrictions(CFMetaData cfm,
+ List<Relation> whereClause,
+ VariableSpecifications boundNames,
+ boolean selectsOnlyStaticColumns,
+ boolean selectACollection,
+ boolean allowFiltering)
+ {
+ this.cfm = cfm;
+ this.partitionKeyRestrictions = new PrimaryKeyRestrictionSet(cfm.getKeyValidatorAsCType());
+ this.clusteringColumnsRestrictions = new PrimaryKeyRestrictionSet(cfm.comparator);
+ this.nonPrimaryKeyRestrictions = new RestrictionSet();
+
+ /*
+ * WHERE clause. For a given entity, rules are: - EQ relation conflicts with anything else (including a 2nd EQ)
+ * - Can't have more than one LT(E) relation (resp. GT(E) relation) - IN relation are restricted to row keys
+ * (for now) and conflicts with anything else (we could allow two IN for the same entity but that doesn't seem
+ * very useful) - The value_alias cannot be restricted in any way (we don't support wide rows with indexed value
+ * in CQL so far)
+ */
+ for (Relation relation : whereClause)
+ addRestriction(relation.toRestriction(cfm, boundNames));
+
+ SecondaryIndexManager secondaryIndexManager = Keyspace.open(cfm.ksName).getColumnFamilyStore(cfm.cfName).indexManager;
+ boolean hasQueriableClusteringColumnIndex = clusteringColumnsRestrictions.hasSupportingIndex(secondaryIndexManager);
+ boolean hasQueriableIndex = hasQueriableClusteringColumnIndex
+ || partitionKeyRestrictions.hasSupportingIndex(secondaryIndexManager)
+ || nonPrimaryKeyRestrictions.hasSupportingIndex(secondaryIndexManager);
+
+ // At this point, the select statement if fully constructed, but we still have a few things to validate
+ processPartitionKeyRestrictions(hasQueriableIndex);
+
+ // Some but not all of the partition key columns have been specified;
+ // hence we need turn these restrictions into index expressions.
+ if (usesSecondaryIndexing)
+ indexRestrictions.add(partitionKeyRestrictions);
+
+ checkFalse(selectsOnlyStaticColumns && hasClusteringColumnsRestriction(),
+ "Cannot restrict clustering columns when selecting only static columns");
+
+ processClusteringColumnsRestrictions(hasQueriableIndex, selectACollection);
+
+ // Covers indexes on the first clustering column (among others).
+ if (isKeyRange && hasQueriableClusteringColumnIndex)
+ usesSecondaryIndexing = true;
+
+ usesSecondaryIndexing = usesSecondaryIndexing || clusteringColumnsRestrictions.isContains();
+
+ if (usesSecondaryIndexing)
+ indexRestrictions.add(clusteringColumnsRestrictions);
+
+ // Even if usesSecondaryIndexing is false at this point, we'll still have to use one if
+ // there is restrictions not covered by the PK.
+ if (!nonPrimaryKeyRestrictions.isEmpty())
+ {
+ if (!hasQueriableIndex)
+ {
+ // Filtering for non-index query is only supported for thrift static CFs
+ if (cfm.comparator.isDense() || cfm.comparator.isCompound())
+ throw invalidRequest("Predicates on non-primary-key columns (%s) are not yet supported for non secondary index queries",
+ Joiner.on(", ").join(toIdentifiers(nonPrimaryKeyRestrictions.getColumnDefs())));
+
+ if (!allowFiltering)
+ throw invalidRequest(REQUIRES_ALLOW_FILTERING_MESSAGE);
+ }
+ usesSecondaryIndexing = true;
+ indexRestrictions.add(nonPrimaryKeyRestrictions);
+ }
+
+ if (usesSecondaryIndexing)
+ validateSecondaryIndexSelections(selectsOnlyStaticColumns);
+ }
+
+ private void addRestriction(Restriction restriction) throws InvalidRequestException
+ {
+ if (restriction.isMultiColumn())
+ clusteringColumnsRestrictions = clusteringColumnsRestrictions.mergeWith(restriction);
+ else if (restriction.isOnToken())
+ partitionKeyRestrictions = partitionKeyRestrictions.mergeWith(restriction);
+ else
+ addSingleColumnRestriction((SingleColumnRestriction) restriction);
+ }
+
+ public Iterable<Function> getFunctions()
+ {
+ return Iterables.concat(partitionKeyRestrictions.getFunctions(),
+ clusteringColumnsRestrictions.getFunctions(),
+ nonPrimaryKeyRestrictions.getFunctions());
+ }
+
+ private void addSingleColumnRestriction(SingleColumnRestriction restriction) throws InvalidRequestException
+ {
+ ColumnDefinition def = restriction.columnDef;
+ if (def.isPartitionKey())
+ partitionKeyRestrictions = partitionKeyRestrictions.mergeWith(restriction);
+ else if (def.isClusteringColumn())
+ clusteringColumnsRestrictions = clusteringColumnsRestrictions.mergeWith(restriction);
+ else
+ nonPrimaryKeyRestrictions = nonPrimaryKeyRestrictions.addRestriction(restriction);
+ }
+
+ /**
+ * Checks if the restrictions on the partition key is an IN restriction.
+ *
+ * @return <code>true</code> the restrictions on the partition key is an IN restriction, <code>false</code>
+ * otherwise.
+ */
+ public boolean keyIsInRelation()
+ {
+ return partitionKeyRestrictions.isIN();
+ }
+
+ /**
+ * Checks if the query request a range of partition keys.
+ *
+ * @return <code>true</code> if the query request a range of partition keys, <code>false</code> otherwise.
+ */
+ public boolean isKeyRange()
+ {
+ return this.isKeyRange;
+ }
+
+ /**
+ * Checks if the secondary index need to be queried.
+ *
+ * @return <code>true</code> if the secondary index need to be queried, <code>false</code> otherwise.
+ */
+ public boolean usesSecondaryIndexing()
+ {
+ return this.usesSecondaryIndexing;
+ }
+
+ private void processPartitionKeyRestrictions(boolean hasQueriableIndex) throws InvalidRequestException
+ {
+ // If there is a queriable index, no special condition are required on the other restrictions.
+ // But we still need to know 2 things:
+ // - If we don't have a queriable index, is the query ok
+ // - Is it queriable without 2ndary index, which is always more efficient
+ // If a component of the partition key is restricted by a relation, all preceding
+ // components must have a EQ. Only the last partition key component can be in IN relation.
+ if (partitionKeyRestrictions.isOnToken())
+ isKeyRange = true;
+
+ if (hasPartitionKeyUnrestrictedComponents())
+ {
+ if (!partitionKeyRestrictions.isEmpty())
+ {
+ if (!hasQueriableIndex)
+ throw invalidRequest("Partition key parts: %s must be restricted as other parts are",
+ Joiner.on(", ").join(getPartitionKeyUnrestrictedComponents()));
+ }
+
+ isKeyRange = true;
+ usesSecondaryIndexing = hasQueriableIndex;
+ }
+ }
+
+ /**
+ * Checks if the partition key has some unrestricted components.
+ * @return <code>true</code> if the partition key has some unrestricted components, <code>false</code> otherwise.
+ */
+ private boolean hasPartitionKeyUnrestrictedComponents()
+ {
+ return partitionKeyRestrictions.size() < cfm.partitionKeyColumns().size();
+ }
+
+ public boolean hasPartitionKeyRestrictions()
+ {
+ return !partitionKeyRestrictions.isEmpty();
+ }
+
+ /**
+ * Checks if the restrictions contain any non-primary key restrictions
+ * @return <code>true</code> if the restrictions contain any non-primary key restrictions, <code>false</code> otherwise.
+ */
+ public boolean hasNonPrimaryKeyRestrictions()
+ {
+ return !nonPrimaryKeyRestrictions.isEmpty();
+ }
+
+ /**
+ * Returns the partition key components that are not restricted.
+ * @return the partition key components that are not restricted.
+ */
+ private List<ColumnIdentifier> getPartitionKeyUnrestrictedComponents()
+ {
+ List<ColumnDefinition> list = new ArrayList<>(cfm.partitionKeyColumns());
+ list.removeAll(partitionKeyRestrictions.getColumnDefs());
+ return ColumnDefinition.toIdentifiers(list);
+ }
+
+ /**
+ * Processes the clustering column restrictions.
+ *
+ * @param hasQueriableIndex <code>true</code> if some of the queried data are indexed, <code>false</code> otherwise
+ * @param selectACollection <code>true</code> if the query should return a collection column
+ * @throws InvalidRequestException if the request is invalid
+ */
+ private void processClusteringColumnsRestrictions(boolean hasQueriableIndex,
+ boolean selectACollection) throws InvalidRequestException
+ {
+ validateClusteringRestrictions(hasQueriableIndex);
+
+ checkFalse(clusteringColumnsRestrictions.isIN() && selectACollection,
+ "Cannot restrict clustering columns by IN relations when a collection is selected by the query");
+ checkFalse(clusteringColumnsRestrictions.isContains() && !hasQueriableIndex,
+ "Cannot restrict clustering columns by a CONTAINS relation without a secondary index");
+
+ if (hasClusteringColumnsRestriction() && clusteringRestrictionsNeedFiltering())
+ {
+ if (hasQueriableIndex)
+ {
+ usesSecondaryIndexing = true;
+ return;
+ }
+
+ List<ColumnDefinition> clusteringColumns = cfm.clusteringColumns();
+ List<ColumnDefinition> restrictedColumns = new LinkedList<>(clusteringColumnsRestrictions.getColumnDefs());
+
+ for (int i = 0, m = restrictedColumns.size(); i < m; i++)
+ {
+ ColumnDefinition clusteringColumn = clusteringColumns.get(i);
+ ColumnDefinition restrictedColumn = restrictedColumns.get(i);
+
+ if (!clusteringColumn.equals(restrictedColumn))
+ {
+ throw invalidRequest(
+ "PRIMARY KEY column \"%s\" cannot be restricted as preceding column \"%s\" is not restricted",
+ restrictedColumn.name,
+ clusteringColumn.name);
+ }
+ }
+ }
+ }
+
+ /**
+ * Validates whether or not restrictions are allowed for execution when secondary index is not used.
+ */
+ public final void validateClusteringRestrictions(boolean hasQueriableIndex)
+ {
+ assert clusteringColumnsRestrictions instanceof PrimaryKeyRestrictionSet;
+
+ // If there's a queriable index, filtering will take care of clustering restrictions
+ if (hasQueriableIndex)
+ return;
+
+ Iterator<Restriction> iter = ((PrimaryKeyRestrictionSet)clusteringColumnsRestrictions).iterator();
+ Restriction previousRestriction = null;
+
+ while (iter.hasNext())
+ {
+ Restriction restriction = iter.next();
+
+ if (previousRestriction != null)
+ {
+ ColumnDefinition lastRestrictionStart = previousRestriction.getFirstColumn();
+ ColumnDefinition newRestrictionStart = restriction.getFirstColumn();
+
+ if (previousRestriction.isSlice() && newRestrictionStart.position() > lastRestrictionStart.position())
+ throw invalidRequest("Clustering column \"%s\" cannot be restricted (preceding column \"%s\" is restricted by a non-EQ relation)",
+ newRestrictionStart.name,
+ lastRestrictionStart.name);
+ }
+ previousRestriction = restriction;
+ }
+ }
+
+ public final boolean clusteringRestrictionsNeedFiltering()
+ {
+ assert clusteringColumnsRestrictions instanceof PrimaryKeyRestrictionSet;
+ return ((PrimaryKeyRestrictionSet) clusteringColumnsRestrictions).needsFiltering();
+ }
+
+ public List<IndexExpression> getIndexExpressions(SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ if (!usesSecondaryIndexing || indexRestrictions.isEmpty())
+ return Collections.emptyList();
+
+ List<IndexExpression> expressions = new ArrayList<>();
+ for (Restrictions restrictions : indexRestrictions)
+ restrictions.addIndexExpressionTo(expressions, indexManager, options);
+
+ return expressions;
+ }
+
+ /**
+ * Returns the partition keys for which the data is requested.
+ *
+ * @param options the query options
+ * @return the partition keys for which the data is requested.
+ * @throws InvalidRequestException if the partition keys cannot be retrieved
+ */
+ public Collection<ByteBuffer> getPartitionKeys(final QueryOptions options) throws InvalidRequestException
+ {
- return partitionKeyRestrictions.values(options);
++ return partitionKeyRestrictions.values(cfm, options);
+ }
+
+ /**
+ * Returns the specified bound of the partition key.
+ *
+ * @param b the boundary type
+ * @param options the query options
+ * @return the specified bound of the partition key
+ * @throws InvalidRequestException if the boundary cannot be retrieved
+ */
+ private ByteBuffer getPartitionKeyBound(Bound b, QueryOptions options) throws InvalidRequestException
+ {
+ // Deal with unrestricted partition key components (special-casing is required to deal with 2i queries on the
+ // first
+ // component of a composite partition key).
+ if (hasPartitionKeyUnrestrictedComponents())
+ return ByteBufferUtil.EMPTY_BYTE_BUFFER;
+
+ // We deal with IN queries for keys in other places, so we know buildBound will return only one result
- return partitionKeyRestrictions.bounds(b, options).get(0);
++ return partitionKeyRestrictions.bounds(cfm, b, options).get(0);
+ }
+
+ /**
+ * Returns the partition key bounds.
+ *
+ * @param options the query options
+ * @return the partition key bounds
+ * @throws InvalidRequestException if the query is invalid
+ */
+ public AbstractBounds<RowPosition> getPartitionKeyBounds(QueryOptions options) throws InvalidRequestException
+ {
+ IPartitioner p = StorageService.getPartitioner();
+
+ if (partitionKeyRestrictions.isOnToken())
+ {
+ return getPartitionKeyBoundsForTokenRestrictions(p, options);
+ }
+
+ return getPartitionKeyBounds(p, options);
+ }
+
+ private AbstractBounds<RowPosition> getPartitionKeyBounds(IPartitioner p,
+ QueryOptions options) throws InvalidRequestException
+ {
+ ByteBuffer startKeyBytes = getPartitionKeyBound(Bound.START, options);
+ ByteBuffer finishKeyBytes = getPartitionKeyBound(Bound.END, options);
+
+ RowPosition startKey = RowPosition.ForKey.get(startKeyBytes, p);
+ RowPosition finishKey = RowPosition.ForKey.get(finishKeyBytes, p);
+
+ if (startKey.compareTo(finishKey) > 0 && !finishKey.isMinimum())
+ return null;
+
+ if (partitionKeyRestrictions.isInclusive(Bound.START))
+ {
+ return partitionKeyRestrictions.isInclusive(Bound.END)
+ ? new Bounds<>(startKey, finishKey)
+ : new IncludingExcludingBounds<>(startKey, finishKey);
+ }
+
+ return partitionKeyRestrictions.isInclusive(Bound.END)
+ ? new Range<>(startKey, finishKey)
+ : new ExcludingBounds<>(startKey, finishKey);
+ }
+
+ private AbstractBounds<RowPosition> getPartitionKeyBoundsForTokenRestrictions(IPartitioner p,
+ QueryOptions options)
+ throws InvalidRequestException
+ {
+ Token startToken = getTokenBound(Bound.START, options, p);
+ Token endToken = getTokenBound(Bound.END, options, p);
+
+ boolean includeStart = partitionKeyRestrictions.isInclusive(Bound.START);
+ boolean includeEnd = partitionKeyRestrictions.isInclusive(Bound.END);
+
+ /*
+ * If we ask SP.getRangeSlice() for (token(200), token(200)], it will happily return the whole ring.
+ * However, wrapping range doesn't really make sense for CQL, and we want to return an empty result in that
+ * case (CASSANDRA-5573). So special case to create a range that is guaranteed to be empty.
+ *
+ * In practice, we want to return an empty result set if either startToken > endToken, or both are equal but
+ * one of the bound is excluded (since [a, a] can contains something, but not (a, a], [a, a) or (a, a)).
+ * Note though that in the case where startToken or endToken is the minimum token, then this special case
+ * rule should not apply.
+ */
+ int cmp = startToken.compareTo(endToken);
+ if (!startToken.isMinimum() && !endToken.isMinimum()
+ && (cmp > 0 || (cmp == 0 && (!includeStart || !includeEnd))))
+ return null;
+
+ RowPosition start = includeStart ? startToken.minKeyBound() : startToken.maxKeyBound();
+ RowPosition end = includeEnd ? endToken.maxKeyBound() : endToken.minKeyBound();
+
+ return new Range<>(start, end);
+ }
+
+ private Token getTokenBound(Bound b, QueryOptions options, IPartitioner p) throws InvalidRequestException
+ {
+ if (!partitionKeyRestrictions.hasBound(b))
+ return p.getMinimumToken();
+
- ByteBuffer value = partitionKeyRestrictions.bounds(b, options).get(0);
++ ByteBuffer value = partitionKeyRestrictions.bounds(cfm, b, options).get(0);
+ checkNotNull(value, "Invalid null token value");
+ return p.getTokenFactory().fromByteArray(value);
+ }
+
+ /**
+ * Checks if the query does not contains any restriction on the clustering columns.
+ *
+ * @return <code>true</code> if the query does not contains any restriction on the clustering columns,
+ * <code>false</code> otherwise.
+ */
+ public boolean hasNoClusteringColumnsRestriction()
+ {
+ return clusteringColumnsRestrictions.isEmpty();
+ }
+
+ /**
+ * Checks if the query has some restrictions on the clustering columns.
+ *
+ * @return <code>true</code> if the query has some restrictions on the clustering columns,
+ * <code>false</code> otherwise.
+ */
+ public boolean hasClusteringColumnsRestriction()
+ {
+ return !clusteringColumnsRestrictions.isEmpty();
+ }
+
+ // For non-composite slices, we don't support internally the difference between exclusive and
+ // inclusive bounds, so we deal with it manually.
+ public boolean isNonCompositeSliceWithExclusiveBounds()
+ {
+ return !cfm.comparator.isCompound()
+ && clusteringColumnsRestrictions.isSlice()
+ && (!clusteringColumnsRestrictions.isInclusive(Bound.START) || !clusteringColumnsRestrictions.isInclusive(Bound.END));
+ }
+
+ /**
+ * Returns the requested clustering columns as <code>Composite</code>s.
+ *
+ * @param options the query options
+ * @return the requested clustering columns as <code>Composite</code>s
+ * @throws InvalidRequestException if the query is not valid
+ */
+ public List<Composite> getClusteringColumnsAsComposites(QueryOptions options) throws InvalidRequestException
+ {
- return clusteringColumnsRestrictions.valuesAsComposites(options);
++ return clusteringColumnsRestrictions.valuesAsComposites(cfm, options);
+ }
+
+ /**
+ * Returns the bounds (start or end) of the clustering columns as <code>Composites</code>.
+ *
+ * @param b the bound type
+ * @param options the query options
+ * @return the bounds (start or end) of the clustering columns as <code>Composites</code>
+ * @throws InvalidRequestException if the request is not valid
+ */
+ public List<Composite> getClusteringColumnsBoundsAsComposites(Bound b,
+ QueryOptions options) throws InvalidRequestException
+ {
- List<Composite> bounds = clusteringColumnsRestrictions.boundsAsComposites(b, options);
++ List<Composite> bounds = clusteringColumnsRestrictions.boundsAsComposites(cfm, b, options);
+ for (Composite c : bounds) {
+ if (!c.isEmpty())
+ QueryProcessor.validateComposite(c, cfm.comparator);
+ }
+ return bounds;
+ }
+
+ /**
+ * Returns the bounds (start or end) of the clustering columns.
+ *
+ * @param b the bound type
+ * @param options the query options
+ * @return the bounds (start or end) of the clustering columns
+ * @throws InvalidRequestException if the request is not valid
+ */
+ public List<ByteBuffer> getClusteringColumnsBounds(Bound b, QueryOptions options) throws InvalidRequestException
+ {
- return clusteringColumnsRestrictions.bounds(b, options);
++ return clusteringColumnsRestrictions.bounds(cfm, b, options);
+ }
+
+ /**
+ * Checks if the bounds (start or end) of the clustering columns are inclusive.
+ *
+ * @param bound the bound type
+ * @return <code>true</code> if the bounds (start or end) of the clustering columns are inclusive,
+ * <code>false</code> otherwise
+ */
+ public boolean areRequestedBoundsInclusive(Bound bound)
+ {
+ return clusteringColumnsRestrictions.isInclusive(bound);
+ }
+
+ /**
+ * Checks if the query returns a range of columns.
+ *
+ * @return <code>true</code> if the query returns a range of columns, <code>false</code> otherwise.
+ */
+ public boolean isColumnRange()
+ {
+ // Due to CASSANDRA-5762, we always do a slice for CQL3 tables (not dense, composite).
+ // Static CF (non dense but non composite) never entails a column slice however
+ if (!cfm.comparator.isDense())
+ return cfm.comparator.isCompound();
+
+ // Otherwise (i.e. for compact table where we don't have a row marker anyway and thus don't care about
+ // CASSANDRA-5762),
+ // it is a range query if it has at least one the column alias for which no relation is defined or is not EQ.
+ return clusteringColumnsRestrictions.size() < cfm.clusteringColumns().size() || clusteringColumnsRestrictions.isSlice();
+ }
+
+ /**
+ * Checks if the query need to use filtering.
+ * @return <code>true</code> if the query need to use filtering, <code>false</code> otherwise.
+ */
+ public boolean needFiltering()
+ {
+ int numberOfRestrictedColumns = 0;
+ for (Restrictions restrictions : indexRestrictions)
+ numberOfRestrictedColumns += restrictions.size();
+
+ return numberOfRestrictedColumns > 1
+ || (numberOfRestrictedColumns == 0 && !clusteringColumnsRestrictions.isEmpty())
+ || (numberOfRestrictedColumns != 0
+ && nonPrimaryKeyRestrictions.hasMultipleContains());
+ }
+
+ private void validateSecondaryIndexSelections(boolean selectsOnlyStaticColumns) throws InvalidRequestException
+ {
+ checkFalse(keyIsInRelation(),
+ "Select on indexed columns and with IN clause for the PRIMARY KEY are not supported");
+ // When the user only select static columns, the intent is that we don't query the whole partition but just
+ // the static parts. But 1) we don't have an easy way to do that with 2i and 2) since we don't support index on
+ // static columns
+ // so far, 2i means that you've restricted a non static column, so the query is somewhat non-sensical.
+ checkFalse(selectsOnlyStaticColumns, "Queries using 2ndary indexes don't support selecting only static columns");
+ }
+
+ public void reverse()
+ {
+ clusteringColumnsRestrictions = new ReversedPrimaryKeyRestrictions(clusteringColumnsRestrictions);
+ }
++
++ /**
++ * Checks if the query will never return any rows.
++ *
++ * @param options the query options
++ * @return {@code true} if the query will never return any rows, {@false} otherwise
++ */
++ public boolean isNotReturningAnyRows(QueryOptions options)
++ {
++ return clusteringColumnsRestrictions.isNotReturningAnyRows(cfm, options);
++ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java
index bd04610,0000000..18444ec
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java
@@@ -1,237 -1,0 +1,245 @@@
+/*
+ * 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.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.common.collect.BoundType;
+import com.google.common.collect.ImmutableRangeSet;
+import com.google.common.collect.Range;
+import com.google.common.collect.RangeSet;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.dht.IPartitioner;
+import org.apache.cassandra.dht.Token;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.service.StorageService;
+
+import static org.apache.cassandra.cql3.statements.Bound.END;
+import static org.apache.cassandra.cql3.statements.Bound.START;
+
+/**
+ * <code>Restriction</code> decorator used to merge non-token restriction and token restriction on partition keys.
+ */
+final class TokenFilter extends ForwardingPrimaryKeyRestrictions
+{
+ /**
+ * The decorated restriction
+ */
+ private PrimaryKeyRestrictions restrictions;
+
+ /**
+ * The restriction on the token
+ */
+ private TokenRestriction tokenRestriction;
+
+ /**
+ * The partitioner
+ */
+ private static final IPartitioner partitioner = StorageService.getPartitioner();
+
+ @Override
+ protected PrimaryKeyRestrictions getDelegate()
+ {
+ return restrictions;
+ }
+
+ @Override
+ public boolean isOnToken()
+ {
+ // if all partition key columns have non-token restrictions, we can simply use the token range to filter
+ // those restrictions and then ignore the token range
+ return restrictions.size() < tokenRestriction.size();
+ }
+
+ public TokenFilter(PrimaryKeyRestrictions restrictions, TokenRestriction tokenRestriction)
+ {
+ this.restrictions = restrictions;
+ this.tokenRestriction = tokenRestriction;
+ }
+
+ @Override
- public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> values(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
- return filter(restrictions.values(options), options);
++ return filter(cfm, restrictions.values(cfm, options), options);
+ }
+
+ @Override
- public List<Composite> valuesAsComposites(QueryOptions options) throws InvalidRequestException
++ public List<Composite> valuesAsComposites(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws InvalidRequestException
+ {
+ if (restriction.isOnToken())
+ return new TokenFilter(restrictions, (TokenRestriction) tokenRestriction.mergeWith(restriction));
+
+ return new TokenFilter(super.mergeWith(restriction), tokenRestriction);
+ }
+
+ @Override
+ public boolean isInclusive(Bound bound)
+ {
+ return tokenRestriction.isInclusive(bound);
+ }
+
+ @Override
+ public boolean hasBound(Bound b)
+ {
+ return tokenRestriction.hasBound(b);
+ }
+
+ @Override
- public List<ByteBuffer> bounds(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> bounds(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
- return tokenRestriction.bounds(bound, options);
++ return tokenRestriction.bounds(cfm, bound, options);
+ }
+
+ @Override
- public List<Composite> boundsAsComposites(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<Composite> boundsAsComposites(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
- return tokenRestriction.boundsAsComposites(bound, options);
++ return tokenRestriction.boundsAsComposites(cfm, bound, options);
+ }
+
+ /**
+ * Filter the values returned by the restriction.
+ *
++ * @param cfm the table metadata
+ * @param values the values returned by the decorated restriction
+ * @param options the query options
+ * @return the values matching the token restriction
+ * @throws InvalidRequestException if the request is invalid
+ */
- private List<ByteBuffer> filter(List<ByteBuffer> values, QueryOptions options) throws InvalidRequestException
++ private List<ByteBuffer> filter(CFMetaData cfm, List<ByteBuffer> values, QueryOptions options) throws InvalidRequestException
+ {
- RangeSet<Token> rangeSet = tokenRestriction.isSlice() ? toRangeSet(tokenRestriction, options)
- : toRangeSet(tokenRestriction.values(options));
++ RangeSet<Token> rangeSet = tokenRestriction.isSlice() ? toRangeSet(cfm, tokenRestriction, options)
++ : toRangeSet(tokenRestriction.values(cfm, options));
+
+ return filterWithRangeSet(rangeSet, values);
+ }
+
+ /**
+ * Filter out the values for which the tokens are not included within the specified range.
+ *
+ * @param tokens the tokens range
+ * @param values the restricted values
+ * @return the values for which the tokens are not included within the specified range.
+ */
+ private static List<ByteBuffer> filterWithRangeSet(RangeSet<Token> tokens, List<ByteBuffer> values)
+ {
+ List<ByteBuffer> remaining = new ArrayList<>();
+
+ for (ByteBuffer value : values)
+ {
+ Token token = partitioner.getToken(value);
+
+ if (!tokens.contains(token))
+ continue;
+
+ remaining.add(value);
+ }
+ return remaining;
+ }
+
+ /**
+ * Converts the specified list into a range set.
+ *
+ * @param buffers the token restriction values
+ * @return the range set corresponding to the specified list
+ */
+ private static RangeSet<Token> toRangeSet(List<ByteBuffer> buffers)
+ {
+ ImmutableRangeSet.Builder<Token> builder = ImmutableRangeSet.builder();
+
+ for (ByteBuffer buffer : buffers)
+ builder.add(Range.singleton(deserializeToken(buffer)));
+
+ return builder.build();
+ }
+
+ /**
+ * Converts the specified slice into a range set.
+ *
++ * @param cfm the table metadata
+ * @param slice the slice to convert
+ * @param options the query option
+ * @return the range set corresponding to the specified slice
+ * @throws InvalidRequestException if the request is invalid
+ */
- private static RangeSet<Token> toRangeSet(TokenRestriction slice, QueryOptions options) throws InvalidRequestException
++ private static RangeSet<Token> toRangeSet(CFMetaData cfm, TokenRestriction slice, QueryOptions options) throws InvalidRequestException
+ {
+ if (slice.hasBound(START))
+ {
- Token start = deserializeToken(slice.bounds(START, options).get(0));
++ Token start = deserializeToken(slice.bounds(cfm, START, options).get(0));
+
+ BoundType startBoundType = toBoundType(slice.isInclusive(START));
+
+ if (slice.hasBound(END))
+ {
+ BoundType endBoundType = toBoundType(slice.isInclusive(END));
- Token end = deserializeToken(slice.bounds(END, options).get(0));
++ Token end = deserializeToken(slice.bounds(cfm, END, options).get(0));
+
+ if (start.equals(end) && (BoundType.OPEN == startBoundType || BoundType.OPEN == endBoundType))
+ return ImmutableRangeSet.of();
+
+ if (start.compareTo(end) <= 0)
+ return ImmutableRangeSet.of(Range.range(start,
+ startBoundType,
+ end,
+ endBoundType));
+
+ return ImmutableRangeSet.<Token> builder()
+ .add(Range.upTo(end, endBoundType))
+ .add(Range.downTo(start, startBoundType))
+ .build();
+ }
+ return ImmutableRangeSet.of(Range.downTo(start,
+ startBoundType));
+ }
- Token end = deserializeToken(slice.bounds(END, options).get(0));
++ Token end = deserializeToken(slice.bounds(cfm, END, options).get(0));
+ return ImmutableRangeSet.of(Range.upTo(end, toBoundType(slice.isInclusive(END))));
+ }
+
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ return false;
++ }
++
+ /**
+ * Deserializes the token corresponding to the specified buffer.
+ *
+ * @param buffer the buffer
+ * @return the token corresponding to the specified buffer
+ */
+ private static Token deserializeToken(ByteBuffer buffer)
+ {
+ return partitioner.getTokenFactory().fromByteArray(buffer);
+ }
+
+ private static BoundType toBoundType(boolean inclusive)
+ {
+ return inclusive ? BoundType.CLOSED : BoundType.OPEN;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
index 3cd3304,0000000..97c55c4
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
@@@ -1,273 -1,0 +1,274 @@@
+/*
+ * 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.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.List;
+
+import com.google.common.base.Joiner;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.Term;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.composites.CType;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.db.composites.CompositesBuilder;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
+
+/**
+ * <code>Restriction</code> using the token function.
+ */
+public abstract class TokenRestriction extends AbstractPrimaryKeyRestrictions
+{
+ /**
+ * The definition of the columns to which apply the token restriction.
+ */
+ protected final List<ColumnDefinition> columnDefs;
+
+ /**
+ * Creates a new <code>TokenRestriction</code> that apply to the specified columns.
+ *
+ * @param ctype the composite type
+ * @param columnDefs the definition of the columns to which apply the token restriction
+ */
+ public TokenRestriction(CType ctype, List<ColumnDefinition> columnDefs)
+ {
+ super(ctype);
+ this.columnDefs = columnDefs;
+ }
+
+ @Override
+ public boolean isOnToken()
+ {
+ return true;
+ }
+
+ @Override
+ public List<ColumnDefinition> getColumnDefs()
+ {
+ return columnDefs;
+ }
+
+ @Override
+ public ColumnDefinition getFirstColumn()
+ {
+ return columnDefs.get(0);
+ }
+
+ @Override
+ public ColumnDefinition getLastColumn()
+ {
+ return columnDefs.get(columnDefs.size() - 1);
+ }
+
+ @Override
+ public boolean hasSupportingIndex(SecondaryIndexManager secondaryIndexManager)
+ {
+ return false;
+ }
+
+ @Override
+ public final void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options)
+ {
+ throw new UnsupportedOperationException("Index expression cannot be created for token restriction");
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
- public List<Composite> valuesAsComposites(QueryOptions options) throws InvalidRequestException
++ public List<Composite> valuesAsComposites(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
- public List<Composite> boundsAsComposites(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<Composite> boundsAsComposites(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns the column names as a comma separated <code>String</code>.
+ *
+ * @return the column names as a comma separated <code>String</code>.
+ */
+ protected final String getColumnNamesAsString()
+ {
+ return Joiner.on(", ").join(ColumnDefinition.toIdentifiers(columnDefs));
+ }
+
+ @Override
+ public final PrimaryKeyRestrictions mergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ if (!otherRestriction.isOnToken())
+ return new TokenFilter(toPrimaryKeyRestriction(otherRestriction), this);
+
+ return doMergeWith((TokenRestriction) otherRestriction);
+ }
+
+ /**
+ * Merges this restriction with the specified <code>TokenRestriction</code>.
+ * @param otherRestriction the <code>TokenRestriction</code> to merge with.
+ */
+ protected abstract PrimaryKeyRestrictions doMergeWith(TokenRestriction otherRestriction) throws InvalidRequestException;
+
+ /**
+ * Converts the specified restriction into a <code>PrimaryKeyRestrictions</code>.
+ *
+ * @param restriction the restriction to convert
+ * @return a <code>PrimaryKeyRestrictions</code>
+ * @throws InvalidRequestException if a problem occurs while converting the restriction
+ */
+ private PrimaryKeyRestrictions toPrimaryKeyRestriction(Restriction restriction) throws InvalidRequestException
+ {
+ if (restriction instanceof PrimaryKeyRestrictions)
+ return (PrimaryKeyRestrictions) restriction;
+
+ return new PrimaryKeyRestrictionSet(ctype).mergeWith(restriction);
+ }
+
+ public static final class EQ extends TokenRestriction
+ {
+ private final Term value;
+
+ public EQ(CType ctype, List<ColumnDefinition> columnDefs, Term value)
+ {
+ super(ctype, columnDefs);
+ this.value = value;
+ }
+
+ @Override
+ public boolean isEQ()
+ {
+ return true;
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return value.getFunctions();
+ }
+
+ @Override
+ protected PrimaryKeyRestrictions doMergeWith(TokenRestriction otherRestriction) throws InvalidRequestException
+ {
+ throw invalidRequest("%s cannot be restricted by more than one relation if it includes an Equal",
+ Joiner.on(", ").join(ColumnDefinition.toIdentifiers(columnDefs)));
+ }
+
+ @Override
- public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> values(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
+ return Collections.singletonList(value.bindAndGet(options));
+ }
+ }
+
+ public static class Slice extends TokenRestriction
+ {
+ private final TermSlice slice;
+
+ public Slice(CType ctype, List<ColumnDefinition> columnDefs, Bound bound, boolean inclusive, Term term)
+ {
+ super(ctype, columnDefs);
+ slice = TermSlice.newInstance(bound, inclusive, term);
+ }
+
+ @Override
+ public boolean isSlice()
+ {
+ return true;
+ }
+
+ @Override
- public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> values(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasBound(Bound b)
+ {
+ return slice.hasBound(b);
+ }
+
+ @Override
- public List<ByteBuffer> bounds(Bound b, QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> bounds(CFMetaData cfm, Bound b, QueryOptions options) throws InvalidRequestException
+ {
+ return Collections.singletonList(slice.bound(b).bindAndGet(options));
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return slice.getFunctions();
+ }
+
+ @Override
+ public boolean isInclusive(Bound b)
+ {
+ return slice.isInclusive(b);
+ }
+
+ @Override
+ protected PrimaryKeyRestrictions doMergeWith(TokenRestriction otherRestriction)
+ throws InvalidRequestException
+ {
+ if (!otherRestriction.isSlice())
+ throw invalidRequest("Columns \"%s\" cannot be restricted by both an equality and an inequality relation",
+ getColumnNamesAsString());
+
+ TokenRestriction.Slice otherSlice = (TokenRestriction.Slice) otherRestriction;
+
+ if (hasBound(Bound.START) && otherSlice.hasBound(Bound.START))
+ throw invalidRequest("More than one restriction was found for the start bound on %s",
+ getColumnNamesAsString());
+
+ if (hasBound(Bound.END) && otherSlice.hasBound(Bound.END))
+ throw invalidRequest("More than one restriction was found for the end bound on %s",
+ getColumnNamesAsString());
+
+ return new Slice(ctype, columnDefs, slice.merge(otherSlice.slice));
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("SLICE%s", slice);
+ }
+
+ private Slice(CType ctype, List<ColumnDefinition> columnDefs, TermSlice slice)
+ {
+ super(ctype, columnDefs);
+ this.slice = slice;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
index 5ffcc8a,f84188a..3d134b5
--- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
@@@ -311,23 -301,38 +311,23 @@@ public abstract class ModificationState
public List<ByteBuffer> buildPartitionKeyNames(QueryOptions options)
throws InvalidRequestException
{
- CBuilder keyBuilder = cfm.getKeyValidatorAsCType().builder();
- List<ByteBuffer> keys = new ArrayList<ByteBuffer>();
+ CompositesBuilder keyBuilder = new CompositesBuilder(cfm.getKeyValidatorAsCType());
for (ColumnDefinition def : cfm.partitionKeyColumns())
{
- Restriction r = processedKeys.get(def.name);
- if (r == null)
- throw new InvalidRequestException(String.format("Missing mandatory PRIMARY KEY part %s", def.name));
-
- List<ByteBuffer> values = r.values(options);
+ Restriction r = checkNotNull(processedKeys.get(def.name), "Missing mandatory PRIMARY KEY part %s", def.name);
- r.appendTo(keyBuilder, options);
++ r.appendTo(cfm, keyBuilder, options);
+ }
- if (keyBuilder.remainingCount() == 1)
- {
- for (ByteBuffer val : values)
- {
- if (val == null)
- throw new InvalidRequestException(String.format("Invalid null value for partition key part %s", def.name));
- ByteBuffer key = keyBuilder.buildWith(val).toByteBuffer();
- ThriftValidation.validateKey(cfm, key);
- keys.add(key);
- }
- }
- else
+ return Lists.transform(filterAndSort(keyBuilder.build()), new com.google.common.base.Function<Composite, ByteBuffer>()
+ {
+ @Override
+ public ByteBuffer apply(Composite composite)
{
- if (values.size() != 1)
- throw new InvalidRequestException("IN is only supported on the last column of the partition key");
- ByteBuffer val = values.get(0);
- if (val == null)
- throw new InvalidRequestException(String.format("Invalid null value for partition key part %s", def.name));
- keyBuilder.add(val);
+ ByteBuffer byteBuffer = composite.toByteBuffer();
+ ThriftValidation.validateKey(cfm, byteBuffer);
+ return byteBuffer;
}
- }
- return keys;
+ });
}
public Composite createClusteringPrefix(QueryOptions options)
@@@ -406,26 -411,15 +406,26 @@@
}
else
{
- r.appendTo(builder, options);
- List<ByteBuffer> values = r.values(options);
- assert values.size() == 1; // We only allow IN for row keys so far
- ByteBuffer val = values.get(0);
- if (val == null)
- throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s", def.name));
- builder.add(val);
++ r.appendTo(cfm, builder, options);
}
}
- return builder.build();
+ return builder.build().get(0); // We only allow IN for row keys so far
+ }
+
+ /**
+ * Removes duplicates and sort the specified composites.
+ *
+ * @param composites the composites to filter and sort
+ * @return the composites sorted and without duplicates
+ */
+ private List<Composite> filterAndSort(List<Composite> composites)
+ {
+ if (composites.size() <= 1)
+ return composites;
+
+ TreeSet<Composite> set = new TreeSet<Composite>(cfm.getKeyValidatorAsCType());
+ set.addAll(composites);
+ return new ArrayList<>(set);
}
protected ColumnDefinition getFirstEmptyKey()
[36/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0'
into cassandra-3.9
Posted by ms...@apache.org.
Merge branch 'cassandra-3.0' into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/e72dc29e
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/e72dc29e
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/e72dc29e
Branch: refs/heads/cassandra-3.8
Commit: e72dc29e69ed21d15c70788cd2c2b620eeabeb6c
Parents: 903ac4d e6dd152
Author: Yuki Morishita <yu...@apache.org>
Authored: Mon Aug 15 10:09:55 2016 -0500
Committer: Yuki Morishita <yu...@apache.org>
Committed: Mon Aug 15 10:09:55 2016 -0500
----------------------------------------------------------------------
.../apache/cassandra/streaming/messages/OutgoingFileMessage.java | 3 +++
1 file changed, 3 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/e72dc29e/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java
----------------------------------------------------------------------
[17/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0'
into cassandra-3.9
Posted by ms...@apache.org.
Merge branch 'cassandra-3.0' into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/02f48c86
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/02f48c86
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/02f48c86
Branch: refs/heads/cassandra-3.8
Commit: 02f48c86591939e3cd20157af867dfec94ca443b
Parents: 6a846e6 19245e5
Author: Yuki Morishita <yu...@apache.org>
Authored: Thu Aug 11 11:06:20 2016 -0500
Committer: Yuki Morishita <yu...@apache.org>
Committed: Thu Aug 11 11:06:20 2016 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../cassandra/utils/RMIServerSocketFactoryImpl.java | 13 +++++++++++--
2 files changed, 12 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/02f48c86/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index bca8879,0a1d406..29a6c50
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,12 -1,9 +1,13 @@@
-3.0.9
+3.9
+ * Fix value skipping with counter columns (CASSANDRA-11726)
+ * Fix nodetool tablestats miss SSTable count (CASSANDRA-12205)
+ * Fixed flacky SSTablesIteratedTest (CASSANDRA-12282)
+ * Fixed flacky SSTableRewriterTest: check file counts before calling validateCFS (CASSANDRA-12348)
+ * cqlsh: Fix handling of $$-escaped strings (CASSANDRA-12189)
+ * Fix SSL JMX requiring truststore containing server cert (CASSANDRA-12109)
+Merged from 3.0:
+ * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
- * Backport CASSANDRA-12002 (CASSANDRA-12177)
* Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
- * Fix potential bad messaging service message for paged range reads
- within mixed-version 3.x clusters (CASSANDRA-12249)
* Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
* NullPointerException during compaction on table with static columns (CASSANDRA-12336)
* Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/02f48c86/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
index 4ac4a39,6444a65..9baf0c7
--- a/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
+++ b/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
@@@ -38,9 -33,18 +39,17 @@@ public class RMIServerSocketFactoryImp
public ServerSocket createServerSocket(final int pPort) throws IOException
{
- ServerSocket socket = ServerSocketFactory.getDefault()
- .createServerSocket(pPort, 0, InetAddress.getLoopbackAddress());
+ ServerSocket socket = ServerSocketFactory.getDefault().createServerSocket(pPort, 0, bindAddress);
- socket.setReuseAddress(true);
- return socket;
+ try
+ {
+ socket.setReuseAddress(true);
+ return socket;
+ }
+ catch (SocketException e)
+ {
+ socket.close();
+ throw e;
+ }
}
public boolean equals(Object obj)
[15/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0'
into cassandra-3.9
Posted by ms...@apache.org.
Merge branch 'cassandra-3.0' into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/6a846e6f
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/6a846e6f
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/6a846e6f
Branch: refs/heads/cassandra-3.8
Commit: 6a846e6f8443280f41a121f318ca192c8d0d97ea
Parents: 15fd71f de86ab1
Author: Yuki Morishita <yu...@apache.org>
Authored: Thu Aug 11 10:11:50 2016 -0500
Committer: Yuki Morishita <yu...@apache.org>
Committed: Thu Aug 11 10:11:50 2016 -0500
----------------------------------------------------------------------
----------------------------------------------------------------------
[28/50] [abbrv] cassandra git commit: Only set broadcast_rpc_address
on Ec2MultiRegionSnitch if it's not set
Posted by ms...@apache.org.
Only set broadcast_rpc_address on Ec2MultiRegionSnitch if it's not set
Patch by Paulo Motta; reviewed by Tyler Hobbs for CASSANDRA-11356
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/91f7387e
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/91f7387e
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/91f7387e
Branch: refs/heads/cassandra-3.8
Commit: 91f7387e1f785b18321777311a5c3416af0663c2
Parents: e86d531
Author: Paulo Motta <pa...@gmail.com>
Authored: Tue Aug 2 12:42:20 2016 -0300
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Fri Aug 12 18:25:19 2016 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
NEWS.txt | 6 ++++
.../cassandra/config/DatabaseDescriptor.java | 6 ++--
.../cassandra/locator/Ec2MultiRegionSnitch.java | 6 +++-
.../cassandra/service/StorageService.java | 4 +--
.../org/apache/cassandra/transport/Server.java | 2 +-
.../org/apache/cassandra/utils/FBUtilities.java | 20 ++++++++++++
.../apache/cassandra/utils/FBUtilitiesTest.java | 32 ++++++++++++++++++++
8 files changed, 71 insertions(+), 6 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index ddc6720..394598a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.2.8
+ * Only set broadcast_rpc_address on Ec2MultiRegionSnitch if it's not set (CASSANDRA-11357)
* Update StorageProxy range metrics for timeouts, failures and unavailables (CASSANDRA-9507)
* Add Sigar to classes included in clientutil.jar (CASSANDRA-11635)
* Add decay to histograms and timers used for metrics (CASSANDRA-11752)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/NEWS.txt
----------------------------------------------------------------------
diff --git a/NEWS.txt b/NEWS.txt
index a3ba0dd..f0712eb 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -21,6 +21,12 @@ New features
- JSON timestamps are now in UTC and contain the timezone information, see
CASSANDRA-11137 for more details.
+Upgrading
+---------
+ - Ec2MultiRegionSnitch will no longer automatically set broadcast_rpc_address
+ to the public instance IP if this property is defined on cassandra.yaml.
+
+
2.2.6
=====
http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index 6e46725..75f80b9 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -174,7 +174,7 @@ public class DatabaseDescriptor
}
@VisibleForTesting
- static void applyAddressConfig(Config config) throws ConfigurationException
+ public static void applyAddressConfig(Config config) throws ConfigurationException
{
listenAddress = null;
rpcAddress = null;
@@ -266,7 +266,6 @@ public class DatabaseDescriptor
if (rpcAddress.isAnyLocalAddress())
throw new ConfigurationException("If rpc_address is set to a wildcard address (" + config.rpc_address + "), then " +
"you must set broadcast_rpc_address to a value other than " + config.rpc_address, false);
- broadcastRpcAddress = rpcAddress;
}
}
@@ -1269,6 +1268,9 @@ public class DatabaseDescriptor
broadcastRpcAddress = broadcastRPCAddr;
}
+ /**
+ * May be null, please use {@link FBUtilities#getBroadcastRpcAddress()} instead.
+ */
public static InetAddress getBroadcastRpcAddress()
{
return broadcastRpcAddress;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/src/java/org/apache/cassandra/locator/Ec2MultiRegionSnitch.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/locator/Ec2MultiRegionSnitch.java b/src/java/org/apache/cassandra/locator/Ec2MultiRegionSnitch.java
index ab1b5d0..b32ca84 100644
--- a/src/java/org/apache/cassandra/locator/Ec2MultiRegionSnitch.java
+++ b/src/java/org/apache/cassandra/locator/Ec2MultiRegionSnitch.java
@@ -51,7 +51,11 @@ public class Ec2MultiRegionSnitch extends Ec2Snitch
localPrivateAddress = awsApiCall(PRIVATE_IP_QUERY_URL);
// use the Public IP to broadcast Address to other nodes.
DatabaseDescriptor.setBroadcastAddress(localPublicAddress);
- DatabaseDescriptor.setBroadcastRpcAddress(localPublicAddress);
+ if (DatabaseDescriptor.getBroadcastRpcAddress() == null)
+ {
+ logger.info("broadcast_rpc_address unset, broadcasting public IP as rpc_address: {}", localPublicAddress);
+ DatabaseDescriptor.setBroadcastRpcAddress(localPublicAddress);
+ }
}
@Override
http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/src/java/org/apache/cassandra/service/StorageService.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/StorageService.java b/src/java/org/apache/cassandra/service/StorageService.java
index fa04595..3148f5e 100644
--- a/src/java/org/apache/cassandra/service/StorageService.java
+++ b/src/java/org/apache/cassandra/service/StorageService.java
@@ -697,7 +697,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
getTokenMetadata().updateHostId(localHostId, FBUtilities.getBroadcastAddress());
appStates.put(ApplicationState.NET_VERSION, valueFactory.networkVersion());
appStates.put(ApplicationState.HOST_ID, valueFactory.hostId(localHostId));
- appStates.put(ApplicationState.RPC_ADDRESS, valueFactory.rpcaddress(DatabaseDescriptor.getBroadcastRpcAddress()));
+ appStates.put(ApplicationState.RPC_ADDRESS, valueFactory.rpcaddress(FBUtilities.getBroadcastRpcAddress()));
appStates.put(ApplicationState.RELEASE_VERSION, valueFactory.releaseVersion());
logger.info("Starting up server gossip");
Gossiper.instance.register(this);
@@ -1282,7 +1282,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
public String getRpcaddress(InetAddress endpoint)
{
if (endpoint.equals(FBUtilities.getBroadcastAddress()))
- return DatabaseDescriptor.getBroadcastRpcAddress().getHostAddress();
+ return FBUtilities.getBroadcastRpcAddress().getHostAddress();
else if (Gossiper.instance.getEndpointStateForEndpoint(endpoint).getApplicationState(ApplicationState.RPC_ADDRESS) == null)
return endpoint.getHostAddress();
else
http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/src/java/org/apache/cassandra/transport/Server.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/transport/Server.java b/src/java/org/apache/cassandra/transport/Server.java
index 5c0d9d2..d1047f9 100644
--- a/src/java/org/apache/cassandra/transport/Server.java
+++ b/src/java/org/apache/cassandra/transport/Server.java
@@ -486,7 +486,7 @@ public class Server implements CassandraDaemon.Server
// which is not useful to any driver and in fact may cauase serious problems to some drivers,
// see CASSANDRA-10052
if (!endpoint.equals(FBUtilities.getBroadcastAddress()) &&
- event.nodeAddress().equals(DatabaseDescriptor.getBroadcastRpcAddress()))
+ event.nodeAddress().equals(FBUtilities.getBroadcastRpcAddress()))
return;
send(event);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/src/java/org/apache/cassandra/utils/FBUtilities.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/utils/FBUtilities.java b/src/java/org/apache/cassandra/utils/FBUtilities.java
index 9eda878..23a2c2e 100644
--- a/src/java/org/apache/cassandra/utils/FBUtilities.java
+++ b/src/java/org/apache/cassandra/utils/FBUtilities.java
@@ -34,6 +34,7 @@ import java.util.zip.Checksum;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.collect.AbstractIterator;
import org.apache.commons.lang3.StringUtils;
@@ -74,6 +75,7 @@ public class FBUtilities
private static volatile InetAddress localInetAddress;
private static volatile InetAddress broadcastInetAddress;
+ private static volatile InetAddress broadcastRpcAddress;
public static int getAvailableProcessors()
{
@@ -147,6 +149,16 @@ public class FBUtilities
return broadcastInetAddress;
}
+
+ public static InetAddress getBroadcastRpcAddress()
+ {
+ if (broadcastRpcAddress == null)
+ broadcastRpcAddress = DatabaseDescriptor.getBroadcastRpcAddress() == null
+ ? DatabaseDescriptor.getRpcAddress()
+ : DatabaseDescriptor.getBroadcastRpcAddress();
+ return broadcastRpcAddress;
+ }
+
public static Collection<InetAddress> getAllLocalAddresses()
{
Set<InetAddress> localAddresses = new HashSet<InetAddress>();
@@ -824,4 +836,12 @@ public class FBUtilities
throw new RuntimeException(e);
}
}
+
+ @VisibleForTesting
+ protected static void reset()
+ {
+ localInetAddress = null;
+ broadcastInetAddress = null;
+ broadcastRpcAddress = null;
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java b/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
index c82bcc9..5b86252 100644
--- a/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
+++ b/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
@@ -21,6 +21,7 @@ package org.apache.cassandra.utils;
import static org.junit.Assert.fail;
import java.io.IOException;
+import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.StandardCharsets;
@@ -30,6 +31,9 @@ import org.junit.Test;
import java.util.Map;
import java.util.TreeMap;
+import org.apache.cassandra.config.Config;
+import org.apache.cassandra.config.DatabaseDescriptor;
+
import static org.junit.Assert.assertEquals;
public class FBUtilitiesTest
@@ -95,4 +99,32 @@ public class FBUtilitiesTest
ByteBuffer bytes = ByteBuffer.wrap(new byte[]{(byte)0xff, (byte)0xfe});
ByteBufferUtil.string(bytes, StandardCharsets.UTF_8);
}
+
+ @Test
+ public void testGetBroadcastRpcAddress() throws Exception
+ {
+ //When both rpc_address and broadcast_rpc_address are null, it should return the local address (from DD.applyAddressConfig)
+ FBUtilities.reset();
+ Config testConfig = DatabaseDescriptor.loadConfig();
+ testConfig.rpc_address = null;
+ testConfig.broadcast_rpc_address = null;
+ DatabaseDescriptor.applyAddressConfig(testConfig);
+ assertEquals(FBUtilities.getLocalAddress(), FBUtilities.getBroadcastRpcAddress());
+
+ //When rpc_address is defined and broadcast_rpc_address is null, it should return the rpc_address
+ FBUtilities.reset();
+ testConfig.rpc_address = "127.0.0.2";
+ testConfig.broadcast_rpc_address = null;
+ DatabaseDescriptor.applyAddressConfig(testConfig);
+ assertEquals(InetAddress.getByName("127.0.0.2"), FBUtilities.getBroadcastRpcAddress());
+
+ //When both rpc_address and broadcast_rpc_address are defined, it should return broadcast_rpc_address
+ FBUtilities.reset();
+ testConfig.rpc_address = "127.0.0.2";
+ testConfig.broadcast_rpc_address = "127.0.0.3";
+ DatabaseDescriptor.applyAddressConfig(testConfig);
+ assertEquals(InetAddress.getByName("127.0.0.3"), FBUtilities.getBroadcastRpcAddress());
+
+ FBUtilities.reset();
+ }
}
[45/50] [abbrv] cassandra git commit: Merge branch cassandra-2.1 into
cassandra-2.2
Posted by ms...@apache.org.
Merge branch cassandra-2.1 into cassandra-2.2
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/9583b6b3
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/9583b6b3
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/9583b6b3
Branch: refs/heads/cassandra-3.8
Commit: 9583b6b312866a4861df1a795329695b3fc09328
Parents: 88b3cfc 527d189
Author: Benjamin Lerer <b....@gmail.com>
Authored: Tue Aug 16 15:25:42 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Tue Aug 16 15:26:26 2016 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
NEWS.txt | 10 +
.../AbstractPrimaryKeyRestrictions.java | 5 +-
.../cql3/restrictions/AbstractRestriction.java | 10 +-
.../ForwardingPrimaryKeyRestrictions.java | 31 +-
.../restrictions/MultiColumnRestriction.java | 48 +-
.../restrictions/PrimaryKeyRestrictionSet.java | 36 +-
.../restrictions/PrimaryKeyRestrictions.java | 9 +-
.../cql3/restrictions/Restriction.java | 16 +-
.../ReversedPrimaryKeyRestrictions.java | 9 +-
.../restrictions/SingleColumnRestriction.java | 57 +-
.../restrictions/StatementRestrictions.java | 23 +-
.../cql3/restrictions/TokenFilter.java | 36 +-
.../cql3/restrictions/TokenRestriction.java | 13 +-
.../cql3/statements/ModificationStatement.java | 4 +-
.../cql3/statements/SelectStatement.java | 11 +-
.../cql3/statements/UpdateStatement.java | 2 +-
.../cassandra/db/compaction/Scrubber.java | 159 +++-
.../cassandra/db/marshal/ReversedType.java | 10 -
.../PrimaryKeyRestrictionSetTest.java | 372 ++++-----
.../validation/entities/SecondaryIndexTest.java | 142 ++++
.../cql3/validation/operations/DeleteTest.java | 42 +
.../operations/SelectOrderByTest.java | 758 +++++++++++--------
.../cql3/validation/operations/SelectTest.java | 372 ++++++++-
.../cassandra/db/marshal/ReversedTypeTest.java | 4 +-
25 files changed, 1540 insertions(+), 640 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 394598a,dee669a..c421398
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,24 -1,5 +1,25 @@@
-2.1.16
- * Fix queries with empty ByteBuffer values in clustering column restrictions (CASSANDRA-12127)
+2.2.8
+ * Only set broadcast_rpc_address on Ec2MultiRegionSnitch if it's not set (CASSANDRA-11357)
+ * Update StorageProxy range metrics for timeouts, failures and unavailables (CASSANDRA-9507)
+ * Add Sigar to classes included in clientutil.jar (CASSANDRA-11635)
+ * Add decay to histograms and timers used for metrics (CASSANDRA-11752)
+ * Fix hanging stream session (CASSANDRA-10992)
+ * Add byteman support for testing (CASSANDRA-12377)
+ * Fix INSERT JSON, fromJson() support of smallint, tinyint types (CASSANDRA-12371)
+ * Restore JVM metric export for metric reporters (CASSANDRA-12312)
+ * Release sstables of failed stream sessions only when outgoing transfers are finished (CASSANDRA-11345)
+ * Revert CASSANDRA-11427 (CASSANDRA-12351)
+ * Wait for tracing events before returning response and query at same consistency level client side (CASSANDRA-11465)
+ * cqlsh copyutil should get host metadata by connected address (CASSANDRA-11979)
+ * Fixed cqlshlib.test.remove_test_db (CASSANDRA-12214)
+ * Synchronize ThriftServer::stop() (CASSANDRA-12105)
+ * Use dedicated thread for JMX notifications (CASSANDRA-12146)
+ * NPE when trying to remove purgable tombstones from result (CASSANDRA-12143)
+ * Improve streaming synchronization and fault tolerance (CASSANDRA-11414)
+ * MemoryUtil.getShort() should return an unsigned short also for architectures not supporting unaligned memory accesses (CASSANDRA-11973)
+ * Don't write shadowed range tombstone (CASSANDRA-12030)
+Merged from 2.1:
++ * Fix queries with empty ByteBuffer values in clustering column restrictions (CASSANDRA-12127)
* Disable passing control to post-flush after flush failure to prevent data loss (CASSANDRA-11828)
* Allow STCS-in-L0 compactions to reduce scope with LCS (CASSANDRA-12040)
* cannot use cql since upgrading python to 2.7.11+ (CASSANDRA-11850)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/NEWS.txt
----------------------------------------------------------------------
diff --cc NEWS.txt
index f0712eb,6a70adc..0a3ab36
--- a/NEWS.txt
+++ b/NEWS.txt
@@@ -13,13 -13,18 +13,23 @@@ restore snapshots created with the prev
'sstableloader' tool. You can upgrade the file format of your snapshots
using the provided 'sstableupgrade' tool.
-2.1.16
-======
++2.2.8
++=====
+
+ Upgrading
+ ---------
+ - The ReversedType behaviour has been corrected for clustering columns of
+ BYTES type containing empty value. Scrub should be run on the existing
+ SSTables containing a descending clustering column of BYTES type to correct
- their ordering. See CASSANDRA-12127 for details.
++ their ordering. See CASSANDRA-12127 for more details.
+
-2.1.15
-======
+2.2.7
+=====
+
+New features
+------------
+ - JSON timestamps are now in UTC and contain the timezone information, see
+ CASSANDRA-11137 for more details.
Upgrading
---------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
index 2eaa386,0000000..51c3e26
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
@@@ -1,60 -1,0 +1,61 @@@
+/*
+ * 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.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.composites.CType;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+/**
+ * Base class for <code>PrimaryKeyRestrictions</code>.
+ */
+abstract class AbstractPrimaryKeyRestrictions extends AbstractRestriction implements PrimaryKeyRestrictions
+{
+ /**
+ * The composite type.
+ */
+ protected final CType ctype;
+
+ public AbstractPrimaryKeyRestrictions(CType ctype)
+ {
+ this.ctype = ctype;
+ }
+
+ @Override
- public List<ByteBuffer> bounds(Bound b, QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> bounds(CFMetaData cfm, Bound b, QueryOptions options) throws InvalidRequestException
+ {
- return values(options);
++ return values(cfm, options);
+ }
+
+ @Override
+ public final boolean isEmpty()
+ {
+ return getColumnDefs().isEmpty();
+ }
+
+ @Override
+ public final int size()
+ {
+ return getColumnDefs().size();
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
index 385bfbc,0000000..4093780
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
@@@ -1,113 -1,0 +1,119 @@@
+/*
+ * 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.restrictions;
+
+import java.nio.ByteBuffer;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+
+import org.apache.cassandra.cql3.ColumnSpecification;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.composites.CompositesBuilder;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkBindValueSet;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
+
+/**
+ * Base class for <code>Restriction</code>s
+ */
+abstract class AbstractRestriction implements Restriction
+{
+ @Override
+ public boolean isOnToken()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isMultiColumn()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isSlice()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isEQ()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isIN()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isContains()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean hasBound(Bound b)
+ {
+ return true;
+ }
+
+ @Override
- public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
++ public CompositesBuilder appendBoundTo(CFMetaData cfm, CompositesBuilder builder, Bound bound, QueryOptions options)
+ {
- return appendTo(builder, options);
++ return appendTo(cfm, builder, options);
+ }
+
+ @Override
+ public boolean isInclusive(Bound b)
+ {
+ return true;
++ }
++
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ return false;
+ }
+
+ protected static ByteBuffer validateIndexedValue(ColumnSpecification columnSpec,
+ ByteBuffer value)
+ throws InvalidRequestException
+ {
+ checkNotNull(value, "Unsupported null value for column %s", columnSpec.name);
+ checkBindValueSet(value, "Unsupported unset value for column %s", columnSpec.name);
+ checkFalse(value.remaining() > 0xFFFF, "Index expression values may not be larger than 64K");
+ return value;
+ }
+
+ /**
+ * Reverses the specified bound if the column type is a reversed one.
+ *
+ * @param columnDefinition the column definition
+ * @param bound the bound
+ * @return the bound reversed if the column type was a reversed one or the original bound
+ */
+ protected static Bound reverseBoundIfNeeded(ColumnDefinition columnDefinition, Bound bound)
+ {
+ return columnDefinition.isReversedType() ? bound.reverse() : bound;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
index 71855a0,0000000..76d0233
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
@@@ -1,185 -1,0 +1,192 @@@
+/*
+ * 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.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.db.composites.CompositesBuilder;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+/**
+ * A <code>PrimaryKeyRestrictions</code> which forwards all its method calls to another
+ * <code>PrimaryKeyRestrictions</code>. Subclasses should override one or more methods to modify the behavior
+ * of the backing <code>PrimaryKeyRestrictions</code> as desired per the decorator pattern.
+ */
+abstract class ForwardingPrimaryKeyRestrictions implements PrimaryKeyRestrictions
+{
+ /**
+ * Returns the backing delegate instance that methods are forwarded to.
+ * @return the backing delegate instance that methods are forwarded to.
+ */
+ protected abstract PrimaryKeyRestrictions getDelegate();
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return getDelegate().getFunctions();
+ }
+
+ @Override
+ public List<ColumnDefinition> getColumnDefs()
+ {
+ return getDelegate().getColumnDefs();
+ }
+
+ @Override
+ public ColumnDefinition getFirstColumn()
+ {
+ return getDelegate().getFirstColumn();
+ }
+
+ @Override
+ public ColumnDefinition getLastColumn()
+ {
+ return getDelegate().getLastColumn();
+ }
+
+ @Override
+ public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws InvalidRequestException
+ {
+ return getDelegate().mergeWith(restriction);
+ }
+
+ @Override
+ public boolean hasSupportingIndex(SecondaryIndexManager secondaryIndexManager)
+ {
+ return getDelegate().hasSupportingIndex(secondaryIndexManager);
+ }
+
+ @Override
- public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> values(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
- return getDelegate().values(options);
++ return getDelegate().values(cfm, options);
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
- return getDelegate().appendTo(builder, options);
++ return getDelegate().appendTo(cfm, builder, options);
+ }
+
+ @Override
- public List<Composite> valuesAsComposites(QueryOptions options) throws InvalidRequestException
++ public List<Composite> valuesAsComposites(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
- return getDelegate().valuesAsComposites(options);
++ return getDelegate().valuesAsComposites(cfm, options);
+ }
+
+ @Override
- public List<ByteBuffer> bounds(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> bounds(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
- return getDelegate().bounds(bound, options);
++ return getDelegate().bounds(cfm, bound, options);
+ }
+
+ @Override
- public List<Composite> boundsAsComposites(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<Composite> boundsAsComposites(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
- return getDelegate().boundsAsComposites(bound, options);
++ return getDelegate().boundsAsComposites(cfm, bound, options);
+ }
+
+ @Override
- public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
++ public CompositesBuilder appendBoundTo(CFMetaData cfm, CompositesBuilder builder, Bound bound, QueryOptions options)
+ {
- return getDelegate().appendBoundTo(builder, bound, options);
++ return getDelegate().appendBoundTo(cfm, builder, bound, options);
+ }
+
+ @Override
+ public boolean isInclusive(Bound bound)
+ {
+ return getDelegate().isInclusive(bound.reverse());
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return getDelegate().isEmpty();
+ }
+
+ @Override
+ public int size()
+ {
+ return getDelegate().size();
+ }
+
+ @Override
+ public boolean isOnToken()
+ {
+ return getDelegate().isOnToken();
+ }
+
+ @Override
+ public boolean isSlice()
+ {
+ return getDelegate().isSlice();
+ }
+
+ @Override
+ public boolean isEQ()
+ {
+ return getDelegate().isEQ();
+ }
+
+ @Override
+ public boolean isIN()
+ {
+ return getDelegate().isIN();
+ }
+
+ @Override
+ public boolean isContains()
+ {
+ return getDelegate().isContains();
+ }
+
+ @Override
+ public boolean isMultiColumn()
+ {
+ return getDelegate().isMultiColumn();
+ }
+
+ @Override
+ public boolean hasBound(Bound b)
+ {
+ return getDelegate().hasBound(b);
+ }
+
+ @Override
+ public void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ getDelegate().addIndexExpressionTo(expressions, indexManager, options);
+ }
++
++ @Override
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ return getDelegate().isNotReturningAnyRows(cfm, options);
++ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
index 51e2ce4,0000000..44f25ec
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
@@@ -1,515 -1,0 +1,553 @@@
+/*
+ * 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.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.Term.Terminal;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.composites.CompositesBuilder;
+import org.apache.cassandra.db.index.SecondaryIndex;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
+import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
+
+public abstract class MultiColumnRestriction extends AbstractRestriction
+{
+ /**
+ * The columns to which the restriction apply.
+ */
+ protected final List<ColumnDefinition> columnDefs;
+
+ public MultiColumnRestriction(List<ColumnDefinition> columnDefs)
+ {
+ this.columnDefs = columnDefs;
+ }
+
+ @Override
+ public boolean isMultiColumn()
+ {
+ return true;
+ }
+
+ @Override
+ public ColumnDefinition getFirstColumn()
+ {
+ return columnDefs.get(0);
+ }
+
+ @Override
+ public ColumnDefinition getLastColumn()
+ {
+ return columnDefs.get(columnDefs.size() - 1);
+ }
+
+ @Override
+ public List<ColumnDefinition> getColumnDefs()
+ {
+ return columnDefs;
+ }
+
+ @Override
+ public final Restriction mergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ // We want to allow query like: (b,c) > (?, ?) AND b < ?
+ if (!otherRestriction.isMultiColumn()
+ && ((SingleColumnRestriction) otherRestriction).canBeConvertedToMultiColumnRestriction())
+ {
+ return doMergeWith(((SingleColumnRestriction) otherRestriction).toMultiColumnRestriction());
+ }
+
+ return doMergeWith(otherRestriction);
+ }
+
+ protected abstract Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException;
+
+ /**
+ * Returns the names of the columns that are specified within this <code>Restrictions</code> and the other one
+ * as a comma separated <code>String</code>.
+ *
+ * @param otherRestriction the other restrictions
+ * @return the names of the columns that are specified within this <code>Restrictions</code> and the other one
+ * as a comma separated <code>String</code>.
+ */
+ protected final String getColumnsInCommons(Restriction otherRestriction)
+ {
+ Set<ColumnDefinition> commons = new HashSet<>(getColumnDefs());
+ commons.retainAll(otherRestriction.getColumnDefs());
+ StringBuilder builder = new StringBuilder();
+ for (ColumnDefinition columnDefinition : commons)
+ {
+ if (builder.length() != 0)
+ builder.append(" ,");
+ builder.append(columnDefinition.name);
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public final boolean hasSupportingIndex(SecondaryIndexManager indexManager)
+ {
+ for (ColumnDefinition columnDef : columnDefs)
+ {
+ SecondaryIndex index = indexManager.getIndexForColumn(columnDef.name.bytes);
+ if (index != null && isSupportedBy(index))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if this type of restriction is supported for by the specified index.
+ * @param index the Secondary index
+ *
+ * @return <code>true</code> this type of restriction is supported by the specified index,
+ * <code>false</code> otherwise.
+ */
+ protected abstract boolean isSupportedBy(SecondaryIndex index);
+
+ public static class EQ extends MultiColumnRestriction
+ {
+ protected final Term value;
+
+ public EQ(List<ColumnDefinition> columnDefs, Term value)
+ {
+ super(columnDefs);
+ this.value = value;
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return value.getFunctions();
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("EQ(%s)", value);
+ }
+
+ @Override
+ public Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ throw invalidRequest("%s cannot be restricted by more than one relation if it includes an Equal",
+ getColumnsInCommons(otherRestriction));
+ }
+
+ @Override
+ protected boolean isSupportedBy(SecondaryIndex index)
+ {
+ return index.supportsOperator(Operator.EQ);
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
+ Tuples.Value t = ((Tuples.Value) value.bind(options));
+ List<ByteBuffer> values = t.getElements();
+ for (int i = 0, m = values.size(); i < m; i++)
+ {
+ builder.addElementToAll(values.get(i));
+ checkFalse(builder.containsNull(), "Invalid null value for column %s", columnDefs.get(i).name);
+ }
+ return builder;
+ }
+
+ @Override
+ public final void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ Tuples.Value t = ((Tuples.Value) value.bind(options));
+ List<ByteBuffer> values = t.getElements();
+
+ for (int i = 0, m = columnDefs.size(); i < m; i++)
+ {
+ ColumnDefinition columnDef = columnDefs.get(i);
+ ByteBuffer component = validateIndexedValue(columnDef, values.get(i));
+ expressions.add(new IndexExpression(columnDef.name.bytes, Operator.EQ, component));
+ }
+ }
++
++ @Override
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that
++ // any query with an EQ restriction containing an empty value will not return any results.
++ return !cfm.comparator.isCompound()
++ && !((Tuples.Value) value.bind(options)).getElements().get(0).hasRemaining();
++ }
+ }
+
+ public abstract static class IN extends MultiColumnRestriction
+ {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
- List<List<ByteBuffer>> splitInValues = splitValues(options);
++ List<List<ByteBuffer>> splitInValues = filterValuesIfNeeded(cfm, splitValues(options));
+ builder.addAllElementsToAll(splitInValues);
+
+ if (builder.containsNull())
+ throw invalidRequest("Invalid null value in condition for columns: %s", ColumnDefinition.toIdentifiers(columnDefs));
+ return builder;
+ }
+
++ private List<List<ByteBuffer>> filterValuesIfNeeded(CFMetaData cfm, List<List<ByteBuffer>> splitInValues)
++ {
++ if (cfm.comparator.isCompound())
++ return splitInValues;
++
++ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that we can
++ // ignore any IN value which is an empty byte buffer an which otherwise will trigger an error.
++
++ // As some List implementations do not support remove, we copy the list to be on the safe side.
++ List<List<ByteBuffer>> filteredValues = new ArrayList<>(splitInValues.size());
++ for (List<ByteBuffer> values : splitInValues)
++ {
++ if (values.get(0).hasRemaining())
++ filteredValues.add(values);
++ }
++ return filteredValues;
++ }
++
+ public IN(List<ColumnDefinition> columnDefs)
+ {
+ super(columnDefs);
+ }
+
+ @Override
+ public boolean isIN()
+ {
+ return true;
+ }
+
+ @Override
+ public Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ throw invalidRequest("%s cannot be restricted by more than one relation if it includes a IN",
+ getColumnsInCommons(otherRestriction));
+ }
+
+ @Override
+ protected boolean isSupportedBy(SecondaryIndex index)
+ {
+ return index.supportsOperator(Operator.IN);
+ }
+
+ @Override
+ public final void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ List<List<ByteBuffer>> splitInValues = splitValues(options);
+ checkTrue(splitInValues.size() == 1, "IN restrictions are not supported on indexed columns");
+ List<ByteBuffer> values = splitInValues.get(0);
+
+ for (int i = 0, m = columnDefs.size(); i < m; i++)
+ {
+ ColumnDefinition columnDef = columnDefs.get(i);
+ ByteBuffer component = validateIndexedValue(columnDef, values.get(i));
+ expressions.add(new IndexExpression(columnDef.name.bytes, Operator.EQ, component));
+ }
+ }
+
+ protected abstract List<List<ByteBuffer>> splitValues(QueryOptions options) throws InvalidRequestException;
+ }
+
+ /**
+ * An IN restriction that has a set of terms for in values.
+ * For example: "SELECT ... WHERE (a, b, c) IN ((1, 2, 3), (4, 5, 6))" or "WHERE (a, b, c) IN (?, ?)"
+ */
+ public static class InWithValues extends MultiColumnRestriction.IN
+ {
+ protected final List<Term> values;
+
+ public InWithValues(List<ColumnDefinition> columnDefs, List<Term> values)
+ {
+ super(columnDefs);
+ this.values = values;
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return Terms.getFunctions(values);
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("IN(%s)", values);
+ }
+
+ @Override
+ protected List<List<ByteBuffer>> splitValues(QueryOptions options) throws InvalidRequestException
+ {
+ List<List<ByteBuffer>> buffers = new ArrayList<>(values.size());
+ for (Term value : values)
+ {
+ Term.MultiItemTerminal term = (Term.MultiItemTerminal) value.bind(options);
+ buffers.add(term.getElements());
+ }
+ return buffers;
+ }
+ }
+
+ /**
+ * An IN restriction that uses a single marker for a set of IN values that are tuples.
+ * For example: "SELECT ... WHERE (a, b, c) IN ?"
+ */
+ public static class InWithMarker extends MultiColumnRestriction.IN
+ {
+ protected final AbstractMarker marker;
+
+ public InWithMarker(List<ColumnDefinition> columnDefs, AbstractMarker marker)
+ {
+ super(columnDefs);
+ this.marker = marker;
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public String toString()
+ {
+ return "IN ?";
+ }
+
+ @Override
+ protected List<List<ByteBuffer>> splitValues(QueryOptions options) throws InvalidRequestException
+ {
+ Tuples.InMarker inMarker = (Tuples.InMarker) marker;
+ Tuples.InValue inValue = inMarker.bind(options);
+ checkNotNull(inValue, "Invalid null value for IN restriction");
+ return inValue.getSplitValues();
+ }
+ }
+
+ public static class Slice extends MultiColumnRestriction
+ {
+ private final TermSlice slice;
+
+ public Slice(List<ColumnDefinition> columnDefs, Bound bound, boolean inclusive, Term term)
+ {
+ this(columnDefs, TermSlice.newInstance(bound, inclusive, term));
+ }
+
+ Slice(List<ColumnDefinition> columnDefs, TermSlice slice)
+ {
+ super(columnDefs);
+ this.slice = slice;
+ }
+
+ @Override
+ public boolean isSlice()
+ {
+ return true;
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
- public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
++ public CompositesBuilder appendBoundTo(CFMetaData cfm, CompositesBuilder builder, Bound bound, QueryOptions options)
+ {
+ boolean reversed = getFirstColumn().isReversedType();
+
+ EnumMap<Bound, List<ByteBuffer>> componentBounds = new EnumMap<Bound, List<ByteBuffer>>(Bound.class);
+ componentBounds.put(Bound.START, componentBounds(Bound.START, options));
+ componentBounds.put(Bound.END, componentBounds(Bound.END, options));
+
+ List<List<ByteBuffer>> toAdd = new ArrayList<>();
+ List<ByteBuffer> values = new ArrayList<>();
+
+ for (int i = 0, m = columnDefs.size(); i < m; i++)
+ {
+ ColumnDefinition column = columnDefs.get(i);
+ Bound b = reverseBoundIfNeeded(column, bound);
+
+ // For mixed order columns, we need to create additional slices when 2 columns are in reverse order
+ if (reversed != column.isReversedType())
+ {
+ reversed = column.isReversedType();
+ // As we are switching direction we need to add the current composite
+ toAdd.add(values);
+
+ // The new bound side has no value for this component. just stop
+ if (!hasComponent(b, i, componentBounds))
+ continue;
+
+ // The other side has still some components. We need to end the slice that we have just open.
+ if (hasComponent(b.reverse(), i, componentBounds))
+ toAdd.add(values);
+
+ // We need to rebuild where we are in this bound side
+ values = new ArrayList<ByteBuffer>();
+
+ List<ByteBuffer> vals = componentBounds.get(b);
+
+ int n = Math.min(i, vals.size());
+ for (int j = 0; j < n; j++)
+ {
+ ByteBuffer v = checkNotNull(vals.get(j),
+ "Invalid null value in condition for column %s",
+ columnDefs.get(j).name);
+ values.add(v);
+ }
+ }
+
+ if (!hasComponent(b, i, componentBounds))
+ continue;
+
+ ByteBuffer v = checkNotNull(componentBounds.get(b).get(i), "Invalid null value in condition for column %s", columnDefs.get(i).name);
+ values.add(v);
+ }
+ toAdd.add(values);
+
+ if (bound.isEnd())
+ Collections.reverse(toAdd);
+
+ return builder.addAllElementsToAll(toAdd);
+ }
+
+ @Override
+ protected boolean isSupportedBy(SecondaryIndex index)
+ {
+ return slice.isSupportedBy(index);
+ }
+
+ @Override
+ public boolean hasBound(Bound bound)
+ {
+ return slice.hasBound(bound);
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return slice.getFunctions();
+ }
+
+ @Override
+ public boolean isInclusive(Bound bound)
+ {
+ return slice.isInclusive(bound);
+ }
+
+ @Override
+ public Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ checkTrue(otherRestriction.isSlice(),
+ "Column \"%s\" cannot be restricted by both an equality and an inequality relation",
+ getColumnsInCommons(otherRestriction));
+
+ if (!getFirstColumn().equals(otherRestriction.getFirstColumn()))
+ {
+ ColumnDefinition column = getFirstColumn().position() > otherRestriction.getFirstColumn().position()
+ ? getFirstColumn() : otherRestriction.getFirstColumn();
+
+ throw invalidRequest("Column \"%s\" cannot be restricted by two inequalities not starting with the same column",
+ column.name);
+ }
+
+ checkFalse(hasBound(Bound.START) && otherRestriction.hasBound(Bound.START),
+ "More than one restriction was found for the start bound on %s",
+ getColumnsInCommons(otherRestriction));
+ checkFalse(hasBound(Bound.END) && otherRestriction.hasBound(Bound.END),
+ "More than one restriction was found for the end bound on %s",
+ getColumnsInCommons(otherRestriction));
+
+ Slice otherSlice = (Slice) otherRestriction;
+ List<ColumnDefinition> newColumnDefs = columnDefs.size() >= otherSlice.columnDefs.size() ? columnDefs : otherSlice.columnDefs;
+
+ return new Slice(newColumnDefs, slice.merge(otherSlice.slice));
+ }
+
+ @Override
+ public final void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ throw invalidRequest("Multi-column slice restrictions cannot be used for filtering.");
+ }
+
+ @Override
+ public String toString()
+ {
+ return "SLICE" + slice;
+ }
+
+ /**
+ * Similar to bounds(), but returns one ByteBuffer per-component in the bound instead of a single
+ * ByteBuffer to represent the entire bound.
+ * @param b the bound type
+ * @param options the query options
+ * @return one ByteBuffer per-component in the bound
+ * @throws InvalidRequestException if the components cannot be retrieved
+ */
+ private List<ByteBuffer> componentBounds(Bound b, QueryOptions options) throws InvalidRequestException
+ {
+ if (!slice.hasBound(b))
+ return Collections.emptyList();
+
+ Terminal terminal = slice.bound(b).bind(options);
+
+ if (terminal instanceof Tuples.Value)
+ {
+ return ((Tuples.Value) terminal).getElements();
+ }
+
+ return Collections.singletonList(terminal.get(options.getProtocolVersion()));
+ }
+
++ @Override
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that
++ // any query with a slice restriction with an empty value for the END bound will not return any results.
++ return !cfm.comparator.isCompound()
++ && hasBound(Bound.END)
++ && !componentBounds(Bound.END, options).get(0).hasRemaining();
++ }
++
+ private boolean hasComponent(Bound b, int index, EnumMap<Bound, List<ByteBuffer>> componentBounds)
+ {
+ return componentBounds.get(b).size() > index;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
index e1cbc29,0000000..2549bdf
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
@@@ -1,405 -1,0 +1,417 @@@
+/*
+ * 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.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+import com.google.common.collect.Lists;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.composites.*;
+import org.apache.cassandra.db.composites.Composite.EOC;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+/**
+ * A set of single column restrictions on a primary key part (partition key or clustering key).
+ */
+final class PrimaryKeyRestrictionSet extends AbstractPrimaryKeyRestrictions implements Iterable<Restriction>
+{
+ /**
+ * The restrictions.
+ */
+ private final RestrictionSet restrictions;
+
+ /**
+ * <code>true</code> if the restrictions are corresponding to an EQ, <code>false</code> otherwise.
+ */
+ private boolean eq;
+
+ /**
+ * <code>true</code> if the restrictions are corresponding to an IN, <code>false</code> otherwise.
+ */
+ private boolean in;
+
+ /**
+ * <code>true</code> if the restrictions are corresponding to a Slice, <code>false</code> otherwise.
+ */
+ private boolean slice;
+
+ /**
+ * <code>true</code> if the restrictions are corresponding to a Contains, <code>false</code> otherwise.
+ */
+ private boolean contains;
+
+ public PrimaryKeyRestrictionSet(CType ctype)
+ {
+ super(ctype);
+ this.restrictions = new RestrictionSet();
+ this.eq = true;
+ }
+
+ private PrimaryKeyRestrictionSet(PrimaryKeyRestrictionSet primaryKeyRestrictions,
+ Restriction restriction) throws InvalidRequestException
+ {
+ super(primaryKeyRestrictions.ctype);
+ this.restrictions = primaryKeyRestrictions.restrictions.addRestriction(restriction);
+
+ if (restriction.isSlice() || primaryKeyRestrictions.isSlice())
+ this.slice = true;
+ else if (restriction.isContains() || primaryKeyRestrictions.isContains())
+ this.contains = true;
+ else if (restriction.isIN() || primaryKeyRestrictions.isIN())
+ this.in = true;
+ else
+ this.eq = true;
+ }
+
+ @Override
+ public boolean isSlice()
+ {
+ return slice;
+ }
+
+ @Override
+ public boolean isEQ()
+ {
+ return eq;
+ }
+
+ @Override
+ public boolean isIN()
+ {
+ return in;
+ }
+
+ @Override
+ public boolean isOnToken()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isContains()
+ {
+ return contains;
+ }
+
+ @Override
+ public boolean isMultiColumn()
+ {
+ return false;
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return restrictions.getFunctions();
+ }
+
+ @Override
+ public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws InvalidRequestException
+ {
+ if (restriction.isOnToken())
+ {
+ if (isEmpty())
+ return (PrimaryKeyRestrictions) restriction;
+
+ return new TokenFilter(this, (TokenRestriction) restriction);
+ }
+
+ return new PrimaryKeyRestrictionSet(this, restriction);
+ }
+
+ @Override
- public List<Composite> valuesAsComposites(QueryOptions options) throws InvalidRequestException
++ public List<Composite> valuesAsComposites(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
- return filterAndSort(appendTo(new CompositesBuilder(ctype), options).build());
++ return filterAndSort(appendTo(cfm, new CompositesBuilder(ctype), options).build());
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
+ for (Restriction r : restrictions)
+ {
- r.appendTo(builder, options);
++ r.appendTo(cfm, builder, options);
+ if (builder.hasMissingElements())
+ break;
+ }
+ return builder;
+ }
+
+ @Override
- public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
++ public CompositesBuilder appendBoundTo(CFMetaData cfm, CompositesBuilder builder, Bound bound, QueryOptions options)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
- public List<Composite> boundsAsComposites(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<Composite> boundsAsComposites(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
+ CompositesBuilder builder = new CompositesBuilder(ctype);
+ // The end-of-component of composite doesn't depend on whether the
+ // component type is reversed or not (i.e. the ReversedType is applied
+ // to the component comparator but not to the end-of-component itself),
+ // it only depends on whether the slice is reversed
+ int keyPosition = 0;
+ for (Restriction r : restrictions)
+ {
+ ColumnDefinition def = r.getFirstColumn();
+
+ if (keyPosition != def.position() || r.isContains())
+ break;
+
+ if (r.isSlice())
+ {
- r.appendBoundTo(builder, bound, options);
++ r.appendBoundTo(cfm, builder, bound, options);
+
+ // Since CASSANDRA-7281, the composites might not end with the same components and it is possible
+ // that one of the composites is an empty one. Unfortunatly, AbstractCType will always sort
+ // Composites.EMPTY before all the other components due to its EOC, even if it is not the desired
+ // behaviour in some cases. To avoid that problem the code will use normal composites for the empty
+ // ones until the composites are properly sorted. They will then be replaced by Composites.EMPTY as
+ // it is what is expected by the intra-node serialization.
+ // It is clearly a hack but it does not make a lot of sense to refactor 2.2 for that as the problem is
+ // already solved in 3.0.
+ List<Composite> composites = filterAndSort(setEocs(r, bound, builder.build()));
+ return Lists.transform(composites, new com.google.common.base.Function<Composite, Composite>()
+ {
+ @Override
+ public Composite apply(Composite composite)
+ {
+ return composite.isEmpty() ? Composites.EMPTY: composite;
+ }
+ });
+ }
+
- r.appendBoundTo(builder, bound, options);
++ r.appendBoundTo(cfm, builder, bound, options);
+
+ if (builder.hasMissingElements())
+ return Collections.emptyList();
+
+ keyPosition = r.getLastColumn().position() + 1;
+ }
+ // Means no relation at all or everything was an equal
+ // Note: if the builder is "full", there is no need to use the end-of-component bit. For columns selection,
+ // it would be harmless to do it. However, we use this method got the partition key too. And when a query
+ // with 2ndary index is done, and with the the partition provided with an EQ, we'll end up here, and in that
+ // case using the eoc would be bad, since for the random partitioner we have no guarantee that
+ // prefix.end() will sort after prefix (see #5240).
+ EOC eoc = !builder.hasRemaining() ? EOC.NONE : (bound.isEnd() ? EOC.END : EOC.START);
+ return filterAndSort(builder.buildWithEOC(eoc));
+ }
+
+ /**
+ * Removes duplicates and sort the specified composites.
+ *
+ * @param composites the composites to filter and sort
+ * @return the composites sorted and without duplicates
+ */
+ private List<Composite> filterAndSort(List<Composite> composites)
+ {
+ if (composites.size() <= 1)
+ return composites;
+
+ TreeSet<Composite> set = new TreeSet<Composite>(ctype);
+ set.addAll(composites);
+
+ return new ArrayList<>(set);
+ }
+
+ /**
+ * Sets EOCs for the composites returned by the specified slice restriction for the given bound.
+ *
+ * @param r the slice restriction
+ * @param bound the bound
+ * @param composites the composites
+ * @return the composites with their EOCs properly set
+ */
+ private List<Composite> setEocs(Restriction r, Bound bound, List<Composite> composites)
+ {
+ List<Composite> list = new ArrayList<>(composites.size());
+
+ // The first column of the slice might not be the first clustering column (e.g. clustering_0 = ? AND (clustering_1, clustering_2) >= (?, ?)
+ int offset = r.getFirstColumn().position();
+
+ for (int i = 0, m = composites.size(); i < m; i++)
+ {
+ Composite composite = composites.get(i);
+
+ // Handle the no bound case
+ if (composite.size() == offset)
+ {
+ list.add(composite.withEOC(bound.isEnd() ? EOC.END : EOC.START));
+ continue;
+ }
+
+ // In the case of mixed order columns, we will have some extra slices where the columns change directions.
+ // For example: if we have clustering_0 DESC and clustering_1 ASC a slice like (clustering_0, clustering_1) > (1, 2)
+ // will produce 2 slices: [EMPTY, 1.START] and [1.2.END, 1.END]
+ // So, the END bound will return 2 composite with the same values 1
+ if (composite.size() <= r.getLastColumn().position() && i < m - 1 && composite.equals(composites.get(i + 1)))
+ {
+ list.add(composite.withEOC(EOC.START));
+ list.add(composites.get(i++).withEOC(EOC.END));
+ continue;
+ }
+
+ // Handle the normal bounds
+ ColumnDefinition column = r.getColumnDefs().get(composite.size() - 1 - offset);
+ Bound b = reverseBoundIfNeeded(column, bound);
+
+ Composite.EOC eoc = eocFor(r, bound, b);
+ list.add(composite.withEOC(eoc));
+ }
+
+ return list;
+ }
+
+ @Override
- public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> values(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
- return Composites.toByteBuffers(valuesAsComposites(options));
++ return Composites.toByteBuffers(valuesAsComposites(cfm, options));
+ }
+
+ @Override
- public List<ByteBuffer> bounds(Bound b, QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> bounds(CFMetaData cfm, Bound b, QueryOptions options) throws InvalidRequestException
+ {
- return Composites.toByteBuffers(boundsAsComposites(b, options));
++ return Composites.toByteBuffers(boundsAsComposites(cfm, b, options));
+ }
+
+ private static Composite.EOC eocFor(Restriction r, Bound eocBound, Bound inclusiveBound)
+ {
+ if (eocBound.isStart())
+ return r.isInclusive(inclusiveBound) ? Composite.EOC.NONE : Composite.EOC.END;
+
+ return r.isInclusive(inclusiveBound) ? Composite.EOC.END : Composite.EOC.START;
+ }
+
+ @Override
+ public boolean hasBound(Bound b)
+ {
+ if (isEmpty())
+ return false;
+ return restrictions.lastRestriction().hasBound(b);
+ }
+
+ @Override
+ public boolean isInclusive(Bound b)
+ {
+ if (isEmpty())
+ return false;
+ return restrictions.lastRestriction().isInclusive(b);
+ }
+
+ @Override
+ public boolean hasSupportingIndex(SecondaryIndexManager indexManager)
+ {
+ return restrictions.hasSupportingIndex(indexManager);
+ }
+
+ @Override
+ public void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ Boolean clusteringColumns = null;
+ int position = 0;
+
+ for (Restriction restriction : restrictions)
+ {
+ ColumnDefinition columnDef = restriction.getFirstColumn();
+
+ // PrimaryKeyRestrictionSet contains only one kind of column, either partition key or clustering columns.
+ // Therefore we only need to check the column kind once. All the other columns will be of the same kind.
+ if (clusteringColumns == null)
+ clusteringColumns = columnDef.isClusteringColumn() ? Boolean.TRUE : Boolean.FALSE;
+
+ // We ignore all the clustering columns that can be handled by slices.
+ if (!clusteringColumns || handleInFilter(restriction, position) || restriction.hasSupportingIndex(indexManager))
+ {
+ restriction.addIndexExpressionTo(expressions, indexManager, options);
+ continue;
+ }
+
+ if (!restriction.isSlice())
+ position = restriction.getLastColumn().position() + 1;
+ }
+ }
+
+ @Override
+ public List<ColumnDefinition> getColumnDefs()
+ {
+ return restrictions.getColumnDefs();
+ }
+
+ @Override
+ public ColumnDefinition getFirstColumn()
+ {
+ return restrictions.firstColumn();
+ }
+
+ @Override
+ public ColumnDefinition getLastColumn()
+ {
+ return restrictions.lastColumn();
+ }
+
+ public final boolean needsFiltering()
+ {
+ // Backported from ClusteringColumnRestrictions from CASSANDRA-11310 for 3.6
+ // As that suggests, this should only be called on clustering column
+ // and not partition key restrictions.
+ int position = 0;
+ for (Restriction restriction : restrictions)
+ {
+ if (handleInFilter(restriction, position))
+ return true;
+
+ if (!restriction.isSlice())
+ position = restriction.getLastColumn().position() + 1;
+ }
+
+ return false;
+ }
+
++ @Override
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ for (Restriction restriction : restrictions)
++ {
++ if (restriction.isNotReturningAnyRows(cfm, options))
++ return true;
++ }
++ return false;
++ }
++
+ private boolean handleInFilter(Restriction restriction, int index)
+ {
+ return restriction.isContains() || index != restriction.getFirstColumn().position();
+ }
+
+ public Iterator<Restriction> iterator()
+ {
+ return restrictions.iterator();
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictions.java
index 7d7b492,0000000..5e5e3f5
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictions.java
@@@ -1,44 -1,0 +1,45 @@@
+/*
+ * 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.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+/**
+ * A set of restrictions on a primary key part (partition key or clustering key).
+ *
+ */
+interface PrimaryKeyRestrictions extends Restriction, Restrictions
+{
+ @Override
+ public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws InvalidRequestException;
+
- public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException;
++ public List<ByteBuffer> values(CFMetaData cfm, QueryOptions options) throws InvalidRequestException;
+
- public List<Composite> valuesAsComposites(QueryOptions options) throws InvalidRequestException;
++ public List<Composite> valuesAsComposites(CFMetaData cfm, QueryOptions options) throws InvalidRequestException;
+
- public List<ByteBuffer> bounds(Bound b, QueryOptions options) throws InvalidRequestException;
++ public List<ByteBuffer> bounds(CFMetaData cfm, Bound b, QueryOptions options) throws InvalidRequestException;
+
- public List<Composite> boundsAsComposites(Bound bound, QueryOptions options) throws InvalidRequestException;
++ public List<Composite> boundsAsComposites(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException;
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
index f0ea6a7,0000000..9df100a
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
@@@ -1,138 -1,0 +1,150 @@@
+/*
+ * 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.restrictions;
+
+import java.util.List;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.composites.CompositesBuilder;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+/**
+ * A restriction/clause on a column.
+ * The goal of this class being to group all conditions for a column in a SELECT.
+ *
+ * <p>Implementation of this class must be immutable. See {@link #mergeWith(Restriction)} for more explanation.</p>
+ */
+public interface Restriction
+{
+ public boolean isOnToken();
+ public boolean isSlice();
+ public boolean isEQ();
+ public boolean isIN();
+ public boolean isContains();
+ public boolean isMultiColumn();
+
+ /**
+ * Returns the definition of the first column.
+ * @return the definition of the first column.
+ */
+ public ColumnDefinition getFirstColumn();
+
+ /**
+ * Returns the definition of the last column.
+ * @return the definition of the last column.
+ */
+ public ColumnDefinition getLastColumn();
+
+ /**
+ * Returns the column definitions in position order.
+ * @return the column definitions in position order.
+ */
+ public List<ColumnDefinition> getColumnDefs();
+
+ /**
+ * Return an Iterable over all of the functions (both native and user-defined) used by any component
+ * of the restriction
+ * @return functions all functions found (may contain duplicates)
+ */
+ public Iterable<Function> getFunctions();
+
+ /**
+ * Checks if the specified bound is set or not.
+ * @param b the bound type
+ * @return <code>true</code> if the specified bound is set, <code>false</code> otherwise
+ */
+ public boolean hasBound(Bound b);
+
+ /**
+ * Checks if the specified bound is inclusive or not.
+ * @param b the bound type
+ * @return <code>true</code> if the specified bound is inclusive, <code>false</code> otherwise
+ */
+ public boolean isInclusive(Bound b);
+
+ /**
+ * Merges this restriction with the specified one.
+ *
+ * <p>Restriction are immutable. Therefore merging two restrictions result in a new one.
+ * The reason behind this choice is that it allow a great flexibility in the way the merging can done while
+ * preventing any side effect.</p>
+ *
+ * @param otherRestriction the restriction to merge into this one
+ * @return the restriction resulting of the merge
+ * @throws InvalidRequestException if the restrictions cannot be merged
+ */
+ public Restriction mergeWith(Restriction otherRestriction) throws InvalidRequestException;
+
+ /**
+ * Check if the restriction is on indexed columns.
+ *
+ * @param indexManager the index manager
+ * @return <code>true</code> if the restriction is on indexed columns, <code>false</code>
+ */
+ public boolean hasSupportingIndex(SecondaryIndexManager indexManager);
+
+ /**
+ * Adds to the specified list the <code>IndexExpression</code>s corresponding to this <code>Restriction</code>.
+ *
+ * @param expressions the list to add the <code>IndexExpression</code>s to
+ * @param indexManager the secondary index manager
+ * @param options the query options
+ * @throws InvalidRequestException if this <code>Restriction</code> cannot be converted into
+ * <code>IndexExpression</code>s
+ */
+ public void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options)
+ throws InvalidRequestException;
+
+ /**
+ * Appends the values of this <code>Restriction</code> to the specified builder.
+ *
++ * @param cfm the table metadata
+ * @param builder the <code>CompositesBuilder</code> to append to.
+ * @param options the query options
+ * @return the <code>CompositesBuilder</code>
+ */
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options);
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options);
+
+ /**
+ * Appends the values of the <code>Restriction</code> for the specified bound to the specified builder.
+ *
++ * @param cfm the table metadata
+ * @param builder the <code>CompositesBuilder</code> to append to.
+ * @param bound the bound
+ * @param options the query options
+ * @return the <code>CompositesBuilder</code>
+ */
- public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options);
++ public CompositesBuilder appendBoundTo(CFMetaData cfm, CompositesBuilder builder, Bound bound, QueryOptions options);
++
++ /**
++ * Checks if this restriction will prevent the query to return any rows.
++ *
++ * @param cfm the table metadata
++ * @param options the query options
++ * @return {@code true} if this restriction will prevent the query to return any rows, {@false} otherwise
++ */
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options);
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/ReversedPrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/ReversedPrimaryKeyRestrictions.java
index 9b33161,0000000..9107acd
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/ReversedPrimaryKeyRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/ReversedPrimaryKeyRestrictions.java
@@@ -1,77 -1,0 +1,78 @@@
+/*
+ * 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.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.List;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+/**
+ * <code>PrimaryKeyRestrictions</code> decorator that reverse the slices.
+ */
+final class ReversedPrimaryKeyRestrictions extends ForwardingPrimaryKeyRestrictions
+{
+ /**
+ * The decorated restrictions.
+ */
+ private PrimaryKeyRestrictions restrictions;
+
+ public ReversedPrimaryKeyRestrictions(PrimaryKeyRestrictions restrictions)
+ {
+ this.restrictions = restrictions;
+ }
+
+ @Override
+ public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws InvalidRequestException
+ {
+ return new ReversedPrimaryKeyRestrictions(this.restrictions.mergeWith(restriction));
+ }
+
+ @Override
- public List<ByteBuffer> bounds(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> bounds(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
- List<ByteBuffer> buffers = restrictions.bounds(bound.reverse(), options);
++ List<ByteBuffer> buffers = restrictions.bounds(cfm, bound.reverse(), options);
+ Collections.reverse(buffers);
+ return buffers;
+ }
+
+ @Override
- public List<Composite> boundsAsComposites(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<Composite> boundsAsComposites(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
- List<Composite> composites = restrictions.boundsAsComposites(bound.reverse(), options);
++ List<Composite> composites = restrictions.boundsAsComposites(cfm, bound.reverse(), options);
+ Collections.reverse(composites);
+ return composites;
+ }
+
+ @Override
+ public boolean isInclusive(Bound bound)
+ {
+ return this.restrictions.isInclusive(bound.reverse());
+ }
+
+ @Override
+ protected PrimaryKeyRestrictions getDelegate()
+ {
+ return this.restrictions;
+ }
+}
[08/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.8'
into cassandra-3.9
Posted by ms...@apache.org.
Merge branch 'cassandra-3.8' into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/c3e636c5
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/c3e636c5
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/c3e636c5
Branch: refs/heads/cassandra-3.8
Commit: c3e636c528a09371255d460ad6a91eb0c1d47875
Parents: 2d769f0 b188e08
Author: Tyler Hobbs <ty...@gmail.com>
Authored: Wed Aug 10 11:12:33 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Wed Aug 10 11:12:33 2016 -0500
----------------------------------------------------------------------
----------------------------------------------------------------------
[47/50] [abbrv] cassandra git commit: Merge branch cassandra-2.2 into
cassandra-3.0
Posted by ms...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
index 814e822,6bd5f26..b9ccafb
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
@@@ -26,8 -28,10 +28,11 @@@ import org.apache.commons.lang3.StringU
import org.junit.Test;
import org.apache.cassandra.cql3.CQLTester;
+
-import static org.junit.Assert.assertEquals;
+ import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
+ import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+import static org.apache.commons.lang3.StringUtils.isEmpty;
+import static org.junit.Assert.assertEquals;
public class DeleteTest extends CQLTester
{
@@@ -326,761 -330,42 +331,869 @@@
assertEmpty(execute("select * from %s where a=1 and b=1"));
}
+ @Test
+ public void testDeleteWithNoClusteringColumns() throws Throwable
+ {
+ testDeleteWithNoClusteringColumns(false);
+ testDeleteWithNoClusteringColumns(true);
+ }
+
+ private void testDeleteWithNoClusteringColumns(boolean forceFlush) throws Throwable
+ {
+ for (String compactOption : new String[] {"", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (partitionKey int PRIMARY KEY," +
+ "value int)" + compactOption);
+
+ execute("INSERT INTO %s (partitionKey, value) VALUES (0, 0)");
+ execute("INSERT INTO %s (partitionKey, value) VALUES (1, 1)");
+ execute("INSERT INTO %s (partitionKey, value) VALUES (2, 2)");
+ execute("INSERT INTO %s (partitionKey, value) VALUES (3, 3)");
+ flush(forceFlush);
+
+ execute("DELETE value FROM %s WHERE partitionKey = ?", 0);
+ flush(forceFlush);
+
+ if (isEmpty(compactOption))
+ {
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, null));
+ }
+ else
+ {
+ assertEmpty(execute("SELECT * FROM %s WHERE partitionKey = ?", 0));
+ }
+
+ execute("DELETE FROM %s WHERE partitionKey IN (?, ?)", 0, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s"),
+ row(2, 2),
+ row(3, 3));
+
+ // test invalid queries
+
+ // token function
+ assertInvalidMessage("The token function cannot be used in WHERE clauses for DELETE statements",
+ "DELETE FROM %s WHERE token(partitionKey) = token(?)", 0);
+
+ // multiple time same primary key element in WHERE clause
+ assertInvalidMessage("partitionkey cannot be restricted by more than one relation if it includes an Equal",
+ "DELETE FROM %s WHERE partitionKey = ? AND partitionKey = ?", 0, 1);
+
+ // unknown identifiers
+ assertInvalidMessage("Unknown identifier unknown",
+ "DELETE unknown FROM %s WHERE partitionKey = ?", 0);
+
+ assertInvalidMessage("Undefined name partitionkey1 in where clause ('partitionkey1 = ?')",
+ "DELETE FROM %s WHERE partitionKey1 = ?", 0);
+
+ // Invalid operator in the where clause
+ assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)",
+ "DELETE FROM %s WHERE partitionKey > ? ", 0);
+
+ assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey",
+ "DELETE FROM %s WHERE partitionKey CONTAINS ?", 0);
+
+ // Non primary key in the where clause
+ assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value",
+ "DELETE FROM %s WHERE partitionKey = ? AND value = ?", 0, 1);
+ }
+ }
+
+ @Test
+ public void testDeleteWithOneClusteringColumns() throws Throwable
+ {
+ testDeleteWithOneClusteringColumns(false);
+ testDeleteWithOneClusteringColumns(true);
+ }
+
+ private void testDeleteWithOneClusteringColumns(boolean forceFlush) throws Throwable
+ {
+ for (String compactOption : new String[] {"", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (partitionKey int," +
+ "clustering int," +
+ "value int," +
+ " PRIMARY KEY (partitionKey, clustering))" + compactOption);
+
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 0, 0)");
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 1, 1)");
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 2, 2)");
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 3, 3)");
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 4, 4)");
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 5, 5)");
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (1, 0, 6)");
+ flush(forceFlush);
+
+ execute("DELETE value FROM %s WHERE partitionKey = ? AND clustering = ?", 0, 1);
+ flush(forceFlush);
+ if (isEmpty(compactOption))
+ {
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering = ?", 0, 1),
+ row(0, 1, null));
+ }
+ else
+ {
+ assertEmpty(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering = ?", 0, 1));
+ }
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) = (?)", 0, 1);
+ flush(forceFlush);
+ assertEmpty(execute("SELECT value FROM %s WHERE partitionKey = ? AND clustering = ?", 0, 1));
+
+ execute("DELETE FROM %s WHERE partitionKey IN (?, ?) AND clustering = ?", 0, 1, 0);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1),
+ row(0, 2, 2),
+ row(0, 3, 3),
+ row(0, 4, 4),
+ row(0, 5, 5));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) IN ((?), (?))", 0, 4, 5);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1),
+ row(0, 2, 2),
+ row(0, 3, 3));
+
+ // test invalid queries
+
+ // missing primary key element
+ assertInvalidMessage("Some partition key parts are missing: partitionkey",
+ "DELETE FROM %s WHERE clustering = ?", 1);
+
+ // token function
+ assertInvalidMessage("The token function cannot be used in WHERE clauses for DELETE statements",
+ "DELETE FROM %s WHERE token(partitionKey) = token(?) AND clustering = ? ", 0, 1);
+
+ // multiple time same primary key element in WHERE clause
+ assertInvalidMessage("clustering cannot be restricted by more than one relation if it includes an Equal",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering = ? AND clustering = ?", 0, 1, 1);
+
+ // unknown identifiers
+ assertInvalidMessage("Unknown identifier value1",
+ "DELETE value1 FROM %s WHERE partitionKey = ? AND clustering = ?", 0, 1);
+
+ assertInvalidMessage("Undefined name partitionkey1 in where clause ('partitionkey1 = ?')",
+ "DELETE FROM %s WHERE partitionKey1 = ? AND clustering = ?", 0, 1);
+
+ assertInvalidMessage("Undefined name clustering_3 in where clause ('clustering_3 = ?')",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering_3 = ?", 0, 1);
+
+ // Invalid operator in the where clause
+ assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)",
+ "DELETE FROM %s WHERE partitionKey > ? AND clustering = ?", 0, 1);
+
+ assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey",
+ "DELETE FROM %s WHERE partitionKey CONTAINS ? AND clustering = ?", 0, 1);
+
+ // Non primary key in the where clause
+ assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering = ? AND value = ?", 0, 1, 3);
+ }
+ }
+
+ @Test
+ public void testDeleteWithTwoClusteringColumns() throws Throwable
+ {
+ testDeleteWithTwoClusteringColumns(false);
+ testDeleteWithTwoClusteringColumns(true);
+ }
+
+ private void testDeleteWithTwoClusteringColumns(boolean forceFlush) throws Throwable
+ {
+ for (String compactOption : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (partitionKey int," +
+ "clustering_1 int," +
+ "clustering_2 int," +
+ "value int," +
+ " PRIMARY KEY (partitionKey, clustering_1, clustering_2))" + compactOption);
+
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 0, 0)");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 1, 1)");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 2, 2)");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 3, 3)");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 1, 1, 4)");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 1, 2, 5)");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (1, 0, 0, 6)");
+ flush(forceFlush);
+
+ execute("DELETE value FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
+ flush(forceFlush);
+
+ if (isEmpty(compactOption))
+ {
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
+ 0, 1, 1),
+ row(0, 1, 1, null));
+ }
+ else
+ {
+ assertEmpty(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
+ 0, 1, 1));
+ }
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) = (?, ?)", 0, 1, 1);
+ flush(forceFlush);
+ assertEmpty(execute("SELECT value FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
+ 0, 1, 1));
+
+ execute("DELETE FROM %s WHERE partitionKey IN (?, ?) AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 0, 0);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1),
+ row(0, 0, 1, 1),
+ row(0, 0, 2, 2),
+ row(0, 0, 3, 3),
+ row(0, 1, 2, 5));
+
+ Object[][] rows;
+ if (isEmpty(compactOption))
+ {
+ rows = new Object[][]{row(0, 0, 1, 1),
+ row(0, 0, 2, null),
+ row(0, 0, 3, null),
+ row(0, 1, 2, 5)};
+ }
+ else
+ {
+ rows = new Object[][]{row(0, 0, 1, 1), row(0, 1, 2, 5)};
+ }
+
+ execute("DELETE value FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 IN (?, ?)", 0, 0, 2, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1), rows);
+
+ if (isEmpty(compactOption))
+ {
+ rows = new Object[][]{row(0, 0, 1, 1),
+ row(0, 0, 3, null)};
+ }
+ else
+ {
+ rows = new Object[][]{row(0, 0, 1, 1)};
+ }
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) IN ((?, ?), (?, ?))", 0, 0, 2, 1, 2);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1), rows);
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1) IN ((?), (?)) AND clustering_2 = ?", 0, 0, 2, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1),
+ row(0, 0, 1, 1));
+
+ // test invalid queries
+
+ // missing primary key element
+ assertInvalidMessage("Some partition key parts are missing: partitionkey",
+ "DELETE FROM %s WHERE clustering_1 = ? AND clustering_2 = ?", 1, 1);
+
+ assertInvalidMessage("PRIMARY KEY column \"clustering_2\" cannot be restricted as preceding column \"clustering_1\" is not restricted",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering_2 = ?", 0, 1);
+
+ // token function
+ assertInvalidMessage("The token function cannot be used in WHERE clauses for DELETE statements",
+ "DELETE FROM %s WHERE token(partitionKey) = token(?) AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
+
+ // multiple time same primary key element in WHERE clause
+ assertInvalidMessage("clustering_1 cannot be restricted by more than one relation if it includes an Equal",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ? AND clustering_1 = ?", 0, 1, 1, 1);
+
+ // unknown identifiers
+ assertInvalidMessage("Unknown identifier value1",
+ "DELETE value1 FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
+
+ assertInvalidMessage("Undefined name partitionkey1 in where clause ('partitionkey1 = ?')",
+ "DELETE FROM %s WHERE partitionKey1 = ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
+
+ assertInvalidMessage("Undefined name clustering_3 in where clause ('clustering_3 = ?')",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_3 = ?", 0, 1, 1);
+
+ // Invalid operator in the where clause
+ assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)",
+ "DELETE FROM %s WHERE partitionKey > ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
+
+ assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey",
+ "DELETE FROM %s WHERE partitionKey CONTAINS ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
+
+ // Non primary key in the where clause
+ assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ? AND value = ?", 0, 1, 1, 3);
+ }
+ }
+
+ @Test
+ public void testDeleteWithRangeAndOneClusteringColumn() throws Throwable
+ {
+ testDeleteWithRangeAndOneClusteringColumn(false);
+ testDeleteWithRangeAndOneClusteringColumn(true);
+ }
+
+ private void testDeleteWithRangeAndOneClusteringColumn(boolean forceFlush) throws Throwable
+ {
+ for (String compactOption : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (partitionKey int," +
+ "clustering int," +
+ "value int," +
+ " PRIMARY KEY (partitionKey, clustering))" + compactOption);
+
+ int value = 0;
+ for (int partitionKey = 0; partitionKey < 5; partitionKey++)
+ for (int clustering1 = 0; clustering1 < 5; clustering1++)
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (?, ?, ?)",
+ partitionKey, clustering1, value++);
+
+ flush(forceFlush);
+
+ // test delete partition
+ execute("DELETE FROM %s WHERE partitionKey = ?", 1);
+ flush(forceFlush);
+ assertEmpty(execute("SELECT * FROM %s WHERE partitionKey = ?", 1));
+
+ // test slices on the first clustering column
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering >= ?", 0, 4);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, 0, 0),
+ row(0, 1, 1),
+ row(0, 2, 2),
+ row(0, 3, 3));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering > ?", 0, 2);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, 0, 0),
+ row(0, 1, 1),
+ row(0, 2, 2));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering <= ?", 0, 0);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, 1, 1),
+ row(0, 2, 2));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering < ?", 0, 2);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, 2, 2));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering >= ? AND clustering < ?", 2, 0, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 13),
+ row(2, 4, 14));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering > ? AND clustering <= ?", 2, 3, 5);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 13));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering < ? AND clustering > ?", 2, 3, 5);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 13));
+
+ // test multi-column slices
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) > (?)", 3, 2);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 3),
+ row(3, 0, 15),
+ row(3, 1, 16),
+ row(3, 2, 17));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) < (?)", 3, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 3),
+ row(3, 1, 16),
+ row(3, 2, 17));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) >= (?) AND (clustering) <= (?)", 3, 0, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 3),
+ row(3, 2, 17));
+
+ // Test invalid queries
+ assertInvalidMessage("Range deletions are not supported for specific columns",
+ "DELETE value FROM %s WHERE partitionKey = ? AND clustering >= ?", 2, 1);
+ assertInvalidMessage("Range deletions are not supported for specific columns",
+ "DELETE value FROM %s WHERE partitionKey = ?", 2);
+ }
+ }
+
+ @Test
+ public void testDeleteWithRangeAndTwoClusteringColumns() throws Throwable
+ {
+ testDeleteWithRangeAndTwoClusteringColumns(false);
+ testDeleteWithRangeAndTwoClusteringColumns(true);
+ }
+
+ private void testDeleteWithRangeAndTwoClusteringColumns(boolean forceFlush) throws Throwable
+ {
+ for (String compactOption : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (partitionKey int," +
+ "clustering_1 int," +
+ "clustering_2 int," +
+ "value int," +
+ " PRIMARY KEY (partitionKey, clustering_1, clustering_2))" + compactOption);
+
+ int value = 0;
+ for (int partitionKey = 0; partitionKey < 5; partitionKey++)
+ for (int clustering1 = 0; clustering1 < 5; clustering1++)
+ for (int clustering2 = 0; clustering2 < 5; clustering2++) {
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (?, ?, ?, ?)",
+ partitionKey, clustering1, clustering2, value++);}
+ flush(forceFlush);
+
+ // test unspecified second clustering column
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 < ?", 0, 2),
+ row(0, 0, 0, 0),
+ row(0, 0, 1, 1),
+ row(0, 0, 2, 2),
+ row(0, 0, 3, 3),
+ row(0, 0, 4, 4));
+
+ // test delete partition
+ execute("DELETE FROM %s WHERE partitionKey = ?", 1);
+ flush(forceFlush);
+ assertEmpty(execute("SELECT * FROM %s WHERE partitionKey = ?", 1));
+
+ // test slices on the second clustering column
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 < ?", 0, 0, 2);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 < ?", 0, 2),
+ row(0, 0, 2, 2),
+ row(0, 0, 3, 3),
+ row(0, 0, 4, 4));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 <= ?", 0, 0, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 < ?", 0, 2),
+ row(0, 0, 4, 4));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 > ? ", 0, 2, 2);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 2),
+ row(0, 2, 0, 10),
+ row(0, 2, 1, 11),
+ row(0, 2, 2, 12));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 >= ? ", 0, 2, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 2),
+ row(0, 2, 0, 10));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 > ? AND clustering_2 < ? ",
+ 0, 3, 1, 4);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 3),
+ row(0, 3, 0, 15),
+ row(0, 3, 1, 16),
+ row(0, 3, 4, 19));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 > ? AND clustering_2 < ? ",
+ 0, 3, 4, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 3),
+ row(0, 3, 0, 15),
+ row(0, 3, 1, 16),
+ row(0, 3, 4, 19));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 >= ? AND clustering_2 <= ? ",
+ 0, 3, 1, 4);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 3),
+ row(0, 3, 0, 15));
+
+ // test slices on the first clustering column
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 >= ?", 0, 4);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, 0, 4, 4),
+ row(0, 2, 0, 10),
+ row(0, 3, 0, 15));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 > ?", 0, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, 0, 4, 4),
+ row(0, 2, 0, 10),
+ row(0, 3, 0, 15));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 < ?", 0, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, 3, 0, 15));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 >= ? AND clustering_1 < ?", 2, 0, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 0, 65),
+ row(2, 3, 1, 66),
+ row(2, 3, 2, 67),
+ row(2, 3, 3, 68),
+ row(2, 3, 4, 69),
+ row(2, 4, 0, 70),
+ row(2, 4, 1, 71),
+ row(2, 4, 2, 72),
+ row(2, 4, 3, 73),
+ row(2, 4, 4, 74));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 > ? AND clustering_1 <= ?", 2, 3, 5);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 0, 65),
+ row(2, 3, 1, 66),
+ row(2, 3, 2, 67),
+ row(2, 3, 3, 68),
+ row(2, 3, 4, 69));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 < ? AND clustering_1 > ?", 2, 3, 5);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 0, 65),
+ row(2, 3, 1, 66),
+ row(2, 3, 2, 67),
+ row(2, 3, 3, 68),
+ row(2, 3, 4, 69));
+
+ // test multi-column slices
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) > (?, ?)", 2, 3, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 0, 65),
+ row(2, 3, 1, 66),
+ row(2, 3, 2, 67),
+ row(2, 3, 3, 68));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) < (?, ?)", 2, 3, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 1, 66),
+ row(2, 3, 2, 67),
+ row(2, 3, 3, 68));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) >= (?, ?) AND (clustering_1) <= (?)", 2, 3, 2, 4);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 1, 66));
+
+ // Test with a mix of single column and multi-column restrictions
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND (clustering_2) < (?)", 3, 0, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 3, 0),
+ row(3, 0, 3, 78),
+ row(3, 0, 4, 79));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 IN (?, ?) AND (clustering_2) >= (?)", 3, 0, 1, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 IN (?, ?)", 3, 0, 1),
+ row(3, 1, 0, 80),
+ row(3, 1, 1, 81),
+ row(3, 1, 2, 82));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1) IN ((?), (?)) AND clustering_2 < ?", 3, 0, 1, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 IN (?, ?)", 3, 0, 1),
+ row(3, 1, 1, 81),
+ row(3, 1, 2, 82));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1) = (?) AND clustering_2 >= ?", 3, 1, 2);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 IN (?, ?)", 3, 0, 1),
+ row(3, 1, 1, 81));
+
+ // Test invalid queries
+ assertInvalidMessage("Range deletions are not supported for specific columns",
+ "DELETE value FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) >= (?, ?)", 2, 3, 1);
+ assertInvalidMessage("Range deletions are not supported for specific columns",
+ "DELETE value FROM %s WHERE partitionKey = ? AND clustering_1 >= ?", 2, 3);
+ assertInvalidMessage("Range deletions are not supported for specific columns",
+ "DELETE value FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 2, 3);
+ assertInvalidMessage("Range deletions are not supported for specific columns",
+ "DELETE value FROM %s WHERE partitionKey = ?", 2);
+ }
+ }
+
+ @Test
+ public void testDeleteWithAStaticColumn() throws Throwable
+ {
+ testDeleteWithAStaticColumn(false);
+ testDeleteWithAStaticColumn(true);
+ }
+
+ private void testDeleteWithAStaticColumn(boolean forceFlush) throws Throwable
+ {
+ createTable("CREATE TABLE %s (partitionKey int," +
+ "clustering_1 int," +
+ "clustering_2 int," +
+ "value int," +
+ "staticValue text static," +
+ " PRIMARY KEY (partitionKey, clustering_1, clustering_2))");
+
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value, staticValue) VALUES (0, 0, 0, 0, 'A')");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 1, 1)");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value, staticValue) VALUES (1, 0, 0, 6, 'B')");
+ flush(forceFlush);
+
+ execute("DELETE staticValue FROM %s WHERE partitionKey = ?", 0);
+ flush(forceFlush);
+ assertRows(execute("SELECT DISTINCT staticValue FROM %s WHERE partitionKey IN (?, ?)", 0, 1),
+ row(new Object[1]), row("B"));
+
+ execute("DELETE staticValue, value FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
+ 1, 0, 0);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s"),
+ row(1, 0, 0, null, null),
+ row(0, 0, 0, null, 0),
+ row(0, 0, 1, null, 1));
+
+ assertInvalidMessage("Invalid restrictions on clustering columns since the DELETE statement modifies only static columns",
+ "DELETE staticValue FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
+ 0, 0, 1);
+
+ assertInvalidMessage("Invalid restrictions on clustering columns since the DELETE statement modifies only static columns",
+ "DELETE staticValue FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) >= (?, ?)",
+ 0, 0, 1);
+ }
+
+ @Test
+ public void testDeleteWithSecondaryIndices() throws Throwable
+ {
+ testDeleteWithSecondaryIndices(false);
+ testDeleteWithSecondaryIndices(true);
+ }
+
+ private void testDeleteWithSecondaryIndices(boolean forceFlush) throws Throwable
+ {
+ createTable("CREATE TABLE %s (partitionKey int," +
+ "clustering_1 int," +
+ "value int," +
+ "values set<int>," +
+ " PRIMARY KEY (partitionKey, clustering_1))");
+
+ createIndex("CREATE INDEX ON %s (value)");
+ createIndex("CREATE INDEX ON %s (clustering_1)");
+ createIndex("CREATE INDEX ON %s (values)");
+
+ execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 0, 0, {0})");
+ execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 1, 1, {0, 1})");
+ execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 2, 2, {0, 1, 2})");
+ execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 3, 3, {0, 1, 2, 3})");
+ execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (1, 0, 4, {0, 1, 2, 3, 4})");
+
+ flush(forceFlush);
+
+ assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND value = ?", 3, 3, 3);
+ assertInvalidMessage("Non PRIMARY KEY columns found in where clause: values",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND values CONTAINS ?", 3, 3, 3);
+ assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value",
+ "DELETE FROM %s WHERE partitionKey = ? AND value = ?", 3, 3);
+ assertInvalidMessage("Non PRIMARY KEY columns found in where clause: values",
+ "DELETE FROM %s WHERE partitionKey = ? AND values CONTAINS ?", 3, 3);
+ assertInvalidMessage("Some partition key parts are missing: partitionkey",
+ "DELETE FROM %s WHERE clustering_1 = ?", 3);
+ assertInvalidMessage("Some partition key parts are missing: partitionkey",
+ "DELETE FROM %s WHERE value = ?", 3);
+ assertInvalidMessage("Some partition key parts are missing: partitionkey",
+ "DELETE FROM %s WHERE values CONTAINS ?", 3);
+ }
+
+ @Test
+ public void testDeleteWithOnlyPK() throws Throwable
+ {
+ // This is a regression test for CASSANDRA-11102
+
+ createTable("CREATE TABLE %s (k int, v int, PRIMARY KEY (k, v)) WITH gc_grace_seconds=1");
+
+ execute("INSERT INTO %s(k, v) VALUES (?, ?)", 1, 2);
+
+ execute("DELETE FROM %s WHERE k = ? AND v = ?", 1, 2);
+ execute("INSERT INTO %s(k, v) VALUES (?, ?)", 2, 3);
+
+ Thread.sleep(500);
+
+ execute("DELETE FROM %s WHERE k = ? AND v = ?", 2, 3);
+ execute("INSERT INTO %s(k, v) VALUES (?, ?)", 1, 2);
+
+ Thread.sleep(500);
+
+ flush();
+
+ assertRows(execute("SELECT * FROM %s"), row(1, 2));
+
+ Thread.sleep(1000);
+ compact();
+
+ assertRows(execute("SELECT * FROM %s"), row(1, 2));
+ }
+
+ @Test
+ public void testDeleteColumnNoClustering() throws Throwable
+ {
+ // This is a regression test for CASSANDRA-11068 (and ultimately another test for CASSANDRA-11102)
+ // Creates a table without clustering, insert a row (with a column) and only remove the column.
+ // We should still have a row (with a null column value) even post-compaction.
+
+ createTable("CREATE TABLE %s (k int PRIMARY KEY, v int) WITH gc_grace_seconds=0");
+
+ execute("INSERT INTO %s(k, v) VALUES (?, ?)", 0, 0);
+ execute("DELETE v FROM %s WHERE k=?", 0);
+
+ assertRows(execute("SELECT * FROM %s"), row(0, null));
+
+ flush();
+ assertRows(execute("SELECT * FROM %s"), row(0, null));
+
+ compact();
+ assertRows(execute("SELECT * FROM %s"), row(0, null));
+ }
+ @Test
+ public void testDeleteWithEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY (pk, c))" + options);
+
+ if (StringUtils.isEmpty(options))
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');");
+
+ assertEmpty(execute("SELECT * FROM %s"));
++
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));");
++
++ assertEmpty(execute("SELECT * FROM %s"));
++
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("2"));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')");
++
++ assertRows(execute("SELECT * FROM %s"),
++ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')");
++
++ assertEmpty(execute("SELECT * FROM %s"));
+ }
+ else
+ {
+ assertInvalid("Invalid empty or null value for column c",
+ "DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('')");
+ assertInvalid("Invalid empty or null value for column c",
+ "DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'))");
+ }
++
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("2"));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')");
++
++ assertEmpty(execute("SELECT * FROM %s"));
++
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("2"));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')");
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')");
++
++ assertRows(execute("SELECT * FROM %s"),
++ row(bytes("foo123"), bytes("1"), bytes("1")),
++ row(bytes("foo123"), bytes("2"), bytes("2")));
+ }
+ }
+
+ @Test
+ public void testDeleteWithMultipleClusteringColumnsAndEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');");
+
+ assertEmpty(execute("SELECT * FROM %s"));
++
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("1"));
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');");
++
++ assertEmpty(execute("SELECT * FROM %s"));
++
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("0"));
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("3"));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')");
++
++ assertRows(execute("SELECT * FROM %s"),
++ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("0")));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 >= textAsBlob('')");
++
++ assertEmpty(execute("SELECT * FROM %s"));
++
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("3"));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')");
++
++ assertEmpty(execute("SELECT * FROM %s"));
++
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("3"));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 <= textAsBlob('')");
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 < textAsBlob('')");
++
++ assertRows(execute("SELECT * FROM %s"),
++ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
++ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("3")));
+ }
+ }
++
+ private void flush(boolean forceFlush)
+ {
+ if (forceFlush)
+ flush();
+ }
+
+ @Test
+ public void testDeleteAndReverseQueries() throws Throwable
+ {
+ // This test insert rows in one sstable and a range tombstone covering some of those rows in another, and it
+ // validates we correctly get only the non-removed rows when doing reverse queries.
+
+ createTable("CREATE TABLE %s (k text, i int, PRIMARY KEY (k, i))");
+
+ for (int i = 0; i < 10; i++)
+ execute("INSERT INTO %s(k, i) values (?, ?)", "a", i);
+
+ flush();
+
+ execute("DELETE FROM %s WHERE k = ? AND i >= ? AND i <= ?", "a", 2, 7);
+
+ assertRows(execute("SELECT i FROM %s WHERE k = ? ORDER BY i DESC", "a"),
+ row(9), row(8), row(1), row(0)
+ );
+
+ flush();
+
+ assertRows(execute("SELECT i FROM %s WHERE k = ? ORDER BY i DESC", "a"),
+ row(9), row(8), row(1), row(0)
+ );
+ }
}
[43/50] [abbrv] cassandra git commit: Merge branch cassandra-2.1 into
cassandra-2.2
Posted by ms...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 8820ff7,40f3f33..edf8e47
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@@ -181,8 -247,11 +181,11 @@@ public class SelectStatement implement
private Pageable getPageableCommand(QueryOptions options, int limit, long now) throws RequestValidationException
{
- if (isNotReturningAnyRows(options))
++ if (restrictions.isNotReturningAnyRows(options))
+ return null;
+
int limitForQuery = updateLimitForQuery(limit);
- if (isKeyRange || usesSecondaryIndexing)
+ if (restrictions.isKeyRange() || restrictions.usesSecondaryIndexing())
return getRangeCommand(options, limitForQuery, now);
List<ReadCommand> commands = getSliceCommands(options, limitForQuery, now);
@@@ -596,52 -961,338 +599,58 @@@
}
}
- /** Returns true if a non-frozen collection is selected, false otherwise. */
- private boolean selectACollection()
+ /**
+ * May be used by custom QueryHandler implementations
+ */
+ public List<IndexExpression> getValidatedIndexExpressions(QueryOptions options) throws InvalidRequestException
{
- if (!cfm.comparator.hasCollections())
- return false;
+ if (!restrictions.usesSecondaryIndexing())
+ return Collections.emptyList();
- for (ColumnDefinition def : selection.getColumns())
- {
- if (def.type.isCollection() && def.type.isMultiCell())
- return true;
- }
+ ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(columnFamily());
+ SecondaryIndexManager secondaryIndexManager = cfs.indexManager;
- return false;
+ List<IndexExpression> expressions = restrictions.getIndexExpressions(secondaryIndexManager, options);
+ secondaryIndexManager.validateIndexSearchersForQuery(expressions);
+
+ return expressions;
}
- @VisibleForTesting
- static List<Composite> buildBound(Bound bound,
- List<ColumnDefinition> defs,
- Restriction[] restrictions,
- boolean isReversed,
- CType type,
- QueryOptions options) throws InvalidRequestException
+ private CellName makeExclusiveSliceBound(Bound bound, CellNameType type, QueryOptions options) throws InvalidRequestException
{
- CBuilder builder = type.builder();
-
- // The end-of-component of composite doesn't depend on whether the
- // component type is reversed or not (i.e. the ReversedType is applied
- // to the component comparator but not to the end-of-component itself),
- // it only depends on whether the slice is reversed
- Bound eocBound = isReversed ? Bound.reverse(bound) : bound;
- for (int i = 0, m = defs.size(); i < m; i++)
- {
- ColumnDefinition def = defs.get(i);
-
- // In a restriction, we always have Bound.START < Bound.END for the "base" comparator.
- // So if we're doing a reverse slice, we must inverse the bounds when giving them as start and end of the slice filter.
- // But if the actual comparator itself is reversed, we must inversed the bounds too.
- Bound b = isReversed == isReversedType(def) ? bound : Bound.reverse(bound);
- Restriction r = restrictions[def.position()];
- if (isNullRestriction(r, b) || !r.canEvaluateWithSlices())
- {
- // There wasn't any non EQ relation on that key, we select all records having the preceding component as prefix.
- // For composites, if there was preceding component and we're computing the end, we must change the last component
- // End-Of-Component, otherwise we would be selecting only one record.
- Composite prefix = builder.build();
- return Collections.singletonList(eocBound == Bound.END ? prefix.end() : prefix.start());
- }
- if (r.isSlice())
- {
- if (r.isMultiColumn())
- {
- MultiColumnRestriction.Slice slice = (MultiColumnRestriction.Slice) r;
+ // clusteringColumnBounds may reverse bound if clustering order is reversed
+ // but areRequestedBoundsInclusive checks for Restriction::isInclusive and never
+ // reverses the order. In order to avoid inconsistencies and check inclusive
+ // bounds correctly, we need to check for column order and reverse it. See CASSANDRA-10988
+ if (restrictions.areRequestedBoundsInclusive(reverseBoundIfNeeded(bound)))
+ return null;
- return type.makeCellName(restrictions.getClusteringColumnsBounds(bound, options).get(0));
- if (!slice.hasBound(b))
- {
- Composite prefix = builder.build();
- return Collections.singletonList(builder.remainingCount() > 0 && eocBound == Bound.END
- ? prefix.end()
- : prefix);
- }
++ // We can only reach that if the table is dense non-compound tables.
++ // By consequence, we know that the table is a COMPACT table with only one clustering column.
++ ByteBuffer value = restrictions.getClusteringColumnsBounds(bound, options).get(0);
+
- List<ByteBuffer> vals = slice.componentBounds(b, options);
-
- for (int j = 0, n = vals.size(); j < n; j++)
- addValue(builder, defs.get(i + j), vals.get(j)) ;
- }
- else
- {
- builder.add(getSliceValue(r, b, options));
- }
- Operator relType = ((Restriction.Slice)r).getRelation(eocBound, b);
- return Collections.singletonList(builder.build().withEOC(eocForRelation(relType)));
- }
-
- if (r.isIN())
- {
- // The IN query might not have listed the values in comparator order, so we need to re-sort
- // the bounds lists to make sure the slices works correctly (also, to avoid duplicates).
- TreeSet<Composite> inValues = new TreeSet<>(isReversed ? type.reverseComparator() : type);
-
- if (r.isMultiColumn())
- {
- List<List<ByteBuffer>> splitInValues = ((MultiColumnRestriction.IN) r).splitValues(options);
-
- for (List<ByteBuffer> components : splitInValues)
- {
- for (int j = 0; j < components.size(); j++)
- if (components.get(j) == null)
- throw new InvalidRequestException("Invalid null value in condition for column " + defs.get(i + j).name);
-
- Composite prefix = builder.buildWith(components);
- inValues.add(builder.remainingCount() == 0 ? prefix : addEOC(prefix, eocBound));
- }
- return new ArrayList<>(inValues);
- }
-
- List<ByteBuffer> values = r.values(options);
- if (values.size() != 1)
- {
- // IN query, we only support it on the clustering columns
- assert def.position() == defs.size() - 1;
- for (ByteBuffer val : values)
- {
- if (val == null)
- throw new InvalidRequestException(String.format("Invalid null value in condition for column %s",
- def.name));
- Composite prefix = builder.buildWith(val);
- // See below for why this
- inValues.add(builder.remainingCount() == 0 ? prefix : addEOC(prefix, eocBound));
- }
- return new ArrayList<>(inValues);
- }
- }
-
- List<ByteBuffer> values = r.values(options);
-
- if (r.isMultiColumn())
- {
- for (int j = 0; j < values.size(); j++)
- addValue(builder, defs.get(i + j), values.get(j));
- i += values.size() - 1; // skips the processed columns
- }
- else
- {
- addValue(builder, def, values.get(0));
- }
- }
- // Means no relation at all or everything was an equal
- // Note: if the builder is "full", there is no need to use the end-of-component bit. For columns selection,
- // it would be harmless to do it. However, we use this method got the partition key too. And when a query
- // with 2ndary index is done, and with the the partition provided with an EQ, we'll end up here, and in that
- // case using the eoc would be bad, since for the random partitioner we have no guarantee that
- // prefix.end() will sort after prefix (see #5240).
- Composite prefix = builder.build();
- return Collections.singletonList(builder.remainingCount() == 0 ? prefix : addEOC(prefix, eocBound));
- }
++ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, if the slice value is empty
++ // we know that we can treat the slice as inclusive.
++ return value.hasRemaining() ? type.makeCellName(value) : null;
+ }
/**
- * Adds an EOC to the specified Composite.
- *
- * @param composite the composite
- * @param eocBound the EOC bound
- * @return a new <code>Composite</code> with the EOC corresponding to the eocBound
+ * Reverses the specified bound if the non-compound clustering column is a reversed one.
+ * @param bound bound to reverse
+ * @return the bound reversed if the column type was a reversed one or the original bound
*/
- private static Composite addEOC(Composite composite, Bound eocBound)
+ private Bound reverseBoundIfNeeded(Bound bound)
{
- return eocBound == Bound.END ? composite.end() : composite.start();
- }
+ assert !cfm.comparator.isCompound();
- /**
- * Adds the specified value to the specified builder
- *
- * @param builder the CBuilder to which the value must be added
- * @param def the column associated to the value
- * @param value the value to add
- * @throws InvalidRequestException if the value is null
- */
- private static void addValue(CBuilder builder, ColumnDefinition def, ByteBuffer value) throws InvalidRequestException
- {
- if (value == null)
- throw new InvalidRequestException(String.format("Invalid null value in condition for column %s", def.name));
- builder.add(value);
- }
-
- private static Composite.EOC eocForRelation(Operator op)
- {
- switch (op)
- {
- case LT:
- // < X => using startOf(X) as finish bound
- return Composite.EOC.START;
- case GT:
- case LTE:
- // > X => using endOf(X) as start bound
- // <= X => using endOf(X) as finish bound
- return Composite.EOC.END;
- default:
- // >= X => using X as start bound (could use START_OF too)
- // = X => using X
- return Composite.EOC.NONE;
- }
- }
-
- private static boolean isNullRestriction(Restriction r, Bound b)
- {
- return r == null || (r.isSlice() && !((Restriction.Slice)r).hasBound(b));
- }
-
- private static ByteBuffer getSliceValue(Restriction r, Bound b, QueryOptions options) throws InvalidRequestException
- {
- Restriction.Slice slice = (Restriction.Slice)r;
- assert slice.hasBound(b);
- ByteBuffer val = slice.bound(b, options);
- if (val == null)
- throw new InvalidRequestException(String.format("Invalid null clustering key part %s", r));
- return val;
- }
-
- private List<Composite> getRequestedBound(Bound b, QueryOptions options) throws InvalidRequestException
- {
- assert isColumnRange();
- List<Composite> bound = buildBound(b, cfm.clusteringColumns(), columnRestrictions, isReversed, cfm.comparator, options);
- for (Composite c : bound) {
- if (!c.isEmpty())
- QueryProcessor.validateComposite(c, cfm.comparator);
- }
- return bound;
+ List<ColumnDefinition> columnDefs = cfm.clusteringColumns();
+ return columnDefs.get(columnDefs.size() - 1).isReversedType() ? bound.reverse() : bound;
}
- /**
- * May be used by custom QueryHandler implementations
- */
- public List<IndexExpression> getValidatedIndexExpressions(QueryOptions options) throws InvalidRequestException
- {
- if (!usesSecondaryIndexing || restrictedColumns.isEmpty())
- return Collections.emptyList();
-
- List<IndexExpression> expressions = new ArrayList<IndexExpression>();
- for (ColumnDefinition def : restrictedColumns.keySet())
- {
- Restriction restriction;
- switch (def.kind)
- {
- case PARTITION_KEY:
- restriction = keyRestrictions[def.position()];
- break;
- case CLUSTERING_COLUMN:
- restriction = columnRestrictions[def.position()];
- break;
- case REGULAR:
- case STATIC:
- restriction = metadataRestrictions.get(def.name);
- break;
- default:
- // We don't allow restricting a COMPACT_VALUE for now in prepare.
- throw new AssertionError();
- }
-
- if (restriction.isSlice())
- {
- Restriction.Slice slice = (Restriction.Slice)restriction;
- for (Bound b : Bound.values())
- {
- if (slice.hasBound(b))
- {
- ByteBuffer value = validateIndexedValue(def, slice.bound(b, options));
- Operator op = slice.getIndexOperator(b);
- // If the underlying comparator for name is reversed, we need to reverse the IndexOperator: user operation
- // always refer to the "forward" sorting even if the clustering order is reversed, but the 2ndary code does
- // use the underlying comparator as is.
- if (def.type instanceof ReversedType)
- op = reverse(op);
- expressions.add(new IndexExpression(def.name.bytes, op, value));
- }
- }
- }
- else if (restriction.isContains())
- {
- SingleColumnRestriction.Contains contains = (SingleColumnRestriction.Contains)restriction;
- for (ByteBuffer value : contains.values(options))
- {
- validateIndexedValue(def, value);
- expressions.add(new IndexExpression(def.name.bytes, Operator.CONTAINS, value));
- }
- for (ByteBuffer key : contains.keys(options))
- {
- validateIndexedValue(def, key);
- expressions.add(new IndexExpression(def.name.bytes, Operator.CONTAINS_KEY, key));
- }
- }
- else
- {
- ByteBuffer value;
- if (restriction.isMultiColumn())
- {
- List<ByteBuffer> values = restriction.values(options);
- value = values.get(def.position());
- }
- else
- {
- List<ByteBuffer> values = restriction.values(options);
- if (values.size() != 1)
- throw new InvalidRequestException("IN restrictions are not supported on indexed columns");
-
- value = values.get(0);
- }
-
- validateIndexedValue(def, value);
- expressions.add(new IndexExpression(def.name.bytes, Operator.EQ, value));
- }
- }
-
- if (usesSecondaryIndexing)
- {
- ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(columnFamily());
- SecondaryIndexManager secondaryIndexManager = cfs.indexManager;
- secondaryIndexManager.validateIndexSearchersForQuery(expressions);
- }
-
- return expressions;
- }
-
- private static ByteBuffer validateIndexedValue(ColumnDefinition def, ByteBuffer value) throws InvalidRequestException
- {
- if (value == null)
- throw new InvalidRequestException(String.format("Unsupported null value for indexed column %s", def.name));
- if (value.remaining() > 0xFFFF)
- throw new InvalidRequestException("Index expression values may not be larger than 64K");
- return value;
- }
-
- private CellName makeExclusiveSliceBound(Bound bound, CellNameType type, QueryOptions options) throws InvalidRequestException
- {
- if (sliceRestriction.isInclusive(bound))
- return null;
-
- // We can only reach that point if cfm.comparator.isCompound() = false and the table has some clustering columns.
- // By consequence, we know that the table is a COMPACT table with only one clustering column.
- ByteBuffer value = sliceRestriction.isMultiColumn() ? ((MultiColumnRestriction.Slice) sliceRestriction).componentBounds(bound, options).get(0)
- : sliceRestriction.bound(bound, options);
-
- // Dense non-compound tables do not accept empty ByteBuffers. By consequence, if the slice value is empty
- // we know that we can treat the slice as inclusive.
- return value.hasRemaining() ? type.makeCellName(value) : null;
- }
-
-
private Iterator<Cell> applySliceRestriction(final Iterator<Cell> cells, final QueryOptions options) throws InvalidRequestException
{
- assert sliceRestriction != null;
-
final CellNameType type = cfm.comparator;
+
final CellName excludedStart = makeExclusiveSliceBound(Bound.START, type, options);
final CellName excludedEnd = makeExclusiveSliceBound(Bound.END, type, options);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
index 91a059f,39e632a..517d842
--- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
@@@ -86,7 -84,7 +86,7 @@@ public class UpdateStatement extends Mo
if (cfm.comparator.isDense())
{
if (prefix.isEmpty())
- throw new InvalidRequestException(String.format("Missing PRIMARY KEY part %s", cfm.clusteringColumns().get(0)));
- throw new InvalidRequestException(String.format("Missing PRIMARY KEY part %s", cfm.clusteringColumns().iterator().next().name));
++ throw new InvalidRequestException(String.format("Missing PRIMARY KEY part %s", cfm.clusteringColumns().get(0).name));
// An empty name for the compact value is what we use to recognize the case where there is not column
// outside the PK, see CreateStatement.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/db/compaction/Scrubber.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/compaction/Scrubber.java
index d1fe702,2df3665..99ee62e
--- a/src/java/org/apache/cassandra/db/compaction/Scrubber.java
+++ b/src/java/org/apache/cassandra/db/compaction/Scrubber.java
@@@ -23,12 -23,12 +23,14 @@@ import java.util.*
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
+ import com.google.common.collect.AbstractIterator;
-import com.google.common.collect.Sets;
import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.lifecycle.LifecycleTransaction;
+ import org.apache.cassandra.db.columniterator.OnDiskAtomIterator;
import org.apache.cassandra.io.sstable.*;
+import org.apache.cassandra.io.sstable.format.SSTableReader;
+import org.apache.cassandra.io.sstable.format.SSTableWriter;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.io.util.RandomAccessReader;
import org.apache.cassandra.service.ActiveRepairService;
@@@ -220,22 -222,11 +222,8 @@@ public class Scrubber implements Closea
if (indexFile != null && dataStart != dataStartFromIndex)
outputHandler.warn(String.format("Data file row position %d differs from index file row position %d", dataStart, dataStartFromIndex));
- SSTableIdentityIterator atoms = new SSTableIdentityIterator(sstable, dataFile, key, checkData);
-
- if (prevKey != null && prevKey.compareTo(key) > 0)
- {
- saveOutOfOrderRow(prevKey, key, atoms);
- continue;
- }
-
- @SuppressWarnings("resource")
- AbstractCompactedRow compactedRow = new LazilyCompactedRow(controller, Collections.singletonList(atoms));
- if (writer.tryAppend(compactedRow) == null)
- emptyRows++;
- else
- goodRows++;
- if (indexFile != null && dataSize != dataSizeFromIndex)
- outputHandler.warn(String.format("Data file row size %d different from index file row size %d", dataSize, dataSizeFromIndex));
--
- prevKey = key;
- if (tryAppend(prevKey, key, dataSize, writer))
++ if (tryAppend(prevKey, key, writer))
+ prevKey = key;
}
catch (Throwable th)
{
@@@ -251,22 -242,8 +239,9 @@@
try
{
dataFile.seek(dataStartFromIndex);
- if (tryAppend(prevKey, key, dataSize, writer))
+
- SSTableIdentityIterator atoms = new SSTableIdentityIterator(sstable, dataFile, key, checkData);
- if (prevKey != null && prevKey.compareTo(key) > 0)
- {
- saveOutOfOrderRow(prevKey, key, atoms);
- continue;
- }
-
- @SuppressWarnings("resource")
- AbstractCompactedRow compactedRow = new LazilyCompactedRow(controller, Collections.singletonList(atoms));
- if (writer.tryAppend(compactedRow) == null)
- emptyRows++;
- else
- goodRows++;
-
- prevKey = key;
++ if (tryAppend(prevKey, key, writer))
+ prevKey = key;
}
catch (Throwable th2)
{
@@@ -337,6 -316,32 +312,32 @@@
}
}
+ @SuppressWarnings("resource")
- private boolean tryAppend(DecoratedKey prevKey, DecoratedKey key, long dataSize, SSTableRewriter writer)
++ private boolean tryAppend(DecoratedKey prevKey, DecoratedKey key, SSTableRewriter writer)
+ {
+ // OrderCheckerIterator will check, at iteration time, that the cells are in the proper order. If it detects
+ // that one cell is out of order, it will stop returning them. The remaining cells will be sorted and added
+ // to the outOfOrderRows that will be later written to a new SSTable.
- OrderCheckerIterator atoms = new OrderCheckerIterator(new SSTableIdentityIterator(sstable, dataFile, key, dataSize, validateColumns),
++ OrderCheckerIterator atoms = new OrderCheckerIterator(new SSTableIdentityIterator(sstable, dataFile, key, checkData),
+ cfs.metadata.comparator.onDiskAtomComparator());
+ if (prevKey != null && prevKey.compareTo(key) > 0)
+ {
+ saveOutOfOrderRow(prevKey, key, atoms);
+ return false;
+ }
+
+ AbstractCompactedRow compactedRow = new LazilyCompactedRow(controller, Collections.singletonList(atoms));
+ if (writer.tryAppend(compactedRow) == null)
+ emptyRows++;
+ else
+ goodRows++;
+
+ if (atoms.hasOutOfOrderCells())
+ saveOutOfOrderRow(key, atoms);
+
+ return true;
+ }
+
private void updateIndexKey()
{
currentIndexKey = nextIndexKey;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/db/marshal/ReversedType.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/marshal/ReversedType.java
index 68a7e02,53798f8..19bee5f
--- a/src/java/org/apache/cassandra/db/marshal/ReversedType.java
+++ b/src/java/org/apache/cassandra/db/marshal/ReversedType.java
@@@ -60,23 -58,8 +60,13 @@@ public class ReversedType<T> extends Ab
this.baseType = baseType;
}
+ public boolean isEmptyValueMeaningless()
+ {
+ return baseType.isEmptyValueMeaningless();
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
- // An empty byte buffer is always smaller
- if (o1.remaining() == 0)
- {
- return o2.remaining() == 0 ? 0 : -1;
- }
- if (o2.remaining() == 0)
- {
- return 1;
- }
-
return baseType.compare(o2, o1);
}
[20/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0'
into cassandra-3.9
Posted by ms...@apache.org.
Merge branch 'cassandra-3.0' into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/f4c3e14f
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/f4c3e14f
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/f4c3e14f
Branch: refs/heads/cassandra-3.8
Commit: f4c3e14fdebe9ec3fb9be4a6918b55f489891f3a
Parents: 02f48c8 2a828af
Author: Tyler Hobbs <ty...@gmail.com>
Authored: Thu Aug 11 13:47:11 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Thu Aug 11 13:47:11 2016 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
build.xml | 4 ++++
2 files changed, 5 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f4c3e14f/CHANGES.txt
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f4c3e14f/build.xml
----------------------------------------------------------------------
[03/50] [abbrv] cassandra git commit: Fix hanging stream session
Posted by ms...@apache.org.
Fix hanging stream session
by preventing CompressedStreamReader from blocking on IOException.
Also removed retry support from streaming.
Patch by Paulo Motta; Reviewed by Yuki Morishita for CASSANDRA-10992
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/76e3100f
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/76e3100f
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/76e3100f
Branch: refs/heads/cassandra-3.8
Commit: 76e3100ffb106cab3cc665404e293c1026e5e65c
Parents: bc9af92
Author: Paulo Motta <pa...@gmail.com>
Authored: Thu Jun 23 11:33:54 2016 -0300
Committer: Yuki Morishita <yu...@apache.org>
Committed: Tue Aug 9 16:31:34 2016 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../org/apache/cassandra/config/Config.java | 4 ++
.../cassandra/config/DatabaseDescriptor.java | 5 --
.../cassandra/streaming/StreamReader.java | 26 +----------
.../cassandra/streaming/StreamSession.java | 36 +-------------
.../compress/CompressedInputStream.java | 21 ++++++++-
.../compress/CompressedStreamReader.java | 10 ++--
.../streaming/messages/IncomingFileMessage.java | 22 ++-------
.../streaming/messages/RetryMessage.java | 4 ++
.../org/apache/cassandra/utils/Throwables.java | 17 +++++++
.../compress/CompressedInputStreamTest.java | 49 +++++++++++++++++---
11 files changed, 102 insertions(+), 93 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index f734476..232203e 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.2.8
+ * Fix hanging stream session (CASSANDRA-10992)
* Add byteman support for testing (CASSANDRA-12377)
* Fix INSERT JSON, fromJson() support of smallint, tinyint types (CASSANDRA-12371)
* Restore JVM metric export for metric reporters (CASSANDRA-12312)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/config/Config.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/Config.java b/src/java/org/apache/cassandra/config/Config.java
index ede4560..60daee6 100644
--- a/src/java/org/apache/cassandra/config/Config.java
+++ b/src/java/org/apache/cassandra/config/Config.java
@@ -171,6 +171,10 @@ public class Config
public volatile Integer compaction_throughput_mb_per_sec = 16;
public volatile Integer compaction_large_partition_warning_threshold_mb = 100;
+ /**
+ * @deprecated retry support removed on CASSANDRA-10992
+ */
+ @Deprecated
public Integer max_streaming_retries = 3;
public volatile Integer stream_throughput_outbound_megabits_per_sec = 200;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index f1acfc4..6e46725 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -957,11 +957,6 @@ public class DatabaseDescriptor
return conf.cluster_name;
}
- public static int getMaxStreamingRetries()
- {
- return conf.max_streaming_retries;
- }
-
public static int getStoragePort()
{
return Integer.parseInt(System.getProperty("cassandra.storage_port", conf.storage_port.toString()));
http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/streaming/StreamReader.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/streaming/StreamReader.java b/src/java/org/apache/cassandra/streaming/StreamReader.java
index 8789720..c96ea22 100644
--- a/src/java/org/apache/cassandra/streaming/StreamReader.java
+++ b/src/java/org/apache/cassandra/streaming/StreamReader.java
@@ -45,6 +45,7 @@ import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.BytesReadTracker;
import org.apache.cassandra.utils.Pair;
+import static org.apache.cassandra.utils.Throwables.extractIOExceptionCause;
/**
* StreamReader reads from stream and writes to SSTable.
@@ -137,11 +138,7 @@ public class StreamReader
e.addSuppressed(e2);
}
}
- drain(dis, in.getBytesRead());
- if (e instanceof IOException)
- throw (IOException) e;
- else
- throw Throwables.propagate(e);
+ throw Throwables.propagate(e);
}
}
@@ -155,25 +152,6 @@ public class StreamReader
return SSTableWriter.create(desc, estimatedKeys, repairedAt, sstableLevel);
}
- protected void drain(InputStream dis, long bytesRead) throws IOException
- {
- long toSkip = totalSize() - bytesRead;
-
- // InputStream.skip can return -1 if dis is inaccessible.
- long skipped = dis.skip(toSkip);
- if (skipped == -1)
- return;
-
- toSkip = toSkip - skipped;
- while (toSkip > 0)
- {
- skipped = dis.skip(toSkip);
- if (skipped == -1)
- break;
- toSkip = toSkip - skipped;
- }
- }
-
protected long totalSize()
{
long size = 0;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/streaming/StreamSession.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/streaming/StreamSession.java b/src/java/org/apache/cassandra/streaming/StreamSession.java
index 294b9c1..0f43f1f 100644
--- a/src/java/org/apache/cassandra/streaming/StreamSession.java
+++ b/src/java/org/apache/cassandra/streaming/StreamSession.java
@@ -105,10 +105,8 @@ import org.apache.cassandra.utils.concurrent.Refs;
* complete (received()). When all files for the StreamReceiveTask have been received, the sstables
* are added to the CFS (and 2ndary index are built, StreamReceiveTask.complete()) and the task
* is marked complete (taskCompleted())
- * (b) If during the streaming of a particular file an I/O error occurs on the receiving end of a stream
- * (FileMessage.deserialize), the node will retry the file (up to DatabaseDescriptor.getMaxStreamingRetries())
- * by sending a RetryMessage to the sender. On receiving a RetryMessage, the sender simply issue a new
- * FileMessage for that file.
+ * (b) If during the streaming of a particular file an error occurs on the receiving end of a stream
+ * (FileMessage.deserialize), the node will send a SessionFailedMessage to the sender and close the stream session.
* (c) When all transfer and receive tasks for a session are complete, the move to the Completion phase
* (maybeCompleted()).
*
@@ -149,8 +147,6 @@ public class StreamSession implements IEndpointStateChangeSubscriber
public final ConnectionHandler handler;
- private int retries;
-
private AtomicBoolean isAborted = new AtomicBoolean(false);
private final boolean keepSSTableLevel;
private final boolean isIncremental;
@@ -481,11 +477,6 @@ public class StreamSession implements IEndpointStateChangeSubscriber
received(received.cfId, received.sequenceNumber);
break;
- case RETRY:
- RetryMessage retry = (RetryMessage) message;
- retry(retry.cfId, retry.sequenceNumber);
- break;
-
case COMPLETE:
complete();
break;
@@ -610,18 +601,6 @@ public class StreamSession implements IEndpointStateChangeSubscriber
}
/**
- * Call back on receiving {@code StreamMessage.Type.RETRY} message.
- *
- * @param cfId ColumnFamily ID
- * @param sequenceNumber Sequence number to indicate which file to stream again
- */
- public void retry(UUID cfId, int sequenceNumber)
- {
- OutgoingFileMessage message = transfers.get(cfId).createMessageForRetry(sequenceNumber);
- handler.sendMessage(message);
- }
-
- /**
* Check if session is completed on receiving {@code StreamMessage.Type.COMPLETE} message.
*/
public synchronized void complete()
@@ -651,17 +630,6 @@ public class StreamSession implements IEndpointStateChangeSubscriber
closeSession(State.FAILED);
}
- public void doRetry(FileMessageHeader header, Throwable e)
- {
- logger.warn("[Stream #{}] Retrying for following error", planId(), e);
- // retry
- retries++;
- if (retries > DatabaseDescriptor.getMaxStreamingRetries())
- onError(new IOException("Too many retries for " + header, e));
- else
- handler.sendMessage(new RetryMessage(header.cfId, header.sequenceNumber));
- }
-
/**
* @return Current snapshot of this session info.
*/
http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java b/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
index 489fed9..d08ffa9 100644
--- a/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
+++ b/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
@@ -63,8 +63,17 @@ public class CompressedInputStream extends InputStream
// raw checksum bytes
private final byte[] checksumBytes = new byte[4];
+ /**
+ * Indicates there was a problem when reading from source stream.
+ * When this is added to the <code>dataBuffer</code> by the stream Reader,
+ * it is expected that the <code>readException</code> variable is populated
+ * with the cause of the error when reading from source stream, so it is
+ * thrown to the consumer on subsequent read operation.
+ */
private static final byte[] POISON_PILL = new byte[0];
+ protected volatile IOException readException = null;
+
private long totalCompressedBytesRead;
/**
@@ -84,13 +93,19 @@ public class CompressedInputStream extends InputStream
public int read() throws IOException
{
+ if (readException != null)
+ throw readException;
+
if (current >= bufferOffset + buffer.length || validBufferBytes == -1)
{
try
{
byte[] compressedWithCRC = dataBuffer.take();
if (compressedWithCRC == POISON_PILL)
- throw new EOFException("No chunk available");
+ {
+ assert readException != null;
+ throw readException;
+ }
decompress(compressedWithCRC);
}
catch (InterruptedException e)
@@ -138,7 +153,7 @@ public class CompressedInputStream extends InputStream
return totalCompressedBytesRead;
}
- static class Reader extends WrappedRunnable
+ class Reader extends WrappedRunnable
{
private final InputStream source;
private final Iterator<CompressionMetadata.Chunk> chunks;
@@ -169,6 +184,7 @@ public class CompressedInputStream extends InputStream
int r = source.read(compressedWithCRC, bufferRead, readLength - bufferRead);
if (r < 0)
{
+ readException = new EOFException("No chunk available");
dataBuffer.put(POISON_PILL);
return; // throw exception where we consume dataBuffer
}
@@ -177,6 +193,7 @@ public class CompressedInputStream extends InputStream
catch (IOException e)
{
logger.warn("Error while reading compressed input stream.", e);
+ readException = e;
dataBuffer.put(POISON_PILL);
return; // throw exception where we consume dataBuffer
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java b/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
index c684e4f..fa1022d 100644
--- a/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
+++ b/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
@@ -44,6 +44,8 @@ import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.BytesReadTracker;
import org.apache.cassandra.utils.Pair;
+import static org.apache.cassandra.utils.Throwables.extractIOExceptionCause;
+
/**
* StreamReader that reads from streamed compressed SSTable
*/
@@ -132,11 +134,9 @@ public class CompressedStreamReader extends StreamReader
e.addSuppressed(e2);
}
}
- drain(cis, in.getBytesRead());
- if (e instanceof IOException)
- throw (IOException) e;
- else
- throw Throwables.propagate(e);
+ if (extractIOExceptionCause(e).isPresent())
+ throw e;
+ throw Throwables.propagate(e);
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java b/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
index 31ab2a8..2870c03 100644
--- a/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
+++ b/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
@@ -22,6 +22,8 @@ import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
+import com.google.common.base.Optional;
+
import org.apache.cassandra.io.sstable.format.SSTableWriter;
import org.apache.cassandra.io.util.DataOutputStreamPlus;
import org.apache.cassandra.streaming.StreamReader;
@@ -29,6 +31,8 @@ import org.apache.cassandra.streaming.StreamSession;
import org.apache.cassandra.streaming.compress.CompressedStreamReader;
import org.apache.cassandra.utils.JVMStabilityInspector;
+import static org.apache.cassandra.utils.Throwables.extractIOExceptionCause;
+
/**
* IncomingFileMessage is used to receive the part(or whole) of a SSTable data file.
*/
@@ -48,26 +52,10 @@ public class IncomingFileMessage extends StreamMessage
{
return new IncomingFileMessage(reader.read(in), header);
}
- catch (IOException eof)
- {
- // Reading from remote failed(i.e. reached EOF before reading expected length of data).
- // This can be caused by network/node failure thus we are not retrying
- throw eof;
- }
catch (Throwable t)
{
- // Throwable can be Runtime error containing IOException.
- // In that case we don't want to retry.
- Throwable cause = t;
- while ((cause = cause.getCause()) != null)
- {
- if (cause instanceof IOException)
- throw (IOException) cause;
- }
JVMStabilityInspector.inspectThrowable(t);
- // Otherwise, we can retry
- session.doRetry(header, t);
- return null;
+ throw t;
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/streaming/messages/RetryMessage.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/streaming/messages/RetryMessage.java b/src/java/org/apache/cassandra/streaming/messages/RetryMessage.java
index 29e84bf..6673aa1 100644
--- a/src/java/org/apache/cassandra/streaming/messages/RetryMessage.java
+++ b/src/java/org/apache/cassandra/streaming/messages/RetryMessage.java
@@ -27,6 +27,10 @@ import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.streaming.StreamSession;
import org.apache.cassandra.utils.UUIDSerializer;
+/**
+ * @deprecated retry support removed on CASSANDRA-10992
+ */
+@Deprecated
public class RetryMessage extends StreamMessage
{
public static Serializer<RetryMessage> serializer = new Serializer<RetryMessage>()
http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/utils/Throwables.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/utils/Throwables.java b/src/java/org/apache/cassandra/utils/Throwables.java
index a895f31..877f388 100644
--- a/src/java/org/apache/cassandra/utils/Throwables.java
+++ b/src/java/org/apache/cassandra/utils/Throwables.java
@@ -18,6 +18,10 @@
*/
package org.apache.cassandra.utils;
+import java.io.IOException;
+
+import com.google.common.base.Optional;
+
public class Throwables
{
@@ -50,4 +54,17 @@ public class Throwables
}
return accumulate;
}
+
+ public static Optional<IOException> extractIOExceptionCause(Throwable t)
+ {
+ if (t instanceof IOException)
+ return Optional.of((IOException) t);
+ Throwable cause = t;
+ while ((cause = cause.getCause()) != null)
+ {
+ if (cause instanceof IOException)
+ return Optional.of((IOException) cause);
+ }
+ return Optional.absent();
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/test/unit/org/apache/cassandra/streaming/compress/CompressedInputStreamTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/streaming/compress/CompressedInputStreamTest.java b/test/unit/org/apache/cassandra/streaming/compress/CompressedInputStreamTest.java
index 0becd18..87d93fd 100644
--- a/test/unit/org/apache/cassandra/streaming/compress/CompressedInputStreamTest.java
+++ b/test/unit/org/apache/cassandra/streaming/compress/CompressedInputStreamTest.java
@@ -36,6 +36,7 @@ import org.apache.cassandra.io.sstable.metadata.MetadataCollector;
import org.apache.cassandra.utils.Pair;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
/**
*/
@@ -44,24 +45,33 @@ public class CompressedInputStreamTest
@Test
public void testCompressedRead() throws Exception
{
- testCompressedReadWith(new long[]{0L}, false);
- testCompressedReadWith(new long[]{1L}, false);
- testCompressedReadWith(new long[]{100L}, false);
+ testCompressedReadWith(new long[]{0L}, false, false);
+ testCompressedReadWith(new long[]{1L}, false, false);
+ testCompressedReadWith(new long[]{100L}, false, false);
- testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, false);
+ testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, false, false);
}
@Test(expected = EOFException.class)
public void testTruncatedRead() throws Exception
{
- testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, true);
+ testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, true, false);
+ }
+
+ /**
+ * Test that CompressedInputStream does not block if there's an exception while reading stream
+ */
+ @Test(timeout = 30000)
+ public void testException() throws Exception
+ {
+ testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, false, true);
}
/**
* @param valuesToCheck array of longs of range(0-999)
* @throws Exception
*/
- private void testCompressedReadWith(long[] valuesToCheck, boolean testTruncate) throws Exception
+ private void testCompressedReadWith(long[] valuesToCheck, boolean testTruncate, boolean testException) throws Exception
{
assert valuesToCheck != null && valuesToCheck.length > 0;
@@ -120,6 +130,12 @@ public class CompressedInputStreamTest
// read buffer using CompressedInputStream
CompressionInfo info = new CompressionInfo(chunks, param);
+
+ if (testException)
+ {
+ testException(sections, info);
+ return;
+ }
CompressedInputStream input = new CompressedInputStream(new ByteArrayInputStream(toRead), info);
try (DataInputStream in = new DataInputStream(input))
@@ -132,4 +148,25 @@ public class CompressedInputStreamTest
}
}
}
+
+ private static void testException(List<Pair<Long, Long>> sections, CompressionInfo info) throws IOException
+ {
+ CompressedInputStream input = new CompressedInputStream(new ByteArrayInputStream(new byte[0]), info);
+
+ try (DataInputStream in = new DataInputStream(input))
+ {
+ for (int i = 0; i < sections.size(); i++)
+ {
+ input.position(sections.get(i).left);
+ try {
+ in.readLong();
+ fail("Should have thrown IOException");
+ }
+ catch (IOException e)
+ {
+ continue;
+ }
+ }
+ }
+ }
}
[09/50] [abbrv] cassandra git commit: Add decay to histograms and
timers used for metrics
Posted by ms...@apache.org.
Add decay to histograms and timers used for metrics
Patch by Per Otterstrom; reviewed by tjake for CASSANDRA-11752
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/2e902596
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/2e902596
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/2e902596
Branch: refs/heads/cassandra-3.8
Commit: 2e90259669bddf04b6c1dba38b604aa6a33dcd47
Parents: 76e3100
Author: Per Otterstrom <pe...@ericsson.com>
Authored: Thu Jun 23 01:01:39 2016 +0200
Committer: T Jake Luciani <ja...@apache.org>
Committed: Wed Aug 10 14:37:45 2016 -0400
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../metrics/CassandraMetricsRegistry.java | 4 +-
.../cassandra/metrics/ClearableHistogram.java | 4 +-
.../DecayingEstimatedHistogramReservoir.java | 549 +++++++++++++++++++
.../metrics/EstimatedHistogramReservoir.java | 111 ----
.../cassandra/utils/EstimatedHistogram.java | 2 +-
...DecayingEstimatedHistogramReservoirTest.java | 381 +++++++++++++
7 files changed, 936 insertions(+), 116 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e902596/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 232203e..05059cc 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.2.8
+ * Add decay to histograms and timers used for metrics (CASSANDRA-11752)
* Fix hanging stream session (CASSANDRA-10992)
* Add byteman support for testing (CASSANDRA-12377)
* Fix INSERT JSON, fromJson() support of smallint, tinyint types (CASSANDRA-12371)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e902596/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java b/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
index 6fdb2ff..8e5671b 100644
--- a/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
+++ b/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
@@ -60,7 +60,7 @@ public class CassandraMetricsRegistry extends MetricRegistry
public Histogram histogram(MetricName name, boolean considerZeroes)
{
- Histogram histogram = register(name, new ClearableHistogram(new EstimatedHistogramReservoir(considerZeroes)));
+ Histogram histogram = register(name, new ClearableHistogram(new DecayingEstimatedHistogramReservoir(considerZeroes)));
registerMBean(histogram, name.getMBeanName());
return histogram;
@@ -68,7 +68,7 @@ public class CassandraMetricsRegistry extends MetricRegistry
public Timer timer(MetricName name)
{
- Timer timer = register(name, new Timer(new EstimatedHistogramReservoir(false)));
+ Timer timer = register(name, new Timer(new DecayingEstimatedHistogramReservoir()));
registerMBean(timer, name.getMBeanName());
return timer;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e902596/src/java/org/apache/cassandra/metrics/ClearableHistogram.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/metrics/ClearableHistogram.java b/src/java/org/apache/cassandra/metrics/ClearableHistogram.java
index 85f2fa9..4a081d8 100644
--- a/src/java/org/apache/cassandra/metrics/ClearableHistogram.java
+++ b/src/java/org/apache/cassandra/metrics/ClearableHistogram.java
@@ -26,14 +26,14 @@ import com.codahale.metrics.Histogram;
*/
public class ClearableHistogram extends Histogram
{
- private final EstimatedHistogramReservoir reservoirRef;
+ private final DecayingEstimatedHistogramReservoir reservoirRef;
/**
* Creates a new {@link com.codahale.metrics.Histogram} with the given reservoir.
*
* @param reservoir the reservoir to create a histogram from
*/
- public ClearableHistogram(EstimatedHistogramReservoir reservoir)
+ public ClearableHistogram(DecayingEstimatedHistogramReservoir reservoir)
{
super(reservoir);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e902596/src/java/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoir.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoir.java b/src/java/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoir.java
new file mode 100644
index 0000000..14a4366
--- /dev/null
+++ b/src/java/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoir.java
@@ -0,0 +1,549 @@
+/*
+ * 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.metrics;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicLongArray;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import com.codahale.metrics.Clock;
+import com.codahale.metrics.Reservoir;
+import com.codahale.metrics.Snapshot;
+import org.apache.cassandra.utils.EstimatedHistogram;
+
+/**
+ * A decaying histogram reservoir where values collected during each minute will be twice as significant as the values
+ * collected in the previous minute. Measured values are collected in variable sized buckets, using small buckets in the
+ * lower range and larger buckets in the upper range. Use this histogram when you want to know if the distribution of
+ * the underlying data stream has changed recently and you want high resolution on values in the lower range.
+ *
+ * The histogram use forward decay [1] to make recent values more significant. The forward decay factor will be doubled
+ * every minute (half-life time set to 60 seconds) [2]. The forward decay landmark is reset every 30 minutes (or at
+ * first read/update after 30 minutes). During landmark reset, updates and reads in the reservoir will be blocked in a
+ * fashion similar to the one used in the metrics library [3]. The 30 minute rescale interval is used based on the
+ * assumption that in an extreme case we would have to collect a metric 1M times for a single bucket each second. By the
+ * end of the 30:th minute all collected values will roughly add up to 1.000.000 * 60 * pow(2, 30) which can be
+ * represented with 56 bits giving us some head room in a signed 64 bit long.
+ *
+ * Internally two reservoirs are maintained, one with decay and one without decay. All public getters in a {@Snapshot}
+ * will expose the decay functionality with the exception of the {@link Snapshot#getValues()} which will return values
+ * from the reservoir without decay. This makes it possible for the caller to maintain precise deltas in an interval of
+ * its choise.
+ *
+ * The bucket size starts at 1 and grows by 1.2 each time (rounding and removing duplicates). It goes from 1 to around
+ * 18T by default (creating 164+1 buckets), which will give a timing resolution from microseconds to roughly 210 days,
+ * with less precision as the numbers get larger.
+ *
+ * The series of values to which the counts in `decayingBuckets` correspond:
+ * 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 17, 20, 24, 29, 35, 42, 50, 60, 72 etc.
+ * Thus, a `decayingBuckets` of [0, 0, 1, 10] would mean we had seen 1 value of 3 and 10 values of 4.
+ *
+ * Each bucket represents values from (previous bucket offset, current offset].
+ *
+ * [1]: http://dimacs.rutgers.edu/~graham/pubs/papers/fwddecay.pdf
+ * [2]: https://en.wikipedia.org/wiki/Half-life
+ * [3]: https://github.com/dropwizard/metrics/blob/v3.1.2/metrics-core/src/main/java/com/codahale/metrics/ExponentiallyDecayingReservoir.java
+ */
+public class DecayingEstimatedHistogramReservoir implements Reservoir
+{
+ /**
+ * The default number of decayingBuckets. Use this bucket count to reduce memory allocation for bucket offsets.
+ */
+ public static final int DEFAULT_BUCKET_COUNT = 164;
+ public static final boolean DEFAULT_ZERO_CONSIDERATION = false;
+
+ // The offsets used with a default sized bucket array without a separate bucket for zero values.
+ public static final long[] DEFAULT_WITHOUT_ZERO_BUCKET_OFFSETS = EstimatedHistogram.newOffsets(DEFAULT_BUCKET_COUNT, false);
+
+ // The offsets used with a default sized bucket array with a separate bucket for zero values.
+ public static final long[] DEFAULT_WITH_ZERO_BUCKET_OFFSETS = EstimatedHistogram.newOffsets(DEFAULT_BUCKET_COUNT, true);
+
+ // Represents the bucket offset as created by {@link EstimatedHistogram#newOffsets()}
+ private final long[] bucketOffsets;
+
+ // decayingBuckets and buckets are one element longer than bucketOffsets -- the last element is values greater than the last offset
+ private final AtomicLongArray decayingBuckets;
+ private final AtomicLongArray buckets;
+
+ public static final long HALF_TIME_IN_S = 60L;
+ public static final double MEAN_LIFETIME_IN_S = HALF_TIME_IN_S / Math.log(2.0);
+ public static final long LANDMARK_RESET_INTERVAL_IN_MS = 30L * 60L * 1000L;
+
+ private final AtomicBoolean rescaling = new AtomicBoolean(false);
+ private volatile long decayLandmark;
+
+ private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+
+ // Wrapper around System.nanoTime() to simplify unit testing.
+ private final Clock clock;
+
+
+ /**
+ * Construct a decaying histogram with default number of buckets and without considering zeroes.
+ */
+ public DecayingEstimatedHistogramReservoir()
+ {
+ this(DEFAULT_ZERO_CONSIDERATION, DEFAULT_BUCKET_COUNT, Clock.defaultClock());
+ }
+
+ /**
+ * Construct a decaying histogram with default number of buckets.
+ *
+ * @param considerZeroes when true, 0-value measurements in a separate bucket, otherwise they will be collected in
+ * same bucket as 1-value measurements
+ */
+ public DecayingEstimatedHistogramReservoir(boolean considerZeroes)
+ {
+ this(considerZeroes, DEFAULT_BUCKET_COUNT, Clock.defaultClock());
+ }
+
+ /**
+ * Construct a decaying histogram.
+ *
+ * @param considerZeroes when true, 0-value measurements in a separate bucket, otherwise they will be collected in
+ * same bucket as 1-value measurements
+ * @param bucketCount number of buckets used to collect measured values
+ */
+ public DecayingEstimatedHistogramReservoir(boolean considerZeroes, int bucketCount)
+ {
+ this(considerZeroes, bucketCount, Clock.defaultClock());
+ }
+
+ @VisibleForTesting
+ DecayingEstimatedHistogramReservoir(boolean considerZeroes, int bucketCount, Clock clock)
+ {
+ if (bucketCount == DEFAULT_BUCKET_COUNT)
+ {
+ if (considerZeroes == true)
+ {
+ bucketOffsets = DEFAULT_WITH_ZERO_BUCKET_OFFSETS;
+ }
+ else
+ {
+ bucketOffsets = DEFAULT_WITHOUT_ZERO_BUCKET_OFFSETS;
+ }
+ }
+ else
+ {
+ bucketOffsets = EstimatedHistogram.newOffsets(bucketCount, considerZeroes);
+ }
+ decayingBuckets = new AtomicLongArray(bucketOffsets.length + 1);
+ buckets = new AtomicLongArray(bucketOffsets.length + 1);
+ this.clock = clock;
+ decayLandmark = clock.getTime();
+ }
+
+ /**
+ * Increments the count of the bucket closest to n, rounding UP.
+ *
+ * @param value the data point to add to the histogram
+ */
+ public void update(long value)
+ {
+ long now = clock.getTime();
+ rescaleIfNeeded(now);
+
+ int index = Arrays.binarySearch(bucketOffsets, value);
+ if (index < 0)
+ {
+ // inexact match, take the first bucket higher than n
+ index = -index - 1;
+ }
+ // else exact match; we're good
+
+ lockForRegularUsage();
+
+ try
+ {
+ decayingBuckets.getAndAdd(index, forwardDecayWeight(now));
+ }
+ finally
+ {
+ unlockForRegularUsage();
+ }
+
+ buckets.getAndIncrement(index);
+ }
+
+ private long forwardDecayWeight(long now)
+ {
+ return Math.round(Math.exp(((now - decayLandmark) / 1000L) / MEAN_LIFETIME_IN_S));
+ }
+
+ /**
+ * Return the number of buckets where recorded values are stored.
+ *
+ * This method does not return the number of recorded values as suggested by the {@link Reservoir} interface.
+ *
+ * @return the number of buckets
+ */
+ public int size()
+ {
+ return decayingBuckets.length();
+ }
+
+ /**
+ * Returns a snapshot of the decaying values in this reservoir.
+ *
+ * Non-decaying reservoir will not be included in the snapshot.
+ *
+ * @return the snapshot
+ */
+ public Snapshot getSnapshot()
+ {
+ rescaleIfNeeded();
+
+ lockForRegularUsage();
+
+ try
+ {
+ return new EstimatedHistogramReservoirSnapshot(this);
+ }
+ finally
+ {
+ unlockForRegularUsage();
+ }
+ }
+
+ /**
+ * @return true if this histogram has overflowed -- that is, a value larger than our largest bucket could bound was added
+ */
+ @VisibleForTesting
+ boolean isOverflowed()
+ {
+ return decayingBuckets.get(decayingBuckets.length() - 1) > 0;
+ }
+
+ private void rescaleIfNeeded()
+ {
+ rescaleIfNeeded(clock.getTime());
+ }
+
+ private void rescaleIfNeeded(long now)
+ {
+ if (needRescale(now))
+ {
+ if (rescaling.compareAndSet(false, true))
+ {
+ try
+ {
+ rescale(now);
+ }
+ finally
+ {
+ rescaling.set(false);
+ }
+ }
+ }
+ }
+
+ private void rescale(long now)
+ {
+ // Check again to make sure that another thread didn't complete rescale already
+ if (needRescale(now))
+ {
+ lockForRescale();
+
+ try
+ {
+ final long rescaleFactor = forwardDecayWeight(now);
+ decayLandmark = now;
+
+ final int bucketCount = decayingBuckets.length();
+ for (int i = 0; i < bucketCount; i++)
+ {
+ long newValue = Math.round((decayingBuckets.get(i) / rescaleFactor));
+ decayingBuckets.set(i, newValue);
+ }
+ }
+ finally
+ {
+ unlockForRescale();
+ }
+ }
+ }
+
+ private boolean needRescale(long now)
+ {
+ return (now - decayLandmark) > LANDMARK_RESET_INTERVAL_IN_MS;
+ }
+
+ @VisibleForTesting
+ public void clear()
+ {
+ lockForRescale();
+
+ try
+ {
+ final int bucketCount = decayingBuckets.length();
+ for (int i = 0; i < bucketCount; i++)
+ {
+ decayingBuckets.set(i, 0L);
+ buckets.set(i, 0L);
+ }
+ }
+ finally
+ {
+ unlockForRescale();
+ }
+ }
+
+ private void lockForRegularUsage()
+ {
+ this.lock.readLock().lock();
+ }
+
+ private void unlockForRegularUsage()
+ {
+ this.lock.readLock().unlock();
+ }
+
+ private void lockForRescale()
+ {
+ this.lock.writeLock().lock();
+ }
+
+ private void unlockForRescale()
+ {
+ this.lock.writeLock().unlock();
+ }
+
+
+ private static final Charset UTF_8 = Charset.forName("UTF-8");
+
+ /**
+ * Represents a snapshot of the decaying histogram.
+ *
+ * The decaying buckets are copied into a snapshot array to give a consistent view for all getters. However, the
+ * copy is made without a write-lock and so other threads may change the buckets while the array is copied,
+ * probably causign a slight skew up in the quantiles and mean values.
+ *
+ * The decaying buckets will be used for quantile calculations and mean values, but the non decaying buckets will be
+ * exposed for calls to {@link Snapshot#getValues()}.
+ */
+ private class EstimatedHistogramReservoirSnapshot extends Snapshot
+ {
+ private final long[] decayingBuckets;
+
+ public EstimatedHistogramReservoirSnapshot(DecayingEstimatedHistogramReservoir reservoir)
+ {
+ final int length = reservoir.decayingBuckets.length();
+
+ this.decayingBuckets = new long[length];
+
+ for (int i = 0; i < length; i++)
+ this.decayingBuckets[i] = reservoir.decayingBuckets.get(i);
+ }
+
+ /**
+ * Get the estimated value at the specified quantile in the distribution.
+ *
+ * @param quantile the quantile specified as a value between 0.0 (zero) and 1.0 (one)
+ * @return estimated value at given quantile
+ * @throws IllegalStateException in case the histogram overflowed
+ */
+ public double getValue(double quantile)
+ {
+ assert quantile >= 0 && quantile <= 1.0;
+
+ final int lastBucket = decayingBuckets.length - 1;
+
+ if (decayingBuckets[lastBucket] > 0)
+ throw new IllegalStateException("Unable to compute when histogram overflowed");
+
+ final long qcount = (long) Math.ceil(count() * quantile);
+ if (qcount == 0)
+ return 0;
+
+ long elements = 0;
+ for (int i = 0; i < lastBucket; i++)
+ {
+ elements += decayingBuckets[i];
+ if (elements >= qcount)
+ return bucketOffsets[i];
+ }
+ return 0;
+ }
+
+ /**
+ * Will return a snapshot of the non-decaying buckets.
+ *
+ * The values returned will not be consistent with the quantile and mean values. The caller must be aware of the
+ * offsets created by {@link EstimatedHistogram#getBucketOffsets()} to make use of the values returned.
+ *
+ * @return a snapshot of the non-decaying buckets.
+ */
+ public long[] getValues()
+ {
+ final int length = buckets.length();
+
+ long[] values = new long[length];
+
+ for (int i = 0; i < length; i++)
+ values[i] = buckets.get(i);
+
+ return values;
+ }
+
+ /**
+ * Return the number of buckets where recorded values are stored.
+ *
+ * This method does not return the number of recorded values as suggested by the {@link Snapshot} interface.
+ *
+ * @return the number of buckets
+ */
+ public int size()
+ {
+ return decayingBuckets.length;
+ }
+
+ /**
+ * Return the number of registered values taking forward decay into account.
+ *
+ * @return the sum of all bucket values
+ */
+ private long count()
+ {
+ long sum = 0L;
+ for (int i = 0; i < decayingBuckets.length; i++)
+ sum += decayingBuckets[i];
+ return sum;
+ }
+
+ /**
+ * Get the estimated max-value that could have been added to this reservoir.
+ *
+ * As values are collected in variable sized buckets, the actual max value recored in the reservoir may be less
+ * than the value returned.
+ *
+ * @return the largest value that could have been added to this reservoir, or Long.MAX_VALUE if the reservoir
+ * overflowed
+ */
+ public long getMax()
+ {
+ final int lastBucket = decayingBuckets.length - 1;
+
+ if (decayingBuckets[lastBucket] > 0)
+ return Long.MAX_VALUE;
+
+ for (int i = lastBucket - 1; i >= 0; i--)
+ {
+ if (decayingBuckets[i] > 0)
+ return bucketOffsets[i];
+ }
+ return 0;
+ }
+
+ /**
+ * Get the estimated mean value in the distribution.
+ *
+ * @return the mean histogram value (average of bucket offsets, weighted by count)
+ * @throws IllegalStateException if any values were greater than the largest bucket threshold
+ */
+ public double getMean()
+ {
+ final int lastBucket = decayingBuckets.length - 1;
+
+ if (decayingBuckets[lastBucket] > 0)
+ throw new IllegalStateException("Unable to compute when histogram overflowed");
+
+ long elements = 0;
+ long sum = 0;
+ for (int i = 0; i < lastBucket; i++)
+ {
+ long bCount = decayingBuckets[i];
+ elements += bCount;
+ sum += bCount * bucketOffsets[i];
+ }
+
+ return (double) sum / elements;
+ }
+
+ /**
+ * Get the estimated min-value that could have been added to this reservoir.
+ *
+ * As values are collected in variable sized buckets, the actual min value recored in the reservoir may be
+ * higher than the value returned.
+ *
+ * @return the smallest value that could have been added to this reservoir
+ */
+ public long getMin()
+ {
+ for (int i = 0; i < decayingBuckets.length; i++)
+ {
+ if (decayingBuckets[i] > 0)
+ return i == 0 ? 0 : 1 + bucketOffsets[i - 1];
+ }
+ return 0;
+ }
+
+ /**
+ * Get the estimated standard deviation of the values added to this reservoir.
+ *
+ * As values are collected in variable sized buckets, the actual deviation may be more or less than the value
+ * returned.
+ *
+ * @return an estimate of the standard deviation
+ */
+ public double getStdDev()
+ {
+ final int lastBucket = decayingBuckets.length - 1;
+
+ if (decayingBuckets[lastBucket] > 0)
+ throw new IllegalStateException("Unable to compute when histogram overflowed");
+
+ final long count = count();
+
+ if(count <= 1) {
+ return 0.0D;
+ } else {
+ double mean = this.getMean();
+ double sum = 0.0D;
+
+ for(int i = 0; i < lastBucket; ++i) {
+ long value = bucketOffsets[i];
+ double diff = (double)value - mean;
+ sum += diff * diff * decayingBuckets[i];
+ }
+
+ return Math.sqrt(sum / (double)(count - 1));
+ }
+ }
+
+ public void dump(OutputStream output)
+ {
+ try (PrintWriter out = new PrintWriter(new OutputStreamWriter(output, UTF_8)))
+ {
+ int length = decayingBuckets.length;
+
+ for(int i = 0; i < length; ++i) {
+ out.printf("%d%n", decayingBuckets[i]);
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e902596/src/java/org/apache/cassandra/metrics/EstimatedHistogramReservoir.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/metrics/EstimatedHistogramReservoir.java b/src/java/org/apache/cassandra/metrics/EstimatedHistogramReservoir.java
deleted file mode 100644
index 29baad8..0000000
--- a/src/java/org/apache/cassandra/metrics/EstimatedHistogramReservoir.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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.metrics;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import com.codahale.metrics.Reservoir;
-import com.codahale.metrics.Snapshot;
-import com.codahale.metrics.UniformSnapshot;
-import org.apache.cassandra.utils.EstimatedHistogram;
-
-/**
- * Allows our Histogram implementation to be used by the metrics library.
- *
- * Default buckets allows nanosecond timings.
- */
-public class EstimatedHistogramReservoir implements Reservoir
-{
- EstimatedHistogram histogram;
-
- // Default to >4 hours of in nanoseconds of buckets
- public EstimatedHistogramReservoir(boolean considerZeroes)
- {
- this(164, considerZeroes);
- }
-
- public EstimatedHistogramReservoir(int numBuckets, boolean considerZeroes)
- {
- histogram = new EstimatedHistogram(numBuckets, considerZeroes);
- }
-
- @Override
- public int size()
- {
- return histogram.getBucketOffsets().length + 1;
- }
-
- @Override
- public void update(long value)
- {
- histogram.add(value);
- }
-
- @Override
- public Snapshot getSnapshot()
- {
- return new HistogramSnapshot(histogram);
- }
-
- @VisibleForTesting
- public void clear()
- {
- histogram.getBuckets(true);
- }
-
- static class HistogramSnapshot extends UniformSnapshot
- {
- EstimatedHistogram histogram;
-
- public HistogramSnapshot(EstimatedHistogram histogram)
- {
- super(histogram.getBuckets(false));
-
- this.histogram = histogram;
- }
-
- @Override
- public double getValue(double quantile)
- {
- return histogram.percentile(quantile);
- }
-
- @Override
- public long getMax()
- {
- return histogram.max();
- }
-
- @Override
- public long getMin()
- {
- return histogram.min();
- }
-
- @Override
- public double getMean()
- {
- return histogram.rawMean();
- }
-
- @Override
- public long[] getValues() {
- return histogram.getBuckets(false);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e902596/src/java/org/apache/cassandra/utils/EstimatedHistogram.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/utils/EstimatedHistogram.java b/src/java/org/apache/cassandra/utils/EstimatedHistogram.java
index 36048fb..1a48039 100644
--- a/src/java/org/apache/cassandra/utils/EstimatedHistogram.java
+++ b/src/java/org/apache/cassandra/utils/EstimatedHistogram.java
@@ -85,7 +85,7 @@ public class EstimatedHistogram
buckets = new AtomicLongArray(bucketData);
}
- private static long[] newOffsets(int size, boolean considerZeroes)
+ public static long[] newOffsets(int size, boolean considerZeroes)
{
long[] result = new long[size + (considerZeroes ? 1 : 0)];
int i = 0;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e902596/test/unit/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoirTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoirTest.java b/test/unit/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoirTest.java
new file mode 100644
index 0000000..f2d817f
--- /dev/null
+++ b/test/unit/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoirTest.java
@@ -0,0 +1,381 @@
+/*
+ * 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.metrics;
+
+import org.junit.Test;
+
+import com.codahale.metrics.Clock;
+import com.codahale.metrics.Snapshot;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+
+public class DecayingEstimatedHistogramReservoirTest
+{
+ private static final double DOUBLE_ASSERT_DELTA = 0;
+
+ @Test
+ public void testSimple()
+ {
+ {
+ // 0 and 1 map to the same, first bucket
+ DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir();
+ histogram.update(0);
+ assertEquals(1, histogram.getSnapshot().getValues()[0]);
+ histogram.update(1);
+ assertEquals(2, histogram.getSnapshot().getValues()[0]);
+ }
+ {
+ // 0 and 1 map to different buckets
+ DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(true, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT);
+ histogram.update(0);
+ assertEquals(1, histogram.getSnapshot().getValues()[0]);
+ histogram.update(1);
+ Snapshot snapshot = histogram.getSnapshot();
+ assertEquals(1, snapshot.getValues()[0]);
+ assertEquals(1, snapshot.getValues()[1]);
+ }
+ }
+
+ @Test
+ public void testOverflow()
+ {
+ DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, 1);
+ histogram.update(100);
+ assert histogram.isOverflowed();
+ assertEquals(Long.MAX_VALUE, histogram.getSnapshot().getMax());
+ }
+
+ @Test
+ public void testMinMax()
+ {
+ DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir();
+ histogram.update(16);
+ Snapshot snapshot = histogram.getSnapshot();
+ assertEquals(15, snapshot.getMin());
+ assertEquals(17, snapshot.getMax());
+ }
+
+ @Test
+ public void testMean()
+ {
+ {
+ TestClock clock = new TestClock();
+
+ DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+ for (int i = 0; i < 40; i++)
+ histogram.update(0);
+ for (int i = 0; i < 20; i++)
+ histogram.update(1);
+ for (int i = 0; i < 10; i++)
+ histogram.update(2);
+ assertEquals(1.14D, histogram.getSnapshot().getMean(), 0.1D);
+ }
+ {
+ TestClock clock = new TestClock();
+
+ DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(true, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+ for (int i = 0; i < 40; i++)
+ histogram.update(0);
+ for (int i = 0; i < 20; i++)
+ histogram.update(1);
+ for (int i = 0; i < 10; i++)
+ histogram.update(2);
+ assertEquals(0.57D, histogram.getSnapshot().getMean(), 0.1D);
+ }
+ }
+
+ @Test
+ public void testStdDev()
+ {
+ {
+ TestClock clock = new TestClock();
+
+ DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+ for (int i = 0; i < 20; i++)
+ histogram.update(10);
+ for (int i = 0; i < 40; i++)
+ histogram.update(20);
+ for (int i = 0; i < 20; i++)
+ histogram.update(30);
+
+ Snapshot snapshot = histogram.getSnapshot();
+ assertEquals(20.0D, snapshot.getMean(), 2.0D);
+ assertEquals(7.07D, snapshot.getStdDev(), 2.0D);
+ }
+ }
+
+ @Test
+ public void testFindingCorrectBuckets()
+ {
+ TestClock clock = new TestClock();
+
+ DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, 90, clock);
+ histogram.update(23282687);
+ assertFalse(histogram.isOverflowed());
+ assertEquals(1, histogram.getSnapshot().getValues()[89]);
+
+ histogram.update(9);
+ assertEquals(1, histogram.getSnapshot().getValues()[8]);
+
+ histogram.update(21);
+ histogram.update(22);
+ Snapshot snapshot = histogram.getSnapshot();
+ assertEquals(2, snapshot.getValues()[13]);
+ assertEquals(6277304.5D, snapshot.getMean(), DOUBLE_ASSERT_DELTA);
+ }
+
+ @Test
+ public void testPercentile()
+ {
+ {
+ TestClock clock = new TestClock();
+
+ DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+ // percentile of empty histogram is 0
+ assertEquals(0D, histogram.getSnapshot().getValue(0.99), DOUBLE_ASSERT_DELTA);
+
+ histogram.update(1);
+ // percentile of a histogram with one element should be that element
+ assertEquals(1D, histogram.getSnapshot().getValue(0.99), DOUBLE_ASSERT_DELTA);
+
+ histogram.update(10);
+ assertEquals(10D, histogram.getSnapshot().getValue(0.99), DOUBLE_ASSERT_DELTA);
+ }
+
+ {
+ TestClock clock = new TestClock();
+
+ DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+
+ histogram.update(1);
+ histogram.update(2);
+ histogram.update(3);
+ histogram.update(4);
+ histogram.update(5);
+
+ Snapshot snapshot = histogram.getSnapshot();
+ assertEquals(0, snapshot.getValue(0.00), DOUBLE_ASSERT_DELTA);
+ assertEquals(3, snapshot.getValue(0.50), DOUBLE_ASSERT_DELTA);
+ assertEquals(3, snapshot.getValue(0.60), DOUBLE_ASSERT_DELTA);
+ assertEquals(5, snapshot.getValue(1.00), DOUBLE_ASSERT_DELTA);
+ }
+
+ {
+ TestClock clock = new TestClock();
+
+ DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+
+ for (int i = 11; i <= 20; i++)
+ histogram.update(i);
+
+ // Right now the histogram looks like:
+ // 10 12 14 17 20
+ // 0 2 2 3 3
+ // %: 0 20 40 70 100
+ Snapshot snapshot = histogram.getSnapshot();
+ assertEquals(12, snapshot.getValue(0.01), DOUBLE_ASSERT_DELTA);
+ assertEquals(14, snapshot.getValue(0.30), DOUBLE_ASSERT_DELTA);
+ assertEquals(17, snapshot.getValue(0.50), DOUBLE_ASSERT_DELTA);
+ assertEquals(17, snapshot.getValue(0.60), DOUBLE_ASSERT_DELTA);
+ assertEquals(20, snapshot.getValue(0.80), DOUBLE_ASSERT_DELTA);
+ }
+ {
+ TestClock clock = new TestClock();
+
+ DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(true, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+ histogram.update(0);
+ histogram.update(0);
+ histogram.update(1);
+
+ Snapshot snapshot = histogram.getSnapshot();
+ assertEquals(0, snapshot.getValue(0.5), DOUBLE_ASSERT_DELTA);
+ assertEquals(1, snapshot.getValue(0.99), DOUBLE_ASSERT_DELTA);
+ }
+ }
+
+
+ @Test
+ public void testDecayingPercentile()
+ {
+ {
+ TestClock clock = new TestClock();
+
+ DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+ // percentile of empty histogram is 0
+ assertEquals(0, histogram.getSnapshot().getValue(1.0), DOUBLE_ASSERT_DELTA);
+
+ for (int v = 1; v <= 100; v++)
+ {
+ for (int i = 0; i < 10_000; i++)
+ {
+ histogram.update(v);
+ }
+ }
+
+ Snapshot snapshot = histogram.getSnapshot();
+ assertEstimatedQuantile(05, snapshot.getValue(0.05));
+ assertEstimatedQuantile(20, snapshot.getValue(0.20));
+ assertEstimatedQuantile(40, snapshot.getValue(0.40));
+ assertEstimatedQuantile(99, snapshot.getValue(0.99));
+
+ clock.addSeconds(DecayingEstimatedHistogramReservoir.HALF_TIME_IN_S);
+ snapshot = histogram.getSnapshot();
+ assertEstimatedQuantile(05, snapshot.getValue(0.05));
+ assertEstimatedQuantile(20, snapshot.getValue(0.20));
+ assertEstimatedQuantile(40, snapshot.getValue(0.40));
+ assertEstimatedQuantile(99, snapshot.getValue(0.99));
+
+ for (int v = 1; v <= 50; v++)
+ {
+ for (int i = 0; i < 10_000; i++)
+ {
+ histogram.update(v);
+ }
+ }
+
+ snapshot = histogram.getSnapshot();
+ assertEstimatedQuantile(04, snapshot.getValue(0.05));
+ assertEstimatedQuantile(14, snapshot.getValue(0.20));
+ assertEstimatedQuantile(27, snapshot.getValue(0.40));
+ assertEstimatedQuantile(98, snapshot.getValue(0.99));
+
+ clock.addSeconds(DecayingEstimatedHistogramReservoir.HALF_TIME_IN_S);
+ snapshot = histogram.getSnapshot();
+ assertEstimatedQuantile(04, snapshot.getValue(0.05));
+ assertEstimatedQuantile(14, snapshot.getValue(0.20));
+ assertEstimatedQuantile(27, snapshot.getValue(0.40));
+ assertEstimatedQuantile(98, snapshot.getValue(0.99));
+
+ for (int v = 1; v <= 50; v++)
+ {
+ for (int i = 0; i < 10_000; i++)
+ {
+ histogram.update(v);
+ }
+ }
+
+ snapshot = histogram.getSnapshot();
+ assertEstimatedQuantile(03, snapshot.getValue(0.05));
+ assertEstimatedQuantile(12, snapshot.getValue(0.20));
+ assertEstimatedQuantile(23, snapshot.getValue(0.40));
+ assertEstimatedQuantile(96, snapshot.getValue(0.99));
+
+ clock.addSeconds(DecayingEstimatedHistogramReservoir.HALF_TIME_IN_S);
+ snapshot = histogram.getSnapshot();
+ assertEstimatedQuantile(03, snapshot.getValue(0.05));
+ assertEstimatedQuantile(12, snapshot.getValue(0.20));
+ assertEstimatedQuantile(23, snapshot.getValue(0.40));
+ assertEstimatedQuantile(96, snapshot.getValue(0.99));
+
+ for (int v = 11; v <= 20; v++)
+ {
+ for (int i = 0; i < 5_000; i++)
+ {
+ histogram.update(v);
+ }
+ }
+
+ snapshot = histogram.getSnapshot();
+ assertEstimatedQuantile(04, snapshot.getValue(0.05));
+ assertEstimatedQuantile(12, snapshot.getValue(0.20));
+ assertEstimatedQuantile(20, snapshot.getValue(0.40));
+ assertEstimatedQuantile(95, snapshot.getValue(0.99));
+
+ clock.addSeconds(DecayingEstimatedHistogramReservoir.HALF_TIME_IN_S);
+ snapshot = histogram.getSnapshot();
+ assertEstimatedQuantile(04, snapshot.getValue(0.05));
+ assertEstimatedQuantile(12, snapshot.getValue(0.20));
+ assertEstimatedQuantile(20, snapshot.getValue(0.40));
+ assertEstimatedQuantile(95, snapshot.getValue(0.99));
+
+ }
+
+ {
+ TestClock clock = new TestClock();
+
+ DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+ // percentile of empty histogram is 0
+ assertEquals(0, histogram.getSnapshot().getValue(0.99), DOUBLE_ASSERT_DELTA);
+
+ for (int m = 0; m < 40; m++)
+ {
+ for (int i = 0; i < 1_000_000; i++)
+ {
+ histogram.update(2);
+ }
+ // percentile of a histogram with one element should be that element
+ clock.addSeconds(DecayingEstimatedHistogramReservoir.HALF_TIME_IN_S);
+ assertEquals(2, histogram.getSnapshot().getValue(0.99), DOUBLE_ASSERT_DELTA);
+ }
+
+ clock.addSeconds(DecayingEstimatedHistogramReservoir.HALF_TIME_IN_S * 100);
+ assertEquals(0, histogram.getSnapshot().getValue(0.99), DOUBLE_ASSERT_DELTA);
+ }
+
+ {
+ TestClock clock = new TestClock();
+
+ DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+
+ histogram.update(20);
+ histogram.update(21);
+ histogram.update(22);
+ Snapshot snapshot = histogram.getSnapshot();
+ assertEquals(1, snapshot.getValues()[12]);
+ assertEquals(2, snapshot.getValues()[13]);
+
+ clock.addSeconds(DecayingEstimatedHistogramReservoir.HALF_TIME_IN_S);
+
+ histogram.update(20);
+ histogram.update(21);
+ histogram.update(22);
+ snapshot = histogram.getSnapshot();
+ assertEquals(2, snapshot.getValues()[12]);
+ assertEquals(4, snapshot.getValues()[13]);
+ }
+ }
+
+ private void assertEstimatedQuantile(long expectedValue, double actualValue)
+ {
+ assertTrue("Expected at least [" + expectedValue + "] but actual is [" + actualValue + "]", actualValue >= expectedValue);
+ assertTrue("Expected less than [" + Math.round(expectedValue * 1.2) + "] but actual is [" + actualValue + "]", actualValue < Math.round(expectedValue * 1.2));
+ }
+
+ public class TestClock extends Clock {
+ private long tick = 0;
+
+ public void addSeconds(long seconds)
+ {
+ tick += seconds * 1_000_000_000L;
+ }
+
+ public long getTick()
+ {
+ return tick;
+ }
+
+ public long getTime()
+ {
+ return tick / 1_000_000L;
+ };
+ }
+}
[29/50] [abbrv] cassandra git commit: Merge branch 'cassandra-2.2'
into cassandra-3.0
Posted by ms...@apache.org.
Merge branch 'cassandra-2.2' into cassandra-3.0
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5fdb90e1
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5fdb90e1
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5fdb90e1
Branch: refs/heads/cassandra-3.8
Commit: 5fdb90e12b04fa4209993f609206e6920138d851
Parents: 5cef78a 91f7387
Author: Tyler Hobbs <ty...@gmail.com>
Authored: Fri Aug 12 18:29:36 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Fri Aug 12 18:29:36 2016 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
NEWS.txt | 4 +--
.../cassandra/config/DatabaseDescriptor.java | 6 ++--
.../cassandra/locator/Ec2MultiRegionSnitch.java | 6 +++-
.../cassandra/service/StorageService.java | 4 +--
.../org/apache/cassandra/transport/Server.java | 2 +-
.../org/apache/cassandra/utils/FBUtilities.java | 20 ++++++++++++
.../apache/cassandra/utils/FBUtilitiesTest.java | 32 ++++++++++++++++++++
8 files changed, 67 insertions(+), 8 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5fdb90e1/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index cccb62d,394598a..7ba8370
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,40 -1,5 +1,41 @@@
-2.2.8
+3.0.9
+ * Fix clean interval not sent to commit log for empty memtable flush (CASSANDRA-12436)
+ * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
+ * Backport CASSANDRA-12002 (CASSANDRA-12177)
+ * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
+ * Fix potential bad messaging service message for paged range reads
+ within mixed-version 3.x clusters (CASSANDRA-12249)
+ * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
+ * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
+ * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
+ * Fix upgrade of super columns on thrift (CASSANDRA-12335)
+ * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
+ * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
+ * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
+ * Lost counter writes in compact table and static columns (CASSANDRA-12219)
+ * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
+ * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
+ * Add option to override compaction space check (CASSANDRA-12180)
+ * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
+ * Respond with v1/v2 protocol header when responding to driver that attempts
+ to connect with too low of a protocol version (CASSANDRA-11464)
+ * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
+ * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
+ * Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
+ * Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
+ * Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)
+ * Fix upgrading sparse tables that are incorrectly marked as dense (CASSANDRA-11315)
+ * Fix reverse queries ignoring range tombstones (CASSANDRA-11733)
+ * Avoid potential race when rebuilding CFMetaData (CASSANDRA-12098)
+ * Avoid missing sstables when getting the canonical sstables (CASSANDRA-11996)
+ * Always select the live sstables when getting sstables in bounds (CASSANDRA-11944)
+ * Fix column ordering of results with static columns for Thrift requests in
+ a mixed 2.x/3.x cluster, also fix potential non-resolved duplication of
+ those static columns in query results (CASSANDRA-12123)
+ * Avoid digest mismatch with empty but static rows (CASSANDRA-12090)
+ * Fix EOF exception when altering column type (CASSANDRA-11820)
+Merged from 2.2:
+ * Only set broadcast_rpc_address on Ec2MultiRegionSnitch if it's not set (CASSANDRA-11357)
* Update StorageProxy range metrics for timeouts, failures and unavailables (CASSANDRA-9507)
* Add Sigar to classes included in clientutil.jar (CASSANDRA-11635)
* Add decay to histograms and timers used for metrics (CASSANDRA-11752)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5fdb90e1/NEWS.txt
----------------------------------------------------------------------
diff --cc NEWS.txt
index 56dd6ea,f0712eb..6b8dfd3
--- a/NEWS.txt
+++ b/NEWS.txt
@@@ -13,55 -13,7 +13,55 @@@ restore snapshots created with the prev
'sstableloader' tool. You can upgrade the file format of your snapshots
using the provided 'sstableupgrade' tool.
-2.2.7
+3.0.8
+=====
+
+Upgrading
+---------
- - Nothing specific to this release, but please see previous versions upgrading section,
- especially if you are upgrading from 2.2.
++ - Ec2MultiRegionSnitch will no longer automatically set broadcast_rpc_address
++ to the public instance IP if this property is defined on cassandra.yaml.
+
+3.0.7
+=====
+
+Upgrading
+---------
+ - A maximum size for SSTables values has been introduced, to prevent out of memory
+ exceptions when reading corrupt SSTables. This maximum size can be set via
+ max_value_size_in_mb in cassandra.yaml. The default is 256MB, which matches the default
+ value of native_transport_max_frame_size_in_mb. SSTables will be considered corrupt if
+ they contain values whose size exceeds this limit. See CASSANDRA-9530 for more details.
+
+Deprecation
+-----------
+ - DateTieredCompactionStrategy has been deprecated - new tables should use
+ TimeWindowCompactionStrategy. Note that migrating an existing DTCS-table to TWCS might
+ cause increased compaction load for a while after the migration so make sure you run
+ tests before migrating. Read CASSANDRA-9666 for background on this.
+
+New features
+------------
+ - TimeWindowCompactionStrategy has been added. This has proven to be a better approach
+ to time series compaction and new tables should use this instead of DTCS. See
+ CASSANDRA-9666 for details.
+
+3.0.6
+=====
+
+New features
+------------
+ - JSON timestamps are now in UTC and contain the timezone information, see
+ CASSANDRA-11137 for more details.
+
+3.0.5
+=====
+
+Upgrading
+---------
+ - Nothing specific to this release, but please see previous versions upgrading section,
+ especially if you are upgrading from 2.2.
+
+3.0.4
=====
New features
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5fdb90e1/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5fdb90e1/src/java/org/apache/cassandra/service/StorageService.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5fdb90e1/src/java/org/apache/cassandra/transport/Server.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5fdb90e1/src/java/org/apache/cassandra/utils/FBUtilities.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/utils/FBUtilities.java
index d996c91,23a2c2e..ca2775f
--- a/src/java/org/apache/cassandra/utils/FBUtilities.java
+++ b/src/java/org/apache/cassandra/utils/FBUtilities.java
@@@ -32,7 -34,9 +32,8 @@@ import java.util.zip.Checksum
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
-import com.google.common.collect.AbstractIterator;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5fdb90e1/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
index 90c5f05,5b86252..acd68eb
--- a/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
+++ b/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
@@@ -18,7 -18,10 +18,8 @@@
package org.apache.cassandra.utils;
-import static org.junit.Assert.fail;
-
import java.io.IOException;
+ import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.StandardCharsets;
@@@ -29,8 -31,10 +30,11 @@@ import org.junit.Test
import java.util.Map;
import java.util.TreeMap;
+ import org.apache.cassandra.config.Config;
+ import org.apache.cassandra.config.DatabaseDescriptor;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
public class FBUtilitiesTest
{
[16/50] [abbrv] cassandra git commit: Fix potential resource leak in
RMIServerSocketFactoryImpl
Posted by ms...@apache.org.
Fix potential resource leak in RMIServerSocketFactoryImpl
patch by Arunkumar M; reviewed by yukim for CASSANDRA-12331
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/19245e5f
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/19245e5f
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/19245e5f
Branch: refs/heads/cassandra-3.8
Commit: 19245e5faccdd4c1b926e94c38b7a7faad005453
Parents: de86ab1
Author: arunkumar-m <ar...@gmail.com>
Authored: Thu Aug 4 08:48:49 2016 -0700
Committer: Yuki Morishita <yu...@apache.org>
Committed: Thu Aug 11 11:01:32 2016 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../cassandra/utils/RMIServerSocketFactoryImpl.java | 13 +++++++++++--
2 files changed, 12 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/19245e5f/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 3db2c77..0a1d406 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
3.0.9
+ * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
* Backport CASSANDRA-12002 (CASSANDRA-12177)
* Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
* Fix potential bad messaging service message for paged range reads
http://git-wip-us.apache.org/repos/asf/cassandra/blob/19245e5f/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java b/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
index 6d4ff72..6444a65 100644
--- a/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
+++ b/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
@@ -23,6 +23,7 @@ package org.apache.cassandra.utils;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
+import java.net.SocketException;
import java.rmi.server.RMIServerSocketFactory;
import javax.net.ServerSocketFactory;
@@ -34,8 +35,16 @@ public class RMIServerSocketFactoryImpl implements RMIServerSocketFactory
{
ServerSocket socket = ServerSocketFactory.getDefault()
.createServerSocket(pPort, 0, InetAddress.getLoopbackAddress());
- socket.setReuseAddress(true);
- return socket;
+ try
+ {
+ socket.setReuseAddress(true);
+ return socket;
+ }
+ catch (SocketException e)
+ {
+ socket.close();
+ throw e;
+ }
}
public boolean equals(Object obj)
[22/50] [abbrv] cassandra git commit: Merge branch cassandra-2.2 into
cassandra-3.0
Posted by ms...@apache.org.
Merge branch cassandra-2.2 into cassandra-3.0
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/c85749e2
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/c85749e2
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/c85749e2
Branch: refs/heads/cassandra-3.8
Commit: c85749e2bc5c65a03b00994565cd0b6b1a642e65
Parents: 2a828af e86d531
Author: Benjamin Lerer <b....@gmail.com>
Authored: Fri Aug 12 15:34:01 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Aug 12 15:37:13 2016 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../apache/cassandra/service/StorageProxy.java | 49 +++++++++++++-------
2 files changed, 34 insertions(+), 16 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/c85749e2/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 3ce9f9e,ddc6720..959b967
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,39 -1,5 +1,40 @@@
-2.2.8
+3.0.9
+ * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
+ * Backport CASSANDRA-12002 (CASSANDRA-12177)
+ * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
+ * Fix potential bad messaging service message for paged range reads
+ within mixed-version 3.x clusters (CASSANDRA-12249)
+ * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
+ * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
+ * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
+ * Fix upgrade of super columns on thrift (CASSANDRA-12335)
+ * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
+ * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
+ * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
+ * Lost counter writes in compact table and static columns (CASSANDRA-12219)
+ * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
+ * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
+ * Add option to override compaction space check (CASSANDRA-12180)
+ * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
+ * Respond with v1/v2 protocol header when responding to driver that attempts
+ to connect with too low of a protocol version (CASSANDRA-11464)
+ * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
+ * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
+ * Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
+ * Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
+ * Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)
+ * Fix upgrading sparse tables that are incorrectly marked as dense (CASSANDRA-11315)
+ * Fix reverse queries ignoring range tombstones (CASSANDRA-11733)
+ * Avoid potential race when rebuilding CFMetaData (CASSANDRA-12098)
+ * Avoid missing sstables when getting the canonical sstables (CASSANDRA-11996)
+ * Always select the live sstables when getting sstables in bounds (CASSANDRA-11944)
+ * Fix column ordering of results with static columns for Thrift requests in
+ a mixed 2.x/3.x cluster, also fix potential non-resolved duplication of
+ those static columns in query results (CASSANDRA-12123)
+ * Avoid digest mismatch with empty but static rows (CASSANDRA-12090)
+ * Fix EOF exception when altering column type (CASSANDRA-11820)
+Merged from 2.2:
+ * Update StorageProxy range metrics for timeouts, failures and unavailables (CASSANDRA-9507)
* Add Sigar to classes included in clientutil.jar (CASSANDRA-11635)
* Add decay to histograms and timers used for metrics (CASSANDRA-11752)
* Fix hanging stream session (CASSANDRA-10992)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/c85749e2/src/java/org/apache/cassandra/service/StorageProxy.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/service/StorageProxy.java
index 483da67,7b7979d..8a151f2
--- a/src/java/org/apache/cassandra/service/StorageProxy.java
+++ b/src/java/org/apache/cassandra/service/StorageProxy.java
@@@ -1955,201 -1716,267 +1955,218 @@@ public class StorageProxy implements St
}
}
- public static List<Row> getRangeSlice(AbstractRangeCommand command, ConsistencyLevel consistency_level)
- throws UnavailableException, ReadFailureException, ReadTimeoutException
+ private static class SingleRangeResponse extends AbstractIterator<RowIterator> implements PartitionIterator
{
- Tracing.trace("Computing ranges to query");
- long startTime = System.nanoTime();
+ private final ReadCallback handler;
+ private PartitionIterator result;
- Keyspace keyspace = Keyspace.open(command.keyspace);
- List<Row> rows;
- // now scan until we have enough results
- try
+ private SingleRangeResponse(ReadCallback handler)
{
- int liveRowCount = 0;
- boolean countLiveRows = command.countCQL3Rows() || command.ignoredTombstonedPartitions();
- rows = new ArrayList<>();
+ this.handler = handler;
+ }
- // when dealing with LocalStrategy keyspaces, we can skip the range splitting and merging (which can be
- // expensive in clusters with vnodes)
- List<? extends AbstractBounds<RowPosition>> ranges;
- if (keyspace.getReplicationStrategy() instanceof LocalStrategy)
- ranges = command.keyRange.unwrap();
- else
- ranges = getRestrictedRanges(command.keyRange);
-
- // determine the number of rows to be fetched and the concurrency factor
- int rowsToBeFetched = command.limit();
- int concurrencyFactor;
- if (command.requiresScanningAllRanges())
- {
- // all nodes must be queried
- rowsToBeFetched *= ranges.size();
- concurrencyFactor = ranges.size();
- logger.debug("Requested rows: {}, ranges.size(): {}; concurrent range requests: {}",
- command.limit(),
- ranges.size(),
- concurrencyFactor);
- Tracing.trace("Submitting range requests on {} ranges with a concurrency of {}",
- ranges.size(), concurrencyFactor);
+ private void waitForResponse() throws ReadTimeoutException
+ {
+ if (result != null)
+ return;
+
+ try
+ {
+ result = handler.get();
}
- else
+ catch (DigestMismatchException e)
{
- // our estimate of how many result rows there will be per-range
- float resultRowsPerRange = estimateResultRowsPerRange(command, keyspace);
- // underestimate how many rows we will get per-range in order to increase the likelihood that we'll
- // fetch enough rows in the first round
- resultRowsPerRange -= resultRowsPerRange * CONCURRENT_SUBREQUESTS_MARGIN;
- concurrencyFactor = resultRowsPerRange == 0.0
- ? 1
- : Math.max(1, Math.min(ranges.size(), (int) Math.ceil(command.limit() / resultRowsPerRange)));
-
- logger.trace("Estimated result rows per range: {}; requested rows: {}, ranges.size(): {}; concurrent range requests: {}",
- resultRowsPerRange,
- command.limit(),
- ranges.size(),
- concurrencyFactor);
- Tracing.trace("Submitting range requests on {} ranges with a concurrency of {} ({} rows per range expected)",
- ranges.size(),
- concurrencyFactor,
- resultRowsPerRange);
- }
-
- boolean haveSufficientRows = false;
- int i = 0;
- AbstractBounds<RowPosition> nextRange = null;
- List<InetAddress> nextEndpoints = null;
- List<InetAddress> nextFilteredEndpoints = null;
- while (i < ranges.size())
- {
- List<Pair<AbstractRangeCommand, ReadCallback<RangeSliceReply, Iterable<Row>>>> scanHandlers = new ArrayList<>(concurrencyFactor);
- int concurrentFetchStartingIndex = i;
- int concurrentRequests = 0;
- while ((i - concurrentFetchStartingIndex) < concurrencyFactor)
- {
- AbstractBounds<RowPosition> range = nextRange == null
- ? ranges.get(i)
- : nextRange;
- List<InetAddress> liveEndpoints = nextEndpoints == null
- ? getLiveSortedEndpoints(keyspace, range.right)
- : nextEndpoints;
- List<InetAddress> filteredEndpoints = nextFilteredEndpoints == null
- ? consistency_level.filterForQuery(keyspace, liveEndpoints)
- : nextFilteredEndpoints;
- ++i;
- ++concurrentRequests;
-
- // getRestrictedRange has broken the queried range into per-[vnode] token ranges, but this doesn't take
- // the replication factor into account. If the intersection of live endpoints for 2 consecutive ranges
- // still meets the CL requirements, then we can merge both ranges into the same RangeSliceCommand.
- while (i < ranges.size())
- {
- nextRange = ranges.get(i);
- nextEndpoints = getLiveSortedEndpoints(keyspace, nextRange.right);
- nextFilteredEndpoints = consistency_level.filterForQuery(keyspace, nextEndpoints);
-
- // If the current range right is the min token, we should stop merging because CFS.getRangeSlice
- // don't know how to deal with a wrapping range.
- // Note: it would be slightly more efficient to have CFS.getRangeSlice on the destination nodes unwraps
- // the range if necessary and deal with it. However, we can't start sending wrapped range without breaking
- // wire compatibility, so It's likely easier not to bother;
- if (range.right.isMinimum())
- break;
-
- List<InetAddress> merged = intersection(liveEndpoints, nextEndpoints);
-
- // Check if there is enough endpoint for the merge to be possible.
- if (!consistency_level.isSufficientLiveNodes(keyspace, merged))
- break;
-
- List<InetAddress> filteredMerged = consistency_level.filterForQuery(keyspace, merged);
-
- // Estimate whether merging will be a win or not
- if (!DatabaseDescriptor.getEndpointSnitch().isWorthMergingForRangeQuery(filteredMerged, filteredEndpoints, nextFilteredEndpoints))
- break;
-
- // If we get there, merge this range and the next one
- range = range.withNewRight(nextRange.right);
- liveEndpoints = merged;
- filteredEndpoints = filteredMerged;
- ++i;
- }
-
- AbstractRangeCommand nodeCmd = command.forSubRange(range);
+ throw new AssertionError(e); // no digests in range slices yet
+ }
+ }
- // collect replies and resolve according to consistency level
- RangeSliceResponseResolver resolver = new RangeSliceResponseResolver(nodeCmd.keyspace, command.timestamp);
- List<InetAddress> minimalEndpoints = filteredEndpoints.subList(0, Math.min(filteredEndpoints.size(), consistency_level.blockFor(keyspace)));
- ReadCallback<RangeSliceReply, Iterable<Row>> handler = new ReadCallback<>(resolver, consistency_level, nodeCmd, minimalEndpoints);
- handler.assureSufficientLiveNodes();
- resolver.setSources(filteredEndpoints);
- if (filteredEndpoints.size() == 1
- && filteredEndpoints.get(0).equals(FBUtilities.getBroadcastAddress()))
- {
- StageManager.getStage(Stage.READ).execute(new LocalRangeSliceRunnable(nodeCmd, handler));
- }
- else
- {
- MessageOut<? extends AbstractRangeCommand> message = nodeCmd.createMessage();
- for (InetAddress endpoint : filteredEndpoints)
- {
- Tracing.trace("Enqueuing request to {}", endpoint);
- MessagingService.instance().sendRRWithFailure(message, endpoint, handler);
- }
- }
- scanHandlers.add(Pair.create(nodeCmd, handler));
- }
- Tracing.trace("Submitted {} concurrent range requests covering {} ranges", concurrentRequests, i - concurrentFetchStartingIndex);
+ protected RowIterator computeNext()
+ {
+ waitForResponse();
+ return result.hasNext() ? result.next() : endOfData();
+ }
- List<AsyncOneResponse> repairResponses = new ArrayList<>();
- for (Pair<AbstractRangeCommand, ReadCallback<RangeSliceReply, Iterable<Row>>> cmdPairHandler : scanHandlers)
- {
- ReadCallback<RangeSliceReply, Iterable<Row>> handler = cmdPairHandler.right;
- RangeSliceResponseResolver resolver = (RangeSliceResponseResolver)handler.resolver;
+ public void close()
+ {
+ if (result != null)
+ result.close();
+ }
+ }
- try
- {
- for (Row row : handler.get())
- {
- rows.add(row);
- if (countLiveRows)
- liveRowCount += row.getLiveCount(command.predicate, command.timestamp);
- }
- repairResponses.addAll(resolver.repairResults);
- }
- catch (ReadTimeoutException|ReadFailureException ex)
- {
- // we timed out or failed waiting for responses
- int blockFor = consistency_level.blockFor(keyspace);
- int responseCount = resolver.responses.size();
- String gotData = responseCount > 0
- ? resolver.isDataPresent() ? " (including data)" : " (only digests)"
- : "";
-
- boolean isTimeout = ex instanceof ReadTimeoutException;
- if (Tracing.isTracing())
- {
- Tracing.trace("{}; received {} of {} responses{} for range {} of {}",
- (isTimeout ? "Timed out" : "Failed"), responseCount, blockFor, gotData, i, ranges.size());
- }
- else if (logger.isDebugEnabled())
- {
- logger.debug("Range slice {}; received {} of {} responses{} for range {} of {}",
- (isTimeout ? "timeout" : "failure"), responseCount, blockFor, gotData, i, ranges.size());
- }
- throw ex;
- }
- catch (DigestMismatchException e)
- {
- throw new AssertionError(e); // no digests in range slices yet
- }
+ private static class RangeCommandIterator extends AbstractIterator<RowIterator> implements PartitionIterator
+ {
+ private final Iterator<RangeForQuery> ranges;
+ private final int totalRangeCount;
+ private final PartitionRangeReadCommand command;
+ private final Keyspace keyspace;
+ private final ConsistencyLevel consistency;
- // if we're done, great, otherwise, move to the next range
- int count = countLiveRows ? liveRowCount : rows.size();
- if (count >= rowsToBeFetched)
- {
- haveSufficientRows = true;
- break;
- }
- }
+ private final long startTime;
+ private DataLimits.Counter counter;
+ private PartitionIterator sentQueryIterator;
- try
- {
- FBUtilities.waitOnFutures(repairResponses, DatabaseDescriptor.getWriteRpcTimeout());
- }
- catch (TimeoutException ex)
- {
- // We got all responses, but timed out while repairing
- int blockFor = consistency_level.blockFor(keyspace);
- if (Tracing.isTracing())
- Tracing.trace("Timed out while read-repairing after receiving all {} data and digest responses", blockFor);
- else
- logger.debug("Range slice timeout while read-repairing after receiving all {} data and digest responses", blockFor);
- throw new ReadTimeoutException(consistency_level, blockFor-1, blockFor, true);
- }
+ private int concurrencyFactor;
+ // The two following "metric" are maintained to improve the concurrencyFactor
+ // when it was not good enough initially.
+ private int liveReturned;
+ private int rangesQueried;
- if (haveSufficientRows)
- return command.postReconciliationProcessing(rows);
+ public RangeCommandIterator(RangeIterator ranges, PartitionRangeReadCommand command, int concurrencyFactor, Keyspace keyspace, ConsistencyLevel consistency)
+ {
+ this.command = command;
+ this.concurrencyFactor = concurrencyFactor;
+ this.startTime = System.nanoTime();
+ this.ranges = new RangeMerger(ranges, keyspace, consistency);
+ this.totalRangeCount = ranges.rangeCount();
+ this.consistency = consistency;
+ this.keyspace = keyspace;
+ }
- // we didn't get enough rows in our concurrent fetch; recalculate our concurrency factor
- // based on the results we've seen so far (as long as we still have ranges left to query)
- if (i < ranges.size())
+ public RowIterator computeNext()
+ {
- while (sentQueryIterator == null || !sentQueryIterator.hasNext())
++ try
+ {
- // If we don't have more range to handle, we're done
- if (!ranges.hasNext())
- return endOfData();
-
- // else, sends the next batch of concurrent queries (after having close the previous iterator)
- if (sentQueryIterator != null)
++ while (sentQueryIterator == null || !sentQueryIterator.hasNext())
{
- liveReturned += counter.counted();
- sentQueryIterator.close();
- float fetchedRows = countLiveRows ? liveRowCount : rows.size();
- float remainingRows = rowsToBeFetched - fetchedRows;
- float actualRowsPerRange;
- if (fetchedRows == 0.0)
- {
- // we haven't actually gotten any results, so query all remaining ranges at once
- actualRowsPerRange = 0.0f;
- concurrencyFactor = ranges.size() - i;
- }
- else
++ // If we don't have more range to handle, we're done
++ if (!ranges.hasNext())
++ return endOfData();
+
- // It's not the first batch of queries and we're not done, so we we can use what has been
- // returned so far to improve our rows-per-range estimate and update the concurrency accordingly
- updateConcurrencyFactor();
++ // else, sends the next batch of concurrent queries (after having close the previous iterator)
++ if (sentQueryIterator != null)
+ {
- actualRowsPerRange = fetchedRows / i;
- concurrencyFactor = Math.max(1, Math.min(ranges.size() - i, Math.round(remainingRows / actualRowsPerRange)));
++ liveReturned += counter.counted();
++ sentQueryIterator.close();
++
++ // It's not the first batch of queries and we're not done, so we we can use what has been
++ // returned so far to improve our rows-per-range estimate and update the concurrency accordingly
++ updateConcurrencyFactor();
+ }
- logger.trace("Didn't get enough response rows; actual rows per range: {}; remaining rows: {}, new concurrent requests: {}",
- actualRowsPerRange, (int) remainingRows, concurrencyFactor);
++ sentQueryIterator = sendNextRequests();
}
- sentQueryIterator = sendNextRequests();
- }
+
- return sentQueryIterator.next();
++ return sentQueryIterator.next();
++ }
++ catch (UnavailableException e)
++ {
++ rangeMetrics.unavailables.mark();
++ throw e;
++ }
++ catch (ReadTimeoutException e)
++ {
++ rangeMetrics.timeouts.mark();
++ throw e;
++ }
++ catch (ReadFailureException e)
++ {
++ rangeMetrics.failures.mark();
++ throw e;
+ }
}
- catch (ReadTimeoutException e)
+
+ private void updateConcurrencyFactor()
{
- rangeMetrics.timeouts.mark();
- throw e;
+ if (liveReturned == 0)
+ {
+ // we haven't actually gotten any results, so query all remaining ranges at once
+ concurrencyFactor = totalRangeCount - rangesQueried;
+ return;
+ }
+
+ // Otherwise, compute how many rows per range we got on average and pick a concurrency factor
+ // that should allow us to fetch all remaining rows with the next batch of (concurrent) queries.
+ int remainingRows = command.limits().count() - liveReturned;
+ float rowsPerRange = (float)liveReturned / (float)rangesQueried;
+ concurrencyFactor = Math.max(1, Math.min(totalRangeCount - rangesQueried, Math.round(remainingRows / rowsPerRange)));
+ logger.trace("Didn't get enough response rows; actual rows per range: {}; remaining rows: {}, new concurrent requests: {}",
+ rowsPerRange, (int) remainingRows, concurrencyFactor);
}
- catch (UnavailableException e)
+
+ private SingleRangeResponse query(RangeForQuery toQuery)
{
- rangeMetrics.unavailables.mark();
- throw e;
+ PartitionRangeReadCommand rangeCommand = command.forSubRange(toQuery.range);
+
+ DataResolver resolver = new DataResolver(keyspace, rangeCommand, consistency, toQuery.filteredEndpoints.size());
+
+ int blockFor = consistency.blockFor(keyspace);
+ int minResponses = Math.min(toQuery.filteredEndpoints.size(), blockFor);
+ List<InetAddress> minimalEndpoints = toQuery.filteredEndpoints.subList(0, minResponses);
+ ReadCallback handler = new ReadCallback(resolver, consistency, rangeCommand, minimalEndpoints);
+
+ handler.assureSufficientLiveNodes();
+
+ if (toQuery.filteredEndpoints.size() == 1 && canDoLocalRequest(toQuery.filteredEndpoints.get(0)))
+ {
+ StageManager.getStage(Stage.READ).execute(new LocalReadRunnable(rangeCommand, handler));
+ }
+ else
+ {
+ for (InetAddress endpoint : toQuery.filteredEndpoints)
+ {
+ MessageOut<ReadCommand> message = rangeCommand.createMessage(MessagingService.instance().getVersion(endpoint));
+ Tracing.trace("Enqueuing request to {}", endpoint);
+ MessagingService.instance().sendRRWithFailure(message, endpoint, handler);
+ }
+ }
+
+ return new SingleRangeResponse(handler);
}
- catch (ReadFailureException e)
+
+ private PartitionIterator sendNextRequests()
{
- rangeMetrics.failures.mark();
- throw e;
+ List<PartitionIterator> concurrentQueries = new ArrayList<>(concurrencyFactor);
+ for (int i = 0; i < concurrencyFactor && ranges.hasNext(); i++)
+ {
+ concurrentQueries.add(query(ranges.next()));
+ ++rangesQueried;
+ }
+
+ Tracing.trace("Submitted {} concurrent range requests", concurrentQueries.size());
+ // We want to count the results for the sake of updating the concurrency factor (see updateConcurrencyFactor) but we don't want to
+ // enforce any particular limit at this point (this could break code than rely on postReconciliationProcessing), hence the DataLimits.NONE.
+ counter = DataLimits.NONE.newCounter(command.nowInSec(), true);
+ return counter.applyTo(PartitionIterators.concat(concurrentQueries));
}
- finally
+
+ public void close()
{
- long latency = System.nanoTime() - startTime;
- rangeMetrics.addNano(latency);
- Keyspace.open(command.keyspace).getColumnFamilyStore(command.columnFamily).metric.coordinatorScanLatency.update(latency, TimeUnit.NANOSECONDS);
+ try
+ {
+ if (sentQueryIterator != null)
+ sentQueryIterator.close();
+ }
+ finally
+ {
+ long latency = System.nanoTime() - startTime;
+ rangeMetrics.addNano(latency);
+ Keyspace.openAndGetStore(command.metadata()).metric.coordinatorScanLatency.update(latency, TimeUnit.NANOSECONDS);
+ }
}
- return command.postReconciliationProcessing(rows);
+ }
+
+ @SuppressWarnings("resource")
+ public static PartitionIterator getRangeSlice(PartitionRangeReadCommand command, ConsistencyLevel consistencyLevel)
- throws UnavailableException, ReadFailureException, ReadTimeoutException
+ {
+ Tracing.trace("Computing ranges to query");
+
+ Keyspace keyspace = Keyspace.open(command.metadata().ksName);
+ RangeIterator ranges = new RangeIterator(command, keyspace, consistencyLevel);
+
+ // our estimate of how many result rows there will be per-range
+ float resultsPerRange = estimateResultsPerRange(command, keyspace);
+ // underestimate how many rows we will get per-range in order to increase the likelihood that we'll
+ // fetch enough rows in the first round
+ resultsPerRange -= resultsPerRange * CONCURRENT_SUBREQUESTS_MARGIN;
+ int concurrencyFactor = resultsPerRange == 0.0
+ ? 1
+ : Math.max(1, Math.min(ranges.rangeCount(), (int) Math.ceil(command.limits().count() / resultsPerRange)));
+ logger.trace("Estimated result rows per range: {}; requested rows: {}, ranges.size(): {}; concurrent range requests: {}",
+ resultsPerRange, command.limits().count(), ranges.rangeCount(), concurrencyFactor);
+ Tracing.trace("Submitting range requests on {} ranges with a concurrency of {} ({} rows per range expected)", ranges.rangeCount(), concurrencyFactor, resultsPerRange);
+
+ // Note that in general, a RangeCommandIterator will honor the command limit for each range, but will not enforce it globally.
+
+ return command.limits().filter(command.postReconciliationProcessing(new RangeCommandIterator(ranges, command, concurrencyFactor, keyspace, consistencyLevel)), command.nowInSec());
}
public Map<String, List<String>> getSchemaVersions()
[26/50] [abbrv] cassandra git commit: Fix clean interval not sent to
commit log for empty memtable flush
Posted by ms...@apache.org.
Fix clean interval not sent to commit log for empty memtable flush
patch by Branimir Lambov; reviewed by Jake Luciani for
CASSANDRA-12436
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/ca556c44
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/ca556c44
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/ca556c44
Branch: refs/heads/cassandra-3.8
Commit: ca556c442049808c013bd20649d6611eb4a01a61
Parents: e60f741
Author: Branimir Lambov <br...@datastax.com>
Authored: Thu Aug 11 11:17:37 2016 +0300
Committer: T Jake Luciani <ja...@apache.org>
Committed: Fri Aug 12 11:29:23 2016 -0400
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../apache/cassandra/db/ColumnFamilyStore.java | 22 +++++------
.../cassandra/db/commitlog/CommitLog.java | 5 ++-
.../db/commitlog/CommitLogSegment.java | 5 ++-
.../db/commitlog/CommitLogCQLTest.java | 41 ++++++++++++++++++++
5 files changed, 58 insertions(+), 16 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca556c44/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index b1b2050..56875ca 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -6,6 +6,7 @@
* cqlsh: Fix handling of $$-escaped strings (CASSANDRA-12189)
* Fix SSL JMX requiring truststore containing server cert (CASSANDRA-12109)
Merged from 3.0:
+ * Fix clean interval not sent to commit log for empty memtable flush (CASSANDRA-12436)
* Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
* Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
* Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca556c44/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
index 21becfe..aa79e90 100644
--- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
@@ -1072,20 +1072,9 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
writeBarrier.markBlocking();
writeBarrier.await();
- // mark all memtables as flushing, removing them from the live memtable list, and
- // remove any memtables that are already clean from the set we need to flush
- Iterator<Memtable> iter = memtables.iterator();
- while (iter.hasNext())
- {
- Memtable memtable = iter.next();
+ // mark all memtables as flushing, removing them from the live memtable list
+ for (Memtable memtable : memtables)
memtable.cfs.data.markFlushing(memtable);
- if (memtable.isClean() || truncate)
- {
- memtable.cfs.replaceFlushed(memtable, Collections.emptyList());
- reclaim(memtable);
- iter.remove();
- }
- }
metric.memtableSwitchCount.inc();
@@ -1105,6 +1094,13 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
public Collection<SSTableReader> flushMemtable(Memtable memtable)
{
+ if (memtable.isClean() || truncate)
+ {
+ memtable.cfs.replaceFlushed(memtable, Collections.emptyList());
+ reclaim(memtable);
+ return Collections.emptyList();
+ }
+
List<Future<SSTableMultiWriter>> futures = new ArrayList<>();
long totalBytesOnDisk = 0;
long maxBytesOnDisk = 0;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca556c44/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/commitlog/CommitLog.java b/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
index 32f69eb..ce92a0a 100644
--- a/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
+++ b/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
@@ -321,8 +321,9 @@ public class CommitLog implements CommitLogMBean
}
else
{
- logger.trace("Not safe to delete{} commit log segment {}; dirty is {}",
- (iter.hasNext() ? "" : " active"), segment, segment.dirtyString());
+ if (logger.isTraceEnabled())
+ logger.trace("Not safe to delete{} commit log segment {}; dirty is {}",
+ (iter.hasNext() ? "" : " active"), segment, segment.dirtyString());
}
// Don't mark or try to delete any newer segments once we've reached the one containing the
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca556c44/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java b/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
index e32c204..94f0ac8 100644
--- a/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
+++ b/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
@@ -564,7 +564,10 @@ public abstract class CommitLogSegment
for (UUID cfId : getDirtyCFIDs())
{
CFMetaData m = Schema.instance.getCFMetaData(cfId);
- sb.append(m == null ? "<deleted>" : m.cfName).append(" (").append(cfId).append("), ");
+ sb.append(m == null ? "<deleted>" : m.cfName).append(" (").append(cfId)
+ .append(", dirty: ").append(cfDirty.get(cfId))
+ .append(", clean: ").append(cfClean.get(cfId))
+ .append("), ");
}
return sb.toString();
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca556c44/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java b/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java
new file mode 100644
index 0000000..7235600
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java
@@ -0,0 +1,41 @@
+package org.apache.cassandra.db.commitlog;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.junit.Test;
+
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.db.ColumnFamilyStore;
+
+public class CommitLogCQLTest extends CQLTester
+{
+ @Test
+ public void testTruncateSegmentDiscard() throws Throwable
+ {
+ String otherTable = createTable("CREATE TABLE %s (idx INT, data TEXT, PRIMARY KEY(idx));");
+
+ createTable("CREATE TABLE %s (idx INT, data TEXT, PRIMARY KEY(idx));");
+ execute("INSERT INTO %s (idx, data) VALUES (?, ?)", 15, Integer.toString(15));
+ flush();
+
+ // We write something in different table to advance the commit log position. Current table remains clean.
+ executeFormattedQuery(String.format("INSERT INTO %s.%s (idx, data) VALUES (?, ?)", keyspace(), otherTable), 16, Integer.toString(16));
+
+ ColumnFamilyStore cfs = getCurrentColumnFamilyStore();
+ assert cfs.getTracker().getView().getCurrentMemtable().isClean();
+ // Calling switchMemtable directly applies Flush even though memtable is empty. This can happen with some races
+ // (flush with recycling by segment manager). It should still tell commitlog that the memtable's region is clean.
+ // CASSANDRA-12436
+ cfs.switchMemtable();
+
+ execute("INSERT INTO %s (idx, data) VALUES (?, ?)", 15, Integer.toString(17));
+
+ Collection<CommitLogSegment> active = new ArrayList<>(CommitLog.instance.segmentManager.getActiveSegments());
+ CommitLog.instance.forceRecycleAllSegments();
+
+ // If one of the previous segments remains, it wasn't clean.
+ active.retainAll(CommitLog.instance.segmentManager.getActiveSegments());
+ assert active.isEmpty();
+ }
+}
[31/50] [abbrv] cassandra git commit: Wait 10s instead of 1s for
connections to be closed on StreamingTransferTest
Posted by ms...@apache.org.
Wait 10s instead of 1s for connections to be closed on StreamingTransferTest
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/315356c1
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/315356c1
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/315356c1
Branch: refs/heads/cassandra-3.8
Commit: 315356c1de7e4d7db5e0f159daebe9a528a8e04d
Parents: 91f7387
Author: Paulo Motta <pa...@gmail.com>
Authored: Fri Aug 12 10:57:29 2016 -0300
Committer: Jason Brown <ja...@gmail.com>
Committed: Sun Aug 14 08:42:58 2016 -0700
----------------------------------------------------------------------
.../unit/org/apache/cassandra/streaming/StreamingTransferTest.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/315356c1/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java b/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
index 2b16267..0af76c2 100644
--- a/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
+++ b/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
@@ -255,7 +255,7 @@ public class StreamingTransferTest
{
//after stream session is finished, message handlers may take several milliseconds to be closed
outer:
- for (int i = 0; i <= 10; i++)
+ for (int i = 0; i <= 100; i++)
{
for (MessagingService.SocketThread socketThread : MessagingService.instance().getSocketThreads())
if (!socketThread.connections.isEmpty())
[38/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0'
into cassandra-3.9
Posted by ms...@apache.org.
Merge branch 'cassandra-3.0' into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/de2c449b
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/de2c449b
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/de2c449b
Branch: refs/heads/cassandra-3.8
Commit: de2c449b716e30ef28c9243b48dc1fb47388026c
Parents: e72dc29 6528fbf
Author: Aleksey Yeschenko <al...@apache.org>
Authored: Mon Aug 15 18:32:36 2016 +0100
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Mon Aug 15 18:32:36 2016 +0100
----------------------------------------------------------------------
----------------------------------------------------------------------
[34/50] [abbrv] cassandra git commit: Throw RuntimeException if
starting transfer of already completed OutgoingFileMessage
Posted by ms...@apache.org.
Throw RuntimeException if starting transfer of already completed OutgoingFileMessage
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/88b3cfc3
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/88b3cfc3
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/88b3cfc3
Branch: refs/heads/cassandra-3.8
Commit: 88b3cfc3b6b4417a913a512b461897378b48c039
Parents: 315356c
Author: Paulo Motta <pa...@gmail.com>
Authored: Fri Aug 12 22:06:27 2016 -0300
Committer: Yuki Morishita <yu...@apache.org>
Committed: Mon Aug 15 10:09:36 2016 -0500
----------------------------------------------------------------------
.../apache/cassandra/streaming/messages/OutgoingFileMessage.java | 3 +++
1 file changed, 3 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/88b3cfc3/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java b/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java
index a88386e..30b79f8 100644
--- a/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java
+++ b/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java
@@ -115,6 +115,9 @@ public class OutgoingFileMessage extends StreamMessage
@VisibleForTesting
public synchronized void startTransfer()
{
+ if (completed)
+ throw new RuntimeException(String.format("Transfer of file %s already completed or aborted (perhaps session failed?).",
+ filename));
transferring = true;
}
[21/50] [abbrv] cassandra git commit: Update StorageProxy range
metrics for timeouts, failures and unavailables
Posted by ms...@apache.org.
Update StorageProxy range metrics for timeouts, failures and unavailables
patch by Nachiket Patil; reviewed by Alex Petrov for CASSANDRA-9507
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/e86d531e
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/e86d531e
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/e86d531e
Branch: refs/heads/cassandra-3.8
Commit: e86d531ec2bf831a68d0bb448e5ed6ad623bbcf7
Parents: 5b38d3a
Author: Nachiket Patil <na...@apple.com>
Authored: Fri Aug 12 15:30:03 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Aug 12 15:30:03 2016 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../org/apache/cassandra/service/StorageProxy.java | 15 +++++++++++++++
2 files changed, 16 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/e86d531e/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 14bed81..ddc6720 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.2.8
+ * Update StorageProxy range metrics for timeouts, failures and unavailables (CASSANDRA-9507)
* Add Sigar to classes included in clientutil.jar (CASSANDRA-11635)
* Add decay to histograms and timers used for metrics (CASSANDRA-11752)
* Fix hanging stream session (CASSANDRA-10992)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/e86d531e/src/java/org/apache/cassandra/service/StorageProxy.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/StorageProxy.java b/src/java/org/apache/cassandra/service/StorageProxy.java
index 03dd209..7b7979d 100644
--- a/src/java/org/apache/cassandra/service/StorageProxy.java
+++ b/src/java/org/apache/cassandra/service/StorageProxy.java
@@ -1955,6 +1955,21 @@ public class StorageProxy implements StorageProxyMBean
}
}
}
+ catch (ReadTimeoutException e)
+ {
+ rangeMetrics.timeouts.mark();
+ throw e;
+ }
+ catch (UnavailableException e)
+ {
+ rangeMetrics.unavailables.mark();
+ throw e;
+ }
+ catch (ReadFailureException e)
+ {
+ rangeMetrics.failures.mark();
+ throw e;
+ }
finally
{
long latency = System.nanoTime() - startTime;