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/714edbce Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/714edbce Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/714edbce Branch: refs/heads/cassandra-3.11 Commit: 714edbce9a38c70a0f031a73d3c1a20f7f2b4bb3 Parents: e1da99a 70e33d9 Author: Benjamin Lerer <b.le...@gmail.com> Authored: Fri Jan 27 15:26:18 2017 +0100 Committer: Benjamin Lerer <b.le...@gmail.com> Committed: Fri Jan 27 15:29:40 2017 +0100 ---------------------------------------------------------------------- CHANGES.txt | 1 + .../apache/cassandra/cql3/ColumnCondition.java | 62 ++++++- .../operations/InsertUpdateIfConditionTest.java | 162 ++++++++++++++++++- 3 files changed, 215 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/714edbce/CHANGES.txt ---------------------------------------------------------------------- diff --cc CHANGES.txt index 3796a8d,c5e5335..547fc07 --- a/CHANGES.txt +++ b/CHANGES.txt @@@ -1,32 -1,5 +1,33 @@@ -2.2.9 +3.0.11 + * Better error when modifying function permissions without explicit keyspace (CASSANDRA-12925) + * Indexer is not correctly invoked when building indexes over sstables (CASSANDRA-13075) + * Read repair is not blocking repair to finish in foreground repair (CASSANDRA-13115) + * Stress daemon help is incorrect (CASSANDRA-12563) + * Remove ALTER TYPE support (CASSANDRA-12443) + * Fix assertion for certain legacy range tombstone pattern (CASSANDRA-12203) + * Set javac encoding to utf-8 (CASSANDRA-11077) + * Replace empty strings with null values if they cannot be converted (CASSANDRA-12794) + * Fixed flacky SSTableRewriterTest: check file counts before calling validateCFS (CASSANDRA-12348) + * Fix deserialization of 2.x DeletedCells (CASSANDRA-12620) + * Add parent repair session id to anticompaction log message (CASSANDRA-12186) + * Improve contention handling on failure to acquire MV lock for streaming and hints (CASSANDRA-12905) + * Fix DELETE and UPDATE queries with empty IN restrictions (CASSANDRA-12829) + * Mark MVs as built after successful bootstrap (CASSANDRA-12984) + * Estimated TS drop-time histogram updated with Cell.NO_DELETION_TIME (CASSANDRA-13040) + * Nodetool compactionstats fails with NullPointerException (CASSANDRA-13021) + * Thread local pools never cleaned up (CASSANDRA-13033) + * Set RPC_READY to false when draining or if a node is marked as shutdown (CASSANDRA-12781) + * Make sure sstables only get committed when it's safe to discard commit log records (CASSANDRA-12956) + * Reject default_time_to_live option when creating or altering MVs (CASSANDRA-12868) + * Nodetool should use a more sane max heap size (CASSANDRA-12739) + * LocalToken ensures token values are cloned on heap (CASSANDRA-12651) + * AnticompactionRequestSerializer serializedSize is incorrect (CASSANDRA-12934) + * Prevent reloading of logback.xml from UDF sandbox (CASSANDRA-12535) + * Reenable HeapPool (CASSANDRA-12900) +Merged from 2.2: + * Fix handling of nulls and unsets in IN conditions (CASSANDRA-12981) + * Fix race causing infinite loop if Thrift server is stopped before it starts listening (CASSANDRA-12856) + * CompactionTasks now correctly drops sstables out of compaction when not enough disk space is available (CASSANDRA-12979) * Remove support for non-JavaScript UDFs (CASSANDRA-12883) * Fix DynamicEndpointSnitch noop in multi-datacenter situations (CASSANDRA-13074) * cqlsh copy-from: encode column names to avoid primary key parsing errors (CASSANDRA-12909) http://git-wip-us.apache.org/repos/asf/cassandra/blob/714edbce/src/java/org/apache/cassandra/cql3/ColumnCondition.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/ColumnCondition.java index b13e534,3412e71..60e67f3 --- a/src/java/org/apache/cassandra/cql3/ColumnCondition.java +++ b/src/java/org/apache/cassandra/cql3/ColumnCondition.java @@@ -23,8 -23,16 +23,9 @@@ import java.util.* import com.google.common.collect.Iterators; import org.apache.cassandra.config.ColumnDefinition; + import org.apache.cassandra.cql3.Term.Terminal; import org.apache.cassandra.cql3.functions.Function; -import org.apache.cassandra.db.Cell; -import org.apache.cassandra.db.ColumnFamily; -import org.apache.cassandra.db.composites.CellName; -import org.apache.cassandra.db.composites.CellNameType; -import org.apache.cassandra.db.composites.Composite; -import org.apache.cassandra.db.filter.ColumnSlice; +import org.apache.cassandra.db.rows.*; import org.apache.cassandra.db.marshal.*; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.transport.Server; http://git-wip-us.apache.org/repos/asf/cassandra/blob/714edbce/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java ---------------------------------------------------------------------- diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java index fd02a69,6396727..8adce7a --- a/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java @@@ -155,8 -150,6 +159,7 @@@ public class InsertUpdateIfConditionTes "UPDATE %s SET v1 = 3, v2 = 'bar' WHERE k = 0 IF v1 >= ?", unset()); assertInvalidMessage("Invalid 'unset' value in condition", "UPDATE %s SET v1 = 3, v2 = 'bar' WHERE k = 0 IF v1 != ?", unset()); - assertInvalidMessage("Invalid 'unset' value in condition", - "UPDATE %s SET v1 = 3, v2 = 'bar' WHERE k = 0 IF v1 IN (?, ?)", unset(), 1); ++ } /** @@@ -1482,107 -1306,158 +1485,262 @@@ row(7, 7, null, null, 7)); } + /** + * Test for CASSANDRA-12060, using a table without clustering. + */ + @Test + public void testMultiExistConditionOnSameRowNoClustering() throws Throwable + { + createTable("CREATE TABLE %s (k int PRIMARY KEY, v1 text, v2 text)"); + + // Multiple inserts on the same row with not exist conditions + assertRows(execute("BEGIN BATCH " + + "INSERT INTO %1$s (k, v1) values (0, 'foo') IF NOT EXISTS; " + + "INSERT INTO %1$s (k, v2) values (0, 'bar') IF NOT EXISTS; " + + "APPLY BATCH"), + row(true)); + + assertRows(execute("SELECT * FROM %s WHERE k = 0"), row(0, "foo", "bar")); + + // Same, but both insert on the same column: doing so would almost surely be a user error, but that's the + // original case reported in #12867, so being thorough. + assertRows(execute("BEGIN BATCH " + + "INSERT INTO %1$s (k, v1) values (1, 'foo') IF NOT EXISTS; " + + "INSERT INTO %1$s (k, v1) values (1, 'bar') IF NOT EXISTS; " + + "APPLY BATCH"), + row(true)); + + // As all statement gets the same timestamp, the biggest value ends up winning, so that's "foo" + assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, "foo", null)); + + // Multiple deletes on the same row with exists conditions (note that this is somewhat non-sensical, one of the + // delete is redundant, we're just checking it doesn't break something) + assertRows(execute("BEGIN BATCH " + + "DELETE FROM %1$s WHERE k = 0 IF EXISTS; " + + "DELETE FROM %1$s WHERE k = 0 IF EXISTS; " + + "APPLY BATCH"), + row(true)); + + assertEmpty(execute("SELECT * FROM %s WHERE k = 0")); + + // Validate we can't mix different type of conditions however + assertInvalidMessage("Cannot mix IF EXISTS and IF NOT EXISTS conditions for the same row", + "BEGIN BATCH " + + "INSERT INTO %1$s (k, v1) values (1, 'foo') IF NOT EXISTS; " + + "DELETE FROM %1$s WHERE k = 1 IF EXISTS; " + + "APPLY BATCH"); + + assertInvalidMessage("Cannot mix IF conditions and IF NOT EXISTS for the same row", + "BEGIN BATCH " + + "INSERT INTO %1$s (k, v1) values (1, 'foo') IF NOT EXISTS; " + + "UPDATE %1$s SET v2 = 'bar' WHERE k = 1 IF v1 = 'foo'; " + + "APPLY BATCH"); + } + + /** + * Test for CASSANDRA-12060, using a table with clustering. + */ + @Test + public void testMultiExistConditionOnSameRowClustering() throws Throwable + { + createTable("CREATE TABLE %s (k int, t int, v1 text, v2 text, PRIMARY KEY (k, t))"); + + // Multiple inserts on the same row with not exist conditions + assertRows(execute("BEGIN BATCH " + + "INSERT INTO %1$s (k, t, v1) values (0, 0, 'foo') IF NOT EXISTS; " + + "INSERT INTO %1$s (k, t, v2) values (0, 0, 'bar') IF NOT EXISTS; " + + "APPLY BATCH"), + row(true)); + + assertRows(execute("SELECT * FROM %s WHERE k = 0"), row(0, 0, "foo", "bar")); + + // Same, but both insert on the same column: doing so would almost surely be a user error, but that's the + // original case reported in #12867, so being thorough. + assertRows(execute("BEGIN BATCH " + + "INSERT INTO %1$s (k, t, v1) values (1, 0, 'foo') IF NOT EXISTS; " + + "INSERT INTO %1$s (k, t, v1) values (1, 0, 'bar') IF NOT EXISTS; " + + "APPLY BATCH"), + row(true)); + + // As all statement gets the same timestamp, the biggest value ends up winning, so that's "foo" + assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, 0, "foo", null)); + + // Multiple deletes on the same row with exists conditions (note that this is somewhat non-sensical, one of the + // delete is redundant, we're just checking it doesn't break something) + assertRows(execute("BEGIN BATCH " + + "DELETE FROM %1$s WHERE k = 0 AND t = 0 IF EXISTS; " + + "DELETE FROM %1$s WHERE k = 0 AND t = 0 IF EXISTS; " + + "APPLY BATCH"), + row(true)); + + assertEmpty(execute("SELECT * FROM %s WHERE k = 0")); + + // Validate we can't mix different type of conditions however + assertInvalidMessage("Cannot mix IF EXISTS and IF NOT EXISTS conditions for the same row", + "BEGIN BATCH " + + "INSERT INTO %1$s (k, t, v1) values (1, 0, 'foo') IF NOT EXISTS; " + + "DELETE FROM %1$s WHERE k = 1 AND t = 0 IF EXISTS; " + + "APPLY BATCH"); + + assertInvalidMessage("Cannot mix IF conditions and IF NOT EXISTS for the same row", + "BEGIN BATCH " + + "INSERT INTO %1$s (k, t, v1) values (1, 0, 'foo') IF NOT EXISTS; " + + "UPDATE %1$s SET v2 = 'bar' WHERE k = 1 AND t = 0 IF v1 = 'foo'; " + + "APPLY BATCH"); + } ++ + @Test + public void testInMarkerWithUDTs() throws Throwable + { + String typename = createType("CREATE TYPE %s (a int, b text)"); + String myType = KEYSPACE + '.' + typename; + + createTable("CREATE TABLE %s (k int PRIMARY KEY, v frozen<" + myType + "> )"); + + Object v = userType(0, "abc"); + execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, v); + + // Does not apply + assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN (?, ?)", userType(1, "abc"), userType(0, "ac")), + row(false, v)); + assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN (?, ?)", userType(1, "abc"), null), + row(false, v)); + assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN (?, ?)", userType(1, "abc"), unset()), + row(false, v)); + assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN (?, ?)", null, null), + row(false, v)); + assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN (?, ?)", unset(), unset()), + row(false, v)); + assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN ?", list(userType(1, "abc"), userType(0, "ac"))), + row(false, v)); + + // Does apply + assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN (?, ?)", userType(0, "abc"), userType(0, "ac")), + row(true)); + assertRows(execute("UPDATE %s SET v = {a: 1, b: 'bc'} WHERE k = 0 IF v IN (?, ?)", userType(0, "bc"), null), + row(true)); + assertRows(execute("UPDATE %s SET v = {a: 1, b: 'ac'} WHERE k = 0 IF v IN (?, ?, ?)", userType(0, "bc"), unset(), userType(1, "bc")), + row(true)); + assertRows(execute("UPDATE %s SET v = {a: 0, b: 'abc'} WHERE k = 0 IF v IN ?", list(userType(1, "ac"), userType(0, "ac"))), + row(true)); + + assertInvalidMessage("Invalid null list in IN condition", + "UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN ?", (List<ByteBuffer>) null); + assertInvalidMessage("Invalid 'unset' value in condition", + "UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN ?", unset()); + } + + @Test + public void testInMarkerWithLists() throws Throwable + { + for (boolean frozen : new boolean[]{false, true}) + { + createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, l %s)", + frozen + ? "frozen<list<text>>" + : "list<text>")); + + execute("INSERT INTO %s(k, l) VALUES (0, ['foo', 'bar', 'foobar'])"); + + // Does not apply + assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN (?, ?)", list("foo", "foobar"), list("bar", "foobar")), + row(false, list("foo", "bar", "foobar"))); + assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN (?, ?)", list("foo", "foobar"), null), + row(false, list("foo", "bar", "foobar"))); + assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN (?, ?)", list("foo", "foobar"), unset()), + row(false, list("foo", "bar", "foobar"))); + assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN (?, ?)", null, null), + row(false, list("foo", "bar", "foobar"))); + assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN ?", list(list("foo", "foobar"), list("bar", "foobar"))), + row(false, list("foo", "bar", "foobar"))); + assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l[?] IN ?", 1, list("foo", "foobar")), + row(false, list("foo", "bar", "foobar"))); + assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l[?] IN (?, ?)", 1, "foo", "foobar"), + row(false, list("foo", "bar", "foobar"))); + assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l[?] IN (?, ?)", 1, "foo", null), + row(false, list("foo", "bar", "foobar"))); + assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l[?] IN (?, ?)", 1, "foo", unset()), + row(false, list("foo", "bar", "foobar"))); + + // Does apply + assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN (?, ?)", list("foo", "bar", "foobar"), list("bar", "foobar")), + row(true)); + assertRows(execute("UPDATE %s SET l = ['foo', 'foobar'] WHERE k = 0 IF l IN (?, ?, ?)", list("foo", "bar", "foobar"), null, list("foo", "bar")), + row(true)); + assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN (?, ?, ?)", list("foo", "bar", "foobar"), unset(), list("foo", "foobar")), + row(true)); + assertRows(execute("UPDATE %s SET l = ['foo', 'foobar'] WHERE k = 0 IF l IN (?, ?)", list("bar", "foobar"), list("foo", "bar")), + row(true)); + assertRows(execute("UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l[?] IN ?", 1, list("bar", "foobar")), + row(true)); + assertRows(execute("UPDATE %s SET l = ['foo', 'foobar'] WHERE k = 0 IF l[?] IN (?, ?, ?)", 1, "bar", null, "foobar"), + row(true)); + assertRows(execute("UPDATE %s SET l = ['foo', 'foobar'] WHERE k = 0 IF l[?] IN (?, ?, ?)", 1, "bar", unset(), "foobar"), + row(true)); + + assertInvalidMessage("Invalid null list in IN condition", + "UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN ?", (List<ByteBuffer>) null); + assertInvalidMessage("Invalid 'unset' value in condition", + "UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l IN ?", unset()); + assertInvalidMessage("Invalid 'unset' value in condition", + "UPDATE %s SET l = ['foo', 'bar'] WHERE k = 0 IF l[?] IN ?", 1, unset()); + } + } + + @Test + public void testInMarkerWithMaps() throws Throwable + { + for (boolean frozen : new boolean[] {false, true}) + { + createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, m %s)", + frozen + ? "frozen<map<text, text>>" + : "map<text, text>")); + + execute("INSERT INTO %s (k, m) VALUES (0, {'foo' : 'bar'})"); + + // Does not apply + assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m IN (?, ?)", map("foo", "foobar"), map("bar", "foobar")), + row(false, map("foo", "bar"))); + assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m IN (?, ?)", map("foo", "foobar"), null), + row(false, map("foo", "bar"))); + assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m IN (?, ?)", map("foo", "foobar"), unset()), + row(false, map("foo", "bar"))); + assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m IN (?, ?)", null, null), + row(false, map("foo", "bar"))); + assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m IN ?", list(map("foo", "foobar"), map("bar", "foobar"))), + row(false, map("foo", "bar"))); + assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN ?", "foo", list("foo", "foobar")), + row(false, map("foo", "bar"))); + assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN (?, ?)", "foo", "foo", "foobar"), + row(false, map("foo", "bar"))); + assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN (?, ?)", "foo", "foo", null), + row(false, map("foo", "bar"))); + assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN (?, ?)", "foo", "foo", unset()), + row(false, map("foo", "bar"))); + + // Does apply + assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m IN (?, ?)", map("foo", "foobar"), map("foo", "bar")), + row(true)); + assertRows(execute("UPDATE %s SET m = {'foo' : 'bar'} WHERE k = 0 IF m IN (?, ?, ?)", map("bar", "foobar"), null, map("foo", "foobar")), + row(true)); + assertRows(execute("UPDATE %s SET m = {'foo' : 'bar'} WHERE k = 0 IF m IN (?, ?, ?)", map("bar", "foobar"), unset(), map("foo", "bar")), + row(true)); + assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m IN ?", list(map("foo", "bar"), map("bar", "foobar"))), + row(true)); + assertRows(execute("UPDATE %s SET m = {'foo' : 'bar'} WHERE k = 0 IF m[?] IN ?", "foo", list("bar", "foobar")), + row(true)); + assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN (?, ?, ?)", "foo", "bar", null, "foobar"), + row(true)); + assertRows(execute("UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN (?, ?, ?)", "foo", "bar", unset(), "foobar"), + row(true)); + + assertInvalidMessage("Invalid null list in IN condition", + "UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m IN ?", (List<ByteBuffer>) null); + assertInvalidMessage("Invalid 'unset' value in condition", + "UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m IN ?", unset()); + assertInvalidMessage("Invalid 'unset' value in condition", + "UPDATE %s SET m = {'foo' : 'foobar'} WHERE k = 0 IF m[?] IN ?", "foo", unset()); + } + } }