This is an automated email from the ASF dual-hosted git repository. mck pushed a commit to branch cassandra-3.11 in repository https://gitbox.apache.org/repos/asf/cassandra.git
commit 3e24c0349a1f091c20602b899d72a447a17d57b9 Merge: 2473ee3 f8500ee Author: Mick Semb Wever <[email protected]> AuthorDate: Thu Jan 7 23:02:49 2021 +0100 Merge branch 'cassandra-3.0' into cassandra-3.11 CHANGES.txt | 1 + doc/source/cql/appendices.rst | 9 +- .../cassandra/db/SinglePartitionReadCommand.java | 37 +++- .../upgrade/CompactStorage2to3UpgradeTest.java | 6 +- .../miscellaneous/SSTablesIteratedTest.java | 236 ++++++++++++++++++++- .../cql3/validation/operations/UpdateTest.java | 14 +- 6 files changed, 278 insertions(+), 25 deletions(-) diff --cc CHANGES.txt index c9602f1,54456c1..35129a2 --- a/CHANGES.txt +++ b/CHANGES.txt @@@ -1,8 -1,6 +1,9 @@@ -3.0.24: - * Fix skipping on pre-3.0 created compact storage sstables due to missing primary key liveness (CASSANDRA-16226) +3.11.10 * Fix DecimalDeserializer#toString OOM (CASSANDRA-14925) + * Rate limit validation compactions using compaction_throughput_mb_per_sec (CASSANDRA-16161) + * SASI's `max_compaction_flush_memory_in_mb` settings over 100GB revert to default of 1GB (CASSANDRA-16071) +Merged from 3.0: ++ * Fix skipping on pre-3.0 created compact storage sstables due to missing primary key liveness (CASSANDRA-16226) * Extend the exclusion of replica filtering protection to other indices instead of just SASI (CASSANDRA-16311) * Synchronize transaction logs for JBOD (CASSANDRA-16225) * Fix the counting of cells per partition (CASSANDRA-16259) diff --cc doc/source/cql/appendices.rst index 480b78e,0000000..456170d mode 100644,000000..100644 --- a/doc/source/cql/appendices.rst +++ b/doc/source/cql/appendices.rst @@@ -1,330 -1,0 +1,333 @@@ +.. 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. + +.. highlight:: cql + +Appendices +---------- + +.. _appendix-A: + +Appendix A: CQL Keywords +~~~~~~~~~~~~~~~~~~~~~~~~ + +CQL distinguishes between *reserved* and *non-reserved* keywords. +Reserved keywords cannot be used as identifier, they are truly reserved +for the language (but one can enclose a reserved keyword by +double-quotes to use it as an identifier). Non-reserved keywords however +only have a specific meaning in certain context but can used as +identifier otherwise. The only *raison d’être* of these non-reserved +keywords is convenience: some keyword are non-reserved when it was +always easy for the parser to decide whether they were used as keywords +or not. + ++--------------------+-------------+ +| Keyword | Reserved? | ++====================+=============+ +| ``ADD`` | yes | ++--------------------+-------------+ +| ``AGGREGATE`` | no | ++--------------------+-------------+ +| ``ALL`` | no | ++--------------------+-------------+ +| ``ALLOW`` | yes | ++--------------------+-------------+ +| ``ALTER`` | yes | ++--------------------+-------------+ +| ``AND`` | yes | ++--------------------+-------------+ +| ``APPLY`` | yes | ++--------------------+-------------+ +| ``AS`` | no | ++--------------------+-------------+ +| ``ASC`` | yes | ++--------------------+-------------+ +| ``ASCII`` | no | ++--------------------+-------------+ +| ``AUTHORIZE`` | yes | ++--------------------+-------------+ +| ``BATCH`` | yes | ++--------------------+-------------+ +| ``BEGIN`` | yes | ++--------------------+-------------+ +| ``BIGINT`` | no | ++--------------------+-------------+ +| ``BLOB`` | no | ++--------------------+-------------+ +| ``BOOLEAN`` | no | ++--------------------+-------------+ +| ``BY`` | yes | ++--------------------+-------------+ +| ``CALLED`` | no | ++--------------------+-------------+ +| ``CLUSTERING`` | no | ++--------------------+-------------+ +| ``COLUMNFAMILY`` | yes | ++--------------------+-------------+ +| ``COMPACT`` | no | ++--------------------+-------------+ +| ``CONTAINS`` | no | ++--------------------+-------------+ +| ``COUNT`` | no | ++--------------------+-------------+ +| ``COUNTER`` | no | ++--------------------+-------------+ +| ``CREATE`` | yes | ++--------------------+-------------+ +| ``CUSTOM`` | no | ++--------------------+-------------+ +| ``DATE`` | no | ++--------------------+-------------+ +| ``DECIMAL`` | no | ++--------------------+-------------+ +| ``DELETE`` | yes | ++--------------------+-------------+ +| ``DESC`` | yes | ++--------------------+-------------+ +| ``DESCRIBE`` | yes | ++--------------------+-------------+ +| ``DISTINCT`` | no | ++--------------------+-------------+ +| ``DOUBLE`` | no | ++--------------------+-------------+ +| ``DROP`` | yes | ++--------------------+-------------+ +| ``ENTRIES`` | yes | ++--------------------+-------------+ +| ``EXECUTE`` | yes | ++--------------------+-------------+ +| ``EXISTS`` | no | ++--------------------+-------------+ +| ``FILTERING`` | no | ++--------------------+-------------+ +| ``FINALFUNC`` | no | ++--------------------+-------------+ +| ``FLOAT`` | no | ++--------------------+-------------+ +| ``FROM`` | yes | ++--------------------+-------------+ +| ``FROZEN`` | no | ++--------------------+-------------+ +| ``FULL`` | yes | ++--------------------+-------------+ +| ``FUNCTION`` | no | ++--------------------+-------------+ +| ``FUNCTIONS`` | no | ++--------------------+-------------+ +| ``GRANT`` | yes | ++--------------------+-------------+ +| ``IF`` | yes | ++--------------------+-------------+ +| ``IN`` | yes | ++--------------------+-------------+ +| ``INDEX`` | yes | ++--------------------+-------------+ +| ``INET`` | no | ++--------------------+-------------+ +| ``INFINITY`` | yes | ++--------------------+-------------+ +| ``INITCOND`` | no | ++--------------------+-------------+ +| ``INPUT`` | no | ++--------------------+-------------+ +| ``INSERT`` | yes | ++--------------------+-------------+ +| ``INT`` | no | ++--------------------+-------------+ +| ``INTO`` | yes | ++--------------------+-------------+ +| ``JSON`` | no | ++--------------------+-------------+ +| ``KEY`` | no | ++--------------------+-------------+ +| ``KEYS`` | no | ++--------------------+-------------+ +| ``KEYSPACE`` | yes | ++--------------------+-------------+ +| ``KEYSPACES`` | no | ++--------------------+-------------+ +| ``LANGUAGE`` | no | ++--------------------+-------------+ +| ``LIMIT`` | yes | ++--------------------+-------------+ +| ``LIST`` | no | ++--------------------+-------------+ +| ``LOGIN`` | no | ++--------------------+-------------+ +| ``MAP`` | no | ++--------------------+-------------+ +| ``MODIFY`` | yes | ++--------------------+-------------+ +| ``NAN`` | yes | ++--------------------+-------------+ +| ``NOLOGIN`` | no | ++--------------------+-------------+ +| ``NORECURSIVE`` | yes | ++--------------------+-------------+ +| ``NOSUPERUSER`` | no | ++--------------------+-------------+ +| ``NOT`` | yes | ++--------------------+-------------+ +| ``NULL`` | yes | ++--------------------+-------------+ +| ``OF`` | yes | ++--------------------+-------------+ +| ``ON`` | yes | ++--------------------+-------------+ +| ``OPTIONS`` | no | ++--------------------+-------------+ +| ``OR`` | yes | ++--------------------+-------------+ +| ``ORDER`` | yes | ++--------------------+-------------+ +| ``PASSWORD`` | no | ++--------------------+-------------+ +| ``PERMISSION`` | no | ++--------------------+-------------+ +| ``PERMISSIONS`` | no | ++--------------------+-------------+ +| ``PRIMARY`` | yes | ++--------------------+-------------+ +| ``RENAME`` | yes | ++--------------------+-------------+ +| ``REPLACE`` | yes | ++--------------------+-------------+ +| ``RETURNS`` | no | ++--------------------+-------------+ +| ``REVOKE`` | yes | ++--------------------+-------------+ +| ``ROLE`` | no | ++--------------------+-------------+ +| ``ROLES`` | no | ++--------------------+-------------+ +| ``SCHEMA`` | yes | ++--------------------+-------------+ +| ``SELECT`` | yes | ++--------------------+-------------+ +| ``SET`` | yes | ++--------------------+-------------+ +| ``SFUNC`` | no | ++--------------------+-------------+ +| ``SMALLINT`` | no | ++--------------------+-------------+ +| ``STATIC`` | no | ++--------------------+-------------+ +| ``STORAGE`` | no | ++--------------------+-------------+ +| ``STYPE`` | no | ++--------------------+-------------+ +| ``SUPERUSER`` | no | ++--------------------+-------------+ +| ``TABLE`` | yes | ++--------------------+-------------+ +| ``TEXT`` | no | ++--------------------+-------------+ +| ``TIME`` | no | ++--------------------+-------------+ +| ``TIMESTAMP`` | no | ++--------------------+-------------+ +| ``TIMEUUID`` | no | ++--------------------+-------------+ +| ``TINYINT`` | no | ++--------------------+-------------+ +| ``TO`` | yes | ++--------------------+-------------+ +| ``TOKEN`` | yes | ++--------------------+-------------+ +| ``TRIGGER`` | no | ++--------------------+-------------+ +| ``TRUNCATE`` | yes | ++--------------------+-------------+ +| ``TTL`` | no | ++--------------------+-------------+ +| ``TUPLE`` | no | ++--------------------+-------------+ +| ``TYPE`` | no | ++--------------------+-------------+ +| ``UNLOGGED`` | yes | ++--------------------+-------------+ +| ``UPDATE`` | yes | ++--------------------+-------------+ +| ``USE`` | yes | ++--------------------+-------------+ +| ``USER`` | no | ++--------------------+-------------+ +| ``USERS`` | no | ++--------------------+-------------+ +| ``USING`` | yes | ++--------------------+-------------+ +| ``UUID`` | no | ++--------------------+-------------+ +| ``VALUES`` | no | ++--------------------+-------------+ +| ``VARCHAR`` | no | ++--------------------+-------------+ +| ``VARINT`` | no | ++--------------------+-------------+ +| ``WHERE`` | yes | ++--------------------+-------------+ +| ``WITH`` | yes | ++--------------------+-------------+ +| ``WRITETIME`` | no | ++--------------------+-------------+ + +Appendix B: CQL Reserved Types +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following type names are not currently used by CQL, but are reserved +for potential future use. User-defined types may not use reserved type +names as their name. + ++-----------------+ +| type | ++=================+ +| ``bitstring`` | ++-----------------+ +| ``byte`` | ++-----------------+ +| ``complex`` | ++-----------------+ +| ``enum`` | ++-----------------+ +| ``interval`` | ++-----------------+ +| ``macaddr`` | ++-----------------+ + + +Appendix C: Dropping Compact Storage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + - Starting version 4.0, Thrift and COMPACT STORAGE is no longer supported. - - 'ALTER ... DROP COMPACT STORAGE' statement makes Compact Tables CQL-compatible, ++``ALTER ... DROP COMPACT STORAGE`` statement makes Compact Tables CQL-compatible, +exposing internal structure of Thrift/Compact Tables: + +- CQL-created Compact Tables that have no clustering columns, will expose an + additional clustering column ``column1`` with ``UTF8Type``. +- CQL-created Compact Tables that had no regular columns, will expose a + regular column ``value`` with ``BytesType``. +- For CQL-Created Compact Tables, all columns originally defined as + ``regular`` will be come ``static`` +- CQL-created Compact Tables that have clustering but have no regular + columns will have an empty value column (of ``EmptyType``) +- SuperColumn Tables (can only be created through Thrift) will expose + a compact value map with an empty name. +- Thrift-created Compact Tables will have types corresponding to their + Thrift definition. ++- If a row was written while a table was still compact but it has no live ++ cells due to later row or cell deletions, it may continue to be simply ++ left out of query results, as is the normal behavior for compact tables. ++ Rows written after a table is fully CQL-compatible, if they have no live ++ cells but a live primary key, may be present in query results with null values. diff --cc test/distributed/org/apache/cassandra/distributed/upgrade/CompactStorage2to3UpgradeTest.java index c7dbf99,4f5d1bc..127b85a --- a/test/distributed/org/apache/cassandra/distributed/upgrade/CompactStorage2to3UpgradeTest.java +++ b/test/distributed/org/apache/cassandra/distributed/upgrade/CompactStorage2to3UpgradeTest.java @@@ -195,126 -198,125 +198,125 @@@ public class CompactStorage2to3UpgradeT final int additionalParititons = 5; new TestCase() - .nodes(2) - .upgrade(Versions.Major.v22, Versions.Major.v30) - .setup(cluster -> { - cluster.schemaChange(String.format( - "CREATE TABLE %s.%s (key int, c1 int, c2 int, c3 int, PRIMARY KEY (key, c1, c2)) WITH COMPACT STORAGE", - KEYSPACE, table)); - ICoordinator coordinator = cluster.coordinator(1); - - for (int i = 1; i <= partitions; i++) - { - for (int j = 1; j <= rowsPerPartition; j++) - { - coordinator.execute(String.format("INSERT INTO %s.%s (key, c1, c2, c3) VALUES (%d, %d, 1, 1)", - KEYSPACE, table, i, j), ConsistencyLevel.ALL); - coordinator.execute(String.format("INSERT INTO %s.%s (key, c1, c2, c3) VALUES (%d, %d, 2, 2)", - KEYSPACE, table, i, j), ConsistencyLevel.ALL); - coordinator.execute(String.format("INSERT INTO %s.%s (key, c1, c2, c3) VALUES (%d, %d, 3, 3)", - KEYSPACE, table, i, j), ConsistencyLevel.ALL); - } - } - - }) - .runAfterClusterUpgrade(cluster -> { - for (int i = 1; i <= cluster.size(); i++) - { - NodeToolResult result = cluster.get(1).nodetoolResult("upgradesstables"); - assertEquals("upgrade sstables failed for node " + i, 0, result.getRc()); - } - - // drop compact storage on only one node before performing writes - IMessageFilters.Filter filter = cluster.verbs().allVerbs().to(2).drop(); - cluster.schemaChange(String.format("ALTER TABLE %s.%s DROP COMPACT STORAGE", KEYSPACE, table), 1); - filter.off(); - - // add new partitions and delete some of the old ones - ICoordinator coordinator = cluster.coordinator(1); - for (int i = 0; i < additionalParititons; i++) - { - for (int j = 1; j <= rowsPerPartition; j++) - { - coordinator.execute(String.format("INSERT INTO %s.%s (key, c1, c2, c3) VALUES (%d, %d, 1, 1)", - KEYSPACE, table, i, j), ConsistencyLevel.ALL); - } - } - - coordinator.execute(String.format("DELETE FROM %s.%s WHERE key = %d and c1 = %d", - KEYSPACE, table, 0, 3), ConsistencyLevel.ALL); - - coordinator.execute(String.format("DELETE FROM %s.%s WHERE key = %d", - KEYSPACE, table, 1), ConsistencyLevel.ALL); - - coordinator.execute(String.format("DELETE FROM %s.%s WHERE key = %d and c1 = %d and c2 = %d", - KEYSPACE, table, 7, 2, 2), ConsistencyLevel.ALL); - - coordinator.execute(String.format("DELETE FROM %s.%s WHERE key = %d and c1 = %d and c2 = %d", - KEYSPACE, table, 7, 6, 1), ConsistencyLevel.ALL); - - coordinator.execute(String.format("DELETE FROM %s.%s WHERE key = %d and c1 = %d and c2 = %d", - KEYSPACE, table, 4, 1, 1), ConsistencyLevel.ALL); - - coordinator.execute(String.format("DELETE c3 FROM %s.%s WHERE key = %d and c1 = %d and c2 = %d", - KEYSPACE, table, 8, 1, 3), ConsistencyLevel.ALL); - - coordinator.execute(String.format("DELETE FROM %s.%s WHERE key = %d and c1 = %d and c2 > 1", - KEYSPACE, table, 6, 2, 4), ConsistencyLevel.ALL); - - ResultsRecorder recorder = new ResultsRecorder(); - runQueries(coordinator, recorder, new String[] { - String.format("SELECT * FROM %s.%s", KEYSPACE, table), - - String.format("SELECT * FROM %s.%s WHERE key = %d and c1 = %d", - KEYSPACE, table, partitions - 3, rowsPerPartition - 2), - - String.format("SELECT * FROM %s.%s WHERE key = %d and c1 = %d", - KEYSPACE, table, partitions - 1, rowsPerPartition - 5), - - - String.format("SELECT * FROM %s.%s WHERE key = %d and c1 > %d", - KEYSPACE, table, partitions - 8, rowsPerPartition - 3), - - String.format("SELECT * FROM %s.%s WHERE key = %d", - KEYSPACE, table, 7), - - String.format("SELECT * FROM %s.%s WHERE key = %d and c1 = %d", - KEYSPACE, table, 7, 2), - - String.format("SELECT * FROM %s.%s WHERE key = %d and c1 = %d", - KEYSPACE, table, 8, 1), - - String.format("SELECT c1, c2 FROM %s.%s WHERE key = %d and c1 = %d", - KEYSPACE, table, 8, 1), - - String.format("SELECT c1, c2 FROM %s.%s WHERE key = %d and c1 = %d", - KEYSPACE, table, 8, 1), - - String.format("SELECT c1, c2 FROM %s.%s WHERE key = %d and c1 = %d", - KEYSPACE, table, 4, 1), - - String.format("SELECT c1, c2 FROM %s.%s WHERE key = %d", - KEYSPACE, table, 6), - - String.format("SELECT * FROM %s.%s WHERE key = %d and c1 > %d", - KEYSPACE, table, 0, 1), + .nodes(2) + .upgrade(Versions.Major.v22, Versions.Major.v3X) + .setup(cluster -> { + cluster.schemaChange(String.format( + "CREATE TABLE %s.%s (key int, c1 int, c2 int, c3 int, PRIMARY KEY (key, c1, c2)) WITH COMPACT STORAGE", + KEYSPACE, table)); + ICoordinator coordinator = cluster.coordinator(1); + + for (int i = 1; i <= partitions; i++) + { + for (int j = 1; j <= rowsPerPartition; j++) + { + coordinator.execute(String.format("INSERT INTO %s.%s (key, c1, c2, c3) VALUES (%d, %d, 1, 1)", + KEYSPACE, table, i, j), ConsistencyLevel.ALL); + coordinator.execute(String.format("INSERT INTO %s.%s (key, c1, c2, c3) VALUES (%d, %d, 2, 2)", + KEYSPACE, table, i, j), ConsistencyLevel.ALL); + coordinator.execute(String.format("INSERT INTO %s.%s (key, c1, c2, c3) VALUES (%d, %d, 3, 3)", + KEYSPACE, table, i, j), ConsistencyLevel.ALL); + } + } + + }) + .runAfterClusterUpgrade(cluster -> { + for (int i = 1; i <= cluster.size(); i++) + { + NodeToolResult result = cluster.get(1).nodetoolResult("upgradesstables"); + assertEquals("upgrade sstables failed for node " + i, 0, result.getRc()); + } + + // drop compact storage on only one node before performing writes + IMessageFilters.Filter filter = cluster.verbs().allVerbs().to(2).drop(); + cluster.schemaChange(String.format("ALTER TABLE %s.%s DROP COMPACT STORAGE", KEYSPACE, table), 1); + filter.off(); + + // add new partitions and delete some of the old ones + ICoordinator coordinator = cluster.coordinator(1); + for (int i = 0; i < additionalParititons; i++) + { + for (int j = 1; j <= rowsPerPartition; j++) + { + coordinator.execute(String.format("INSERT INTO %s.%s (key, c1, c2, c3) VALUES (%d, %d, 1, 1)", + KEYSPACE, table, i, j), ConsistencyLevel.ALL); + } + } + + coordinator.execute(String.format("DELETE FROM %s.%s WHERE key = %d and c1 = %d", + KEYSPACE, table, 0, 3), ConsistencyLevel.ALL); + + coordinator.execute(String.format("DELETE FROM %s.%s WHERE key = %d", + KEYSPACE, table, 1), ConsistencyLevel.ALL); + + coordinator.execute(String.format("DELETE FROM %s.%s WHERE key = %d and c1 = %d and c2 = %d", + KEYSPACE, table, 7, 2, 2), ConsistencyLevel.ALL); + + coordinator.execute(String.format("DELETE FROM %s.%s WHERE key = %d and c1 = %d and c2 = %d", + KEYSPACE, table, 7, 6, 1), ConsistencyLevel.ALL); + + coordinator.execute(String.format("DELETE FROM %s.%s WHERE key = %d and c1 = %d and c2 = %d", + KEYSPACE, table, 4, 1, 1), ConsistencyLevel.ALL); + + coordinator.execute(String.format("DELETE c3 FROM %s.%s WHERE key = %d and c1 = %d and c2 = %d", + KEYSPACE, table, 8, 1, 3), ConsistencyLevel.ALL); + + coordinator.execute(String.format("DELETE FROM %s.%s WHERE key = %d and c1 = %d and c2 > 1", + KEYSPACE, table, 6, 2, 4), ConsistencyLevel.ALL); + + ResultsRecorder recorder = new ResultsRecorder(); + runQueries(coordinator, recorder, new String[] { + String.format("SELECT * FROM %s.%s", KEYSPACE, table), + + String.format("SELECT * FROM %s.%s WHERE key = %d and c1 = %d", + KEYSPACE, table, partitions - 3, rowsPerPartition - 2), - String.format("SELECT * FROM %s.%s WHERE key = %d", - KEYSPACE, table, partitions - (additionalParititons - 2)), + String.format("SELECT * FROM %s.%s WHERE key = %d and c1 = %d", + KEYSPACE, table, partitions - 1, rowsPerPartition - 5), - String.format("SELECT * FROM %s.%s WHERE key = %d and c1 > %d", - KEYSPACE, table, partitions - (additionalParititons - 3), 4) - }); + String.format("SELECT * FROM %s.%s WHERE key = %d and c1 > %d", + KEYSPACE, table, partitions - 8, rowsPerPartition - 3), - // drop compact storage on remaining node and check result - cluster.schemaChange(String.format("ALTER TABLE %s.%s DROP COMPACT STORAGE", KEYSPACE, table), 2); - recorder.validateResults(cluster, 1); - recorder.validateResults(cluster, 2); - }).run(); + String.format("SELECT * FROM %s.%s WHERE key = %d", + KEYSPACE, table, 7), + + String.format("SELECT * FROM %s.%s WHERE key = %d and c1 = %d", + KEYSPACE, table, 7, 2), + + String.format("SELECT * FROM %s.%s WHERE key = %d and c1 = %d", + KEYSPACE, table, 8, 1), + + String.format("SELECT c1, c2 FROM %s.%s WHERE key = %d and c1 = %d", + KEYSPACE, table, 8, 1), + + String.format("SELECT c1, c2 FROM %s.%s WHERE key = %d and c1 = %d", + KEYSPACE, table, 8, 1), + + String.format("SELECT c1, c2 FROM %s.%s WHERE key = %d and c1 = %d", + KEYSPACE, table, 4, 1), + + String.format("SELECT c1, c2 FROM %s.%s WHERE key = %d", + KEYSPACE, table, 6), + + String.format("SELECT * FROM %s.%s WHERE key = %d and c1 > %d", + KEYSPACE, table, 0, 1), + + String.format("SELECT * FROM %s.%s WHERE key = %d", + KEYSPACE, table, partitions - (additionalParititons - 2)), + + String.format("SELECT * FROM %s.%s WHERE key = %d and c1 > %d", + KEYSPACE, table, partitions - (additionalParititons - 3), 4) + + }); + + // drop compact storage on remaining node and check result + cluster.schemaChange(String.format("ALTER TABLE %s.%s DROP COMPACT STORAGE", KEYSPACE, table), 2); + recorder.validateResults(cluster, 1); + recorder.validateResults(cluster, 2); + }).run(); } - private void runQueries(ICoordinator coordinator, ResultsRecorder helper, String[] queries) { for (String query : queries) diff --cc test/unit/org/apache/cassandra/cql3/validation/miscellaneous/SSTablesIteratedTest.java index d363ecf,599711e..72d2909 --- a/test/unit/org/apache/cassandra/cql3/validation/miscellaneous/SSTablesIteratedTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/miscellaneous/SSTablesIteratedTest.java @@@ -22,8 -22,6 +22,7 @@@ package org.apache.cassandra.cql3.valid import org.junit.Test; - import static org.junit.Assert.assertEquals; +import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.CQLTester; import org.apache.cassandra.cql3.UntypedResultSet; import org.apache.cassandra.db.ColumnFamilyStore; @@@ -136,422 -136,231 +137,651 @@@ public class SSTablesIteratedTest exten } @Test + public void testSSTablesOnlyASC() throws Throwable + { + createTable("CREATE TABLE %s (id int, col int, val text, PRIMARY KEY (id, col)) WITH CLUSTERING ORDER BY (col ASC)"); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 10, "10"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 20, "20"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 30, "30"); + flush(); + + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 1", 1, row(1, 10, "10")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 2", 2, row(1, 10, "10"), row(1, 20, "20")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 3", 3, row(1, 10, "10"), row(1, 20, "20"), row(1, 30, "30")); + executeAndCheck("SELECT * FROM %s WHERE id=1", 3, row(1, 10, "10"), row(1, 20, "20"), row(1, 30, "30")); + + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col > 25 LIMIT 1", 1, row(1, 30, "30")); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col < 40 LIMIT 1", 1, row(1, 10, "10")); + } + + @Test + public void testMixedMemtableSStablesASC() throws Throwable + { + createTable("CREATE TABLE %s (id int, col int, val text, PRIMARY KEY (id, col)) WITH CLUSTERING ORDER BY (col ASC)"); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 30, "30"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 20, "20"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 10, "10"); + + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 1", 0, row(1, 10, "10")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 2", 1, row(1, 10, "10"), row(1, 20, "20")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 3", 2, row(1, 10, "10"), row(1, 20, "20"), row(1, 30, "30")); + executeAndCheck("SELECT * FROM %s WHERE id=1", 2, row(1, 10, "10"), row(1, 20, "20"), row(1, 30, "30")); + + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col > 25 LIMIT 1", 1, row(1, 30, "30")); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col < 40 LIMIT 1", 0, row(1, 10, "10")); + } + + @Test + public void testOverlappingSStablesASC() throws Throwable + { + createTable("CREATE TABLE %s (id int, col int, val text, PRIMARY KEY (id, col)) WITH CLUSTERING ORDER BY (col ASC)"); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 10, "10"); + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 30, "30"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 20, "20"); + flush(); + + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 1", 1, row(1, 10, "10")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 2", 2, row(1, 10, "10"), row(1, 20, "20")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 3", 2, row(1, 10, "10"), row(1, 20, "20"), row(1, 30, "30")); + executeAndCheck("SELECT * FROM %s WHERE id=1", 2, row(1, 10, "10"), row(1, 20, "20"), row(1, 30, "30")); + + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col > 25 LIMIT 1", 1, row(1, 30, "30")); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col < 40 LIMIT 1", 1, row(1, 10, "10")); + } + + @Test + public void testSSTablesOnlyDESC() throws Throwable + { + createTable("CREATE TABLE %s (id int, col int, val text, PRIMARY KEY (id, col)) WITH CLUSTERING ORDER BY (col DESC)"); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 10, "10"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 20, "20"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 30, "30"); + flush(); + + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 1", 1, row(1, 30, "30")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 2", 2, row(1, 30, "30"), row(1, 20, "20")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 3", 3, row(1, 30, "30"), row(1, 20, "20"), row(1, 10, "10")); + executeAndCheck("SELECT * FROM %s WHERE id=1", 3, row(1, 30, "30"), row(1, 20, "20"), row(1, 10, "10")); + + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col > 25 LIMIT 1", 1, row(1, 30, "30")); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col < 40 LIMIT 1", 1, row(1, 30, "30")); + } + + @Test + public void testMixedMemtableSStablesDESC() throws Throwable + { + createTable("CREATE TABLE %s (id int, col int, val text, PRIMARY KEY (id, col)) WITH CLUSTERING ORDER BY (col DESC)"); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 10, "10"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 20, "20"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 30, "30"); + + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 1", 0, row(1, 30, "30")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 2", 1, row(1, 30, "30"), row(1, 20, "20")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 3", 2, row(1, 30, "30"), row(1, 20, "20"), row(1, 10, "10")); + executeAndCheck("SELECT * FROM %s WHERE id=1", 2, row(1, 30, "30"), row(1, 20, "20"), row(1, 10, "10")); + + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col > 25 LIMIT 1", 0, row(1, 30, "30")); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col < 40 LIMIT 1", 0, row(1, 30, "30")); + } + + @Test + public void testOverlappingSStablesDESC() throws Throwable + { + createTable("CREATE TABLE %s (id int, col int, val text, PRIMARY KEY (id, col)) WITH CLUSTERING ORDER BY (col DESC)"); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 10, "10"); + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 30, "30"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 20, "20"); + flush(); + + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 1", 1, row(1, 30, "30")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 2", 2, row(1, 30, "30"), row(1, 20, "20")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 3", 2, row(1, 30, "30"), row(1, 20, "20"), row(1, 10, "10")); + executeAndCheck("SELECT * FROM %s WHERE id=1", 2, row(1, 30, "30"), row(1, 20, "20"), row(1, 10, "10")); + + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col > 25 LIMIT 1", 1, row(1, 30, "30")); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col < 40 LIMIT 1", 1, row(1, 30, "30")); + } + + @Test + public void testDeletionOnDifferentSSTables() throws Throwable + { + createTable("CREATE TABLE %s (id int, col int, val text, PRIMARY KEY (id, col)) WITH CLUSTERING ORDER BY (col DESC)"); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 10, "10"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 20, "20"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 30, "30"); + flush(); + + execute("DELETE FROM %s WHERE id=1 and col=30"); + flush(); + + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 1", 3, row(1, 20, "20")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 2", 4, row(1, 20, "20"), row(1, 10, "10")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 3", 4, row(1, 20, "20"), row(1, 10, "10")); + executeAndCheck("SELECT * FROM %s WHERE id=1", 4, row(1, 20, "20"), row(1, 10, "10")); + + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col > 25 LIMIT 1", 2); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col < 40 LIMIT 1", 3, row(1, 20, "20")); + } + + @Test + public void testDeletionOnSameSSTable() throws Throwable + { + createTable("CREATE TABLE %s (id int, col int, val text, PRIMARY KEY (id, col)) WITH CLUSTERING ORDER BY (col DESC)"); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 10, "10"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 20, "20"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 30, "30"); + execute("DELETE FROM %s WHERE id=1 and col=30"); + flush(); + + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 1", 2, row(1, 20, "20")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 2", 3, row(1, 20, "20"), row(1, 10, "10")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 3", 3, row(1, 20, "20"), row(1, 10, "10")); + executeAndCheck("SELECT * FROM %s WHERE id=1", 3, row(1, 20, "20"), row(1, 10, "10")); + + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col > 25 LIMIT 1", 1); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col < 40 LIMIT 1", 2, row(1, 20, "20")); + } + + @Test + public void testDeletionOnMemTable() throws Throwable + { + createTable("CREATE TABLE %s (id int, col int, val text, PRIMARY KEY (id, col)) WITH CLUSTERING ORDER BY (col DESC)"); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 10, "10"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 20, "20"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 30, "30"); + execute("DELETE FROM %s WHERE id=1 and col=30"); + + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 1", 1, row(1, 20, "20")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 2", 2, row(1, 20, "20"), row(1, 10, "10")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 3", 2, row(1, 20, "20"), row(1, 10, "10")); + executeAndCheck("SELECT * FROM %s WHERE id=1", 2, row(1, 20, "20"), row(1, 10, "10")); + + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col > 25 LIMIT 1", 0); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col < 40 LIMIT 1", 1, row(1, 20, "20")); + } + + @Test + public void testDeletionOnIndexedSSTableDESC() throws Throwable + { + testDeletionOnIndexedSSTableDESC(true); + testDeletionOnIndexedSSTableDESC(false); + } + + private void testDeletionOnIndexedSSTableDESC(boolean deleteWithRange) throws Throwable + { + // reduce the column index size so that columns get indexed during flush + DatabaseDescriptor.setColumnIndexSize(1); + + createTable("CREATE TABLE %s (id int, col int, val text, PRIMARY KEY (id, col)) WITH CLUSTERING ORDER BY (col DESC)"); + + for (int i = 1; i <= 1000; i++) + { + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, i, Integer.toString(i)); + } + flush(); + + Object[][] allRows = new Object[1000][]; + for (int i = 1001; i <= 2000; i++) + { + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, i, Integer.toString(i)); + allRows[2000 - i] = row(1, i, Integer.toString(i)); + } + + if (deleteWithRange) + { + execute("DELETE FROM %s WHERE id=1 and col <= ?", 1000); + } + else + { + for (int i = 1; i <= 1000; i++) + execute("DELETE FROM %s WHERE id=1 and col = ?", i); + } + flush(); + + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 1", 1, row(1, 2000, "2000")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 2", 1, row(1, 2000, "2000"), row(1, 1999, "1999")); + + executeAndCheck("SELECT * FROM %s WHERE id=1", 2, allRows); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col > 1000 LIMIT 1", 1, row(1, 2000, "2000")); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col <= 2000 LIMIT 1", 1, row(1, 2000, "2000")); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col > 1000", 1, allRows); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col <= 2000", 2, allRows); + } + + @Test + public void testDeletionOnIndexedSSTableASC() throws Throwable + { + testDeletionOnIndexedSSTableASC(true); + testDeletionOnIndexedSSTableASC(false); + } + + private void testDeletionOnIndexedSSTableASC(boolean deleteWithRange) throws Throwable + { + // reduce the column index size so that columns get indexed during flush + DatabaseDescriptor.setColumnIndexSize(1); + + createTable("CREATE TABLE %s (id int, col int, val text, PRIMARY KEY (id, col)) WITH CLUSTERING ORDER BY (col ASC)"); + + for (int i = 1; i <= 1000; i++) + { + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, i, Integer.toString(i)); + } + flush(); + + Object[][] allRows = new Object[1000][]; + for (int i = 1001; i <= 2000; i++) + { + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, i, Integer.toString(i)); + allRows[i - 1001] = row(1, i, Integer.toString(i)); + } + flush(); + + if (deleteWithRange) + { + execute("DELETE FROM %s WHERE id =1 and col <= ?", 1000); + } + else + { + for (int i = 1; i <= 1000; i++) + execute("DELETE FROM %s WHERE id=1 and col = ?", i); + } + flush(); + + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 1", 3, row(1, 1001, "1001")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 2", 3, row(1, 1001, "1001"), row(1, 1002, "1002")); + + executeAndCheck("SELECT * FROM %s WHERE id=1", 3, allRows); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col > 1000 LIMIT 1", 2, row(1, 1001, "1001")); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col <= 2000 LIMIT 1", 3, row(1, 1001, "1001")); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col > 1000", 2, allRows); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col <= 2000", 3, allRows); + } + + @Test + public void testDeletionOnOverlappingIndexedSSTable() throws Throwable + { + testDeletionOnOverlappingIndexedSSTable(true); + testDeletionOnOverlappingIndexedSSTable(false); + } + + private void testDeletionOnOverlappingIndexedSSTable(boolean deleteWithRange) throws Throwable + { + // reduce the column index size so that columns get indexed during flush + DatabaseDescriptor.setColumnIndexSize(1); + + createTable("CREATE TABLE %s (id int, col int, val1 text, val2 text, PRIMARY KEY (id, col)) WITH CLUSTERING ORDER BY (col ASC)"); + + for (int i = 1; i <= 500; i++) + { + if (i % 2 == 0) + execute("INSERT INTO %s (id, col, val1) VALUES (?, ?, ?)", 1, i, Integer.toString(i)); + else + execute("INSERT INTO %s (id, col, val1, val2) VALUES (?, ?, ?, ?)", 1, i, Integer.toString(i), Integer.toString(i)); + } + + for (int i = 1001; i <= 1500; i++) + { + if (i % 2 == 0) + execute("INSERT INTO %s (id, col, val1) VALUES (?, ?, ?)", 1, i, Integer.toString(i)); + else + execute("INSERT INTO %s (id, col, val1, val2) VALUES (?, ?, ?, ?)", 1, i, Integer.toString(i), Integer.toString(i)); + } + + flush(); + + for (int i = 501; i <= 1000; i++) + { + if (i % 2 == 0) + execute("INSERT INTO %s (id, col, val1) VALUES (?, ?, ?)", 1, i, Integer.toString(i)); + else + execute("INSERT INTO %s (id, col, val1, val2) VALUES (?, ?, ?, ?)", 1, i, Integer.toString(i), Integer.toString(i)); + } + + for (int i = 1501; i <= 2000; i++) + { + if (i % 2 == 0) + execute("INSERT INTO %s (id, col, val1) VALUES (?, ?, ?)", 1, i, Integer.toString(i)); + else + execute("INSERT INTO %s (id, col, val1, val2) VALUES (?, ?, ?, ?)", 1, i, Integer.toString(i), Integer.toString(i)); + } + + if (deleteWithRange) + { + execute("DELETE FROM %s WHERE id=1 and col > ? and col <= ?", 250, 750); + } + else + { + for (int i = 251; i <= 750; i++) + execute("DELETE FROM %s WHERE id=1 and col = ?", i); + } + + flush(); + + Object[][] allRows = new Object[1500][]; // non deleted rows + for (int i = 1; i <= 2000; i++) + { + if (i > 250 && i <= 750) + continue; // skip deleted records + + int idx = (i <= 250 ? i - 1 : i - 501); + + if (i % 2 == 0) + allRows[idx] = row(1, i, Integer.toString(i), null); + else + allRows[idx] = row(1, i, Integer.toString(i), Integer.toString(i)); + } + + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 1", 2, row(1, 1, "1", "1")); + executeAndCheck("SELECT * FROM %s WHERE id=1 LIMIT 2", 2, row(1, 1, "1", "1"), row(1, 2, "2", null)); + + executeAndCheck("SELECT * FROM %s WHERE id=1", 2, allRows); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col > 1000 LIMIT 1", 2, row(1, 1001, "1001", "1001")); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col <= 2000 LIMIT 1", 2, row(1, 1, "1", "1")); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col > 500 LIMIT 1", 2, row(1, 751, "751", "751")); + executeAndCheck("SELECT * FROM %s WHERE id=1 AND col <= 500 LIMIT 1", 2, row(1, 1, "1", "1")); + } + + @Test + public void testMultiplePartitionsDESC() throws Throwable + { + createTable("CREATE TABLE %s (id int, col int, val text, PRIMARY KEY (id, col)) WITH CLUSTERING ORDER BY (col DESC)"); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 10, "10"); + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 2, 10, "10"); + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 3, 10, "10"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 20, "20"); + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 2, 20, "20"); + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 3, 20, "20"); + flush(); + + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 1, 30, "30"); + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 2, 30, "30"); + execute("INSERT INTO %s (id, col, val) VALUES (?, ?, ?)", 3, 30, "30"); + flush(); + + for (int i = 1; i <= 3; i++) + { + String base = "SELECT * FROM %s "; + + executeAndCheck(base + String.format("WHERE id=%d LIMIT 1", i), 1, row(i, 30, "30")); + executeAndCheck(base + String.format("WHERE id=%d LIMIT 2", i), 2, row(i, 30, "30"), row(i, 20, "20")); + executeAndCheck(base + String.format("WHERE id=%d LIMIT 3", i), 3, row(i, 30, "30"), row(i, 20, "20"), row(i, 10, "10")); + executeAndCheck(base + String.format("WHERE id=%d", i), 3, row(i, 30, "30"), row(i, 20, "20"), row(i, 10, "10")); + + executeAndCheck(base + String.format("WHERE id=%d AND col > 25 LIMIT 1", i), 1, row(i, 30, "30")); + executeAndCheck(base + String.format("WHERE id=%d AND col < 40 LIMIT 1", i), 1, row(i, 30, "30")); + } + } ++ ++ @Test + public void testNonCompactTableRowDeletion() throws Throwable + { + createTable("CREATE TABLE %s (pk int, ck int, v text, PRIMARY KEY (pk, ck))"); + + execute("INSERT INTO %s (pk, ck, v) VALUES (1, 1, '1')"); + flush(); + + execute("DELETE FROM %s WHERE pk = 1 AND ck = 1"); + flush(); + + executeAndCheck("SELECT * FROM %s WHERE pk = 1 AND ck = 1", 2); + } + + @Test + public void testNonCompactTableRangeDeletion() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY (a, b, c))"); + + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 1, 1, 1, 1); + flush(); + + execute("DELETE FROM %s WHERE a=? AND b=?", 1, 1); + flush(); + + executeAndCheck("SELECT * FROM %s WHERE a=1 AND b=1 AND c=1", 2); + } + + @Test + public void testNonCompactTableCellsDeletion() throws Throwable + { + createTable("CREATE TABLE %s (pk int, ck int, v1 text, v2 text, PRIMARY KEY (pk, ck))"); + + execute("INSERT INTO %s (pk, ck, v1, v2) VALUES (1, 1, '1', '1')"); + flush(); + + execute("DELETE v1 FROM %s WHERE pk = 1 AND ck = 1"); + execute("DELETE v2 FROM %s WHERE pk = 1 AND ck = 1"); + flush(); + + executeAndCheck("SELECT * FROM %s WHERE pk = 1 AND ck = 1", 2, row(1, 1, null, null)); + } + + @Test + public void testCompactTableSkipping() throws Throwable + { + createTable("CREATE TABLE %s (pk int, ck int, v text, PRIMARY KEY (pk, ck)) WITH COMPACT STORAGE"); + + execute("INSERT INTO %s (pk, ck, v) VALUES (1, 1, '1') USING TIMESTAMP 1000000"); + execute("INSERT INTO %s (pk, ck, v) VALUES (1, 50, '2') USING TIMESTAMP 1000001"); + execute("INSERT INTO %s (pk, ck, v) VALUES (1, 100, '3') USING TIMESTAMP 1000002"); + flush(); + + execute("INSERT INTO %s (pk, ck, v) VALUES (1, 2, '4') USING TIMESTAMP 2000000"); + execute("INSERT INTO %s (pk, ck, v) VALUES (1, 51, '5') USING TIMESTAMP 2000001"); + execute("INSERT INTO %s (pk, ck, v) VALUES (1, 101, '6') USING TIMESTAMP 2000002"); + flush(); + + executeAndCheck("SELECT * FROM %s WHERE pk = 1 AND ck = 51", 1, row(1, 51, "5")); + + execute("ALTER TABLE %s DROP COMPACT STORAGE"); + executeAndCheck("SELECT * FROM %s WHERE pk = 1 AND ck = 51", 1, row(1, 51, "5")); + } + + @Test + public void testCompactTableSkippingPkOnly() throws Throwable + { + createTable("CREATE TABLE %s (pk int, ck int, PRIMARY KEY (pk, ck)) WITH COMPACT STORAGE"); + + execute("INSERT INTO %s (pk, ck) VALUES (1, 1) USING TIMESTAMP 1000000"); + execute("INSERT INTO %s (pk, ck) VALUES (1, 50) USING TIMESTAMP 1000001"); + execute("INSERT INTO %s (pk, ck) VALUES (1, 100) USING TIMESTAMP 1000002"); + flush(); + + execute("INSERT INTO %s (pk, ck) VALUES (1, 2) USING TIMESTAMP 2000000"); + execute("INSERT INTO %s (pk, ck) VALUES (1, 51) USING TIMESTAMP 2000001"); + execute("INSERT INTO %s (pk, ck) VALUES (1, 101) USING TIMESTAMP 2000002"); + flush(); + + executeAndCheck("SELECT * FROM %s WHERE pk = 1 AND ck = 51", 1, row(1, 51)); + + execute("ALTER TABLE %s DROP COMPACT STORAGE"); + executeAndCheck("SELECT * FROM %s WHERE pk = 1 AND ck = 51", 1, row(1, 51)); + } + + @Test + public void testCompactTableCellDeletion() throws Throwable + { + createTable("CREATE TABLE %s (pk int, ck int, v text, PRIMARY KEY (pk, ck)) WITH COMPACT STORAGE"); + + execute("INSERT INTO %s (pk, ck, v) VALUES (1, 1, '1')"); + flush(); + + execute("DELETE v FROM %s WHERE pk = 1 AND ck = 1"); + flush(); + + executeAndCheck("SELECT * FROM %s WHERE pk = 1 AND ck = 1", 1); + + // Dropping compact storage forces us to hit an extra SSTable, since we can't rely on the isDense flag + // to determine that a row with a complete set of column deletes is complete. + execute("ALTER TABLE %s DROP COMPACT STORAGE"); + executeAndCheck("SELECT * FROM %s WHERE pk = 1 AND ck = 1", 2); + } + + @Test + public void testCompactTableRowDeletion() throws Throwable + { + createTable("CREATE TABLE %s (pk int, ck int, v text, PRIMARY KEY (pk, ck)) WITH COMPACT STORAGE"); + + execute("INSERT INTO %s (pk, ck, v) VALUES (1, 1, '1')"); + flush(); + + execute("DELETE FROM %s WHERE pk = 1 AND ck = 1"); + flush(); + + executeAndCheck("SELECT * FROM %s WHERE pk = 1 AND ck = 1", 1); + + // Dropping compact storage forces us to hit an extra SSTable, since we can't rely on the isDense flag + // to determine that a row with a complete set of column deletes is complete. + execute("ALTER TABLE %s DROP COMPACT STORAGE"); + executeAndCheck("SELECT * FROM %s WHERE pk = 1 AND ck = 1", 2); + } + + @Test + public void testCompactTableRangeDeletion() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY (a, b, c)) WITH COMPACT STORAGE"); + + 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 (?, ?, ?, ?)", 1, 2, 1, 1); + flush(); + + execute("DELETE FROM %s WHERE a=? AND b=?", 1, 1); + flush(); + + // Even with a compact table, we can't short-circuit for a range deletion rather than a cell tombstone. + executeAndCheck("SELECT * FROM %s WHERE a=1 AND b=1 AND c=1", 2); + + execute("ALTER TABLE %s DROP COMPACT STORAGE"); + executeAndCheck("SELECT * FROM %s WHERE a=1 AND b=1 AND c=1", 2); + } + + @Test + public void testCompactTableRangeOverRowDeletion() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY (a, b, c)) WITH COMPACT STORAGE"); + + 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 (?, ?, ?, ?)", 1, 2, 1, 1); + flush(); + + execute("DELETE FROM %s WHERE a=? AND b=? AND c=?", 1, 1, 1); + flush(); + + execute("DELETE FROM %s WHERE a=? AND b=?", 1, 1); + flush(); + + // The range delete will subsume the row delete, and the latter will not factor into skipping decisions. + executeAndCheck("SELECT * FROM %s WHERE a=1 AND b=1 AND c=1", 3); + + execute("ALTER TABLE %s DROP COMPACT STORAGE"); + executeAndCheck("SELECT * FROM %s WHERE a=1 AND b=1 AND c=1", 3); + } + + @Test + public void testCompactTableRowOverRangeDeletion() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY (a, b, c)) WITH COMPACT STORAGE"); + + 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 (?, ?, ?, ?)", 1, 2, 1, 1); + flush(); + + execute("DELETE FROM %s WHERE a=? AND b=?", 1, 1); + flush(); + + execute("DELETE FROM %s WHERE a=? AND b=? AND c=?", 1, 1, 1); + flush(); + + // The row delete provides a tombstone, which is enough information to short-circuit after the first SSTable. + executeAndCheck("SELECT * FROM %s WHERE a=1 AND b=1 AND c=1", 1); + + execute("ALTER TABLE %s DROP COMPACT STORAGE"); + executeAndCheck("SELECT * FROM %s WHERE a=1 AND b=1 AND c=1", 3); + } + + @Test + public void testCompactTableCellUpdate() throws Throwable + { + createTable("CREATE TABLE %s (pk int, ck int, v text, PRIMARY KEY (pk, ck)) WITH COMPACT STORAGE"); + + execute("INSERT INTO %s (pk, ck, v) VALUES (1, 1, '1')"); + flush(); + + execute("UPDATE %s SET v = '2' WHERE pk = 1 AND ck = 1"); + flush(); + + executeAndCheck("SELECT * FROM %s WHERE pk = 1 AND ck = 1", 1, row(1, 1, "2")); + + execute("ALTER TABLE %s DROP COMPACT STORAGE"); + executeAndCheck("SELECT * FROM %s WHERE pk = 1 AND ck = 1", 1, row(1, 1, "2")); + } + + @Test + public void testCompactTableDeleteOverlappingSSTables() throws Throwable + { + createTable("CREATE TABLE %s (pk int, ck int, PRIMARY KEY (pk, ck)) WITH COMPACT STORAGE"); + + execute("INSERT INTO %s (pk, ck) VALUES (1, 51) USING TIMESTAMP 1000002"); + flush(); + execute("DELETE FROM %s WHERE pk = 1 AND ck = 51"); + flush(); + + execute("INSERT INTO %s (pk, ck) VALUES (1, 51) USING TIMESTAMP 1000001"); + execute("INSERT INTO %s (pk, ck) VALUES (2, 51)"); + flush(); + + // If it weren't for the write to pk = 2, ck = 51, we could skip the third SSTable too and hit only one here. + executeAndCheck("SELECT * FROM %s WHERE pk = 1 AND ck = 51", 2); + + // Dropping compact storage forces us to hit an extra SSTable, since we can't rely on the isDense flag + // to determine that a row with a complete set of column deletes is complete. + execute("ALTER TABLE %s DROP COMPACT STORAGE"); + executeAndCheck("SELECT * FROM %s WHERE pk = 1 AND ck = 51", 3); + } } diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/UpdateTest.java index 1a8b49b,42a8560..c9c8051 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/UpdateTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/UpdateTest.java @@@ -20,20 -20,16 +20,20 @@@ package org.apache.cassandra.cql3.valid import java.util.Arrays; - import org.junit.Assert; import org.junit.Test; -import static org.apache.commons.lang3.StringUtils.isEmpty; -import static org.junit.Assert.assertTrue; - +import org.apache.cassandra.cql3.Attributes; import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.cql3.UntypedResultSet; +import org.apache.cassandra.cql3.UntypedResultSet.Row; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.utils.ByteBufferUtil; +import static org.apache.commons.lang3.StringUtils.isEmpty; +import static org.junit.Assert.assertTrue; ++import static org.junit.Assert.assertEquals; + public class UpdateTest extends CQLTester { @Test @@@ -497,62 -493,6 +497,62 @@@ assertRows(execute("SELECT l FROM %s WHERE k = 0"), row(list("v1", "v4", "v3"))); } + @Test + public void testUpdateWithDefaultTtl() throws Throwable + { + final int secondsPerMinute = 60; + createTable("CREATE TABLE %s (a int PRIMARY KEY, b int) WITH default_time_to_live = " + (10 * secondsPerMinute)); + + execute("UPDATE %s SET b = 1 WHERE a = 1"); + UntypedResultSet resultSet = execute("SELECT ttl(b) FROM %s WHERE a = 1"); - Assert.assertEquals(1, resultSet.size()); ++ assertEquals(1, resultSet.size()); + Row row = resultSet.one(); - Assert.assertTrue(row.getInt("ttl(b)") >= (9 * secondsPerMinute)); ++ assertTrue(row.getInt("ttl(b)") >= (9 * secondsPerMinute)); + + execute("UPDATE %s USING TTL ? SET b = 3 WHERE a = 1", 0); + assertRows(execute("SELECT ttl(b) FROM %s WHERE a = 1"), row(new Object[]{null})); + + execute("UPDATE %s SET b = 3 WHERE a = 1"); + resultSet = execute("SELECT ttl(b) FROM %s WHERE a = 1"); - Assert.assertEquals(1, resultSet.size()); ++ assertEquals(1, resultSet.size()); + row = resultSet.one(); - Assert.assertTrue(row.getInt("ttl(b)") >= (9 * secondsPerMinute)); ++ assertTrue(row.getInt("ttl(b)") >= (9 * secondsPerMinute)); + + execute("UPDATE %s USING TTL ? SET b = 2 WHERE a = 2", unset()); + resultSet = execute("SELECT ttl(b) FROM %s WHERE a = 2"); - Assert.assertEquals(1, resultSet.size()); ++ assertEquals(1, resultSet.size()); + row = resultSet.one(); - Assert.assertTrue(row.getInt("ttl(b)") >= (9 * secondsPerMinute)); ++ assertTrue(row.getInt("ttl(b)") >= (9 * secondsPerMinute)); + + execute("UPDATE %s USING TTL ? SET b = ? WHERE a = ?", null, 3, 3); + assertRows(execute("SELECT ttl(b) FROM %s WHERE a = 3"), row(new Object[] { null })); + } + + @Test + public void testUpdateWithTtl() throws Throwable + { + createTable("CREATE TABLE %s (k int PRIMARY KEY, v int)"); + + execute("INSERT INTO %s (k, v) VALUES (1, 1) USING TTL ?", 3600); + execute("INSERT INTO %s (k, v) VALUES (2, 2) USING TTL ?", 3600); + + // test with unset + execute("UPDATE %s USING TTL ? SET v = ? WHERE k = ?", unset(), 1, 1); // treat as 'unlimited' + assertRows(execute("SELECT ttl(v) FROM %s WHERE k = 1"), row(new Object[] { null })); + + // test with null + execute("UPDATE %s USING TTL ? SET v = ? WHERE k = ?", unset(), 2, 2); + assertRows(execute("SELECT k, v, TTL(v) FROM %s WHERE k = 2"), row(2, 2, null)); + + // test error handling + assertInvalidMessage("A TTL must be greater or equal to 0, but was -5", + "UPDATE %s USING TTL ? SET v = ? WHERE k = ?", -5, 1, 1); + + assertInvalidMessage("ttl is too large.", + "UPDATE %s USING TTL ? SET v = ? WHERE k = ?", + Attributes.MAX_TTL + 1, 1, 1); + } + /** * Test for CASSANDRA-12829 */ --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
