diff --git a/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java b/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java index ae2a4e583670..c203ace7c8fe 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java +++ b/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java @@ -20,8 +20,7 @@ import java.nio.ByteBuffer; import java.util.*; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; +import com.google.common.collect.*; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.cql3.*; @@ -179,19 +178,22 @@ public SinglePartitionReadCommand readCommand(int nowInSec) { assert staticConditions != null || !conditions.isEmpty(); + // Fetch all columns, but query only the selected ones + ColumnFilter columnFilter = ColumnFilter.selection(cfm, columnsToRead()); + // With only a static condition, we still want to make the distinction between a non-existing partition and one // that exists (has some live data) but has not static content. So we query the first live row of the partition. if (conditions.isEmpty()) return SinglePartitionReadCommand.create(cfm, nowInSec, - ColumnFilter.selection(columnsToRead()), + columnFilter, RowFilter.NONE, DataLimits.cqlLimits(1), key, new ClusteringIndexSliceFilter(Slices.ALL, false)); ClusteringIndexNamesFilter filter = new ClusteringIndexNamesFilter(conditions.navigableKeySet(), false); - return SinglePartitionReadCommand.create(cfm, nowInSec, key, ColumnFilter.selection(columnsToRead()), filter); + return SinglePartitionReadCommand.create(cfm, nowInSec, key, columnFilter, filter); } /** diff --git a/src/java/org/apache/cassandra/db/filter/ColumnFilter.java b/src/java/org/apache/cassandra/db/filter/ColumnFilter.java index 0dd0aac17a46..d320fc3c130b 100644 --- a/src/java/org/apache/cassandra/db/filter/ColumnFilter.java +++ b/src/java/org/apache/cassandra/db/filter/ColumnFilter.java @@ -105,6 +105,15 @@ public static ColumnFilter selection(PartitionColumns columns) return new ColumnFilter(false, null, columns, null); } + /** + * A filter that fetches all columns for the provided table, but returns + * only the queried ones. + */ + public static ColumnFilter selection(CFMetaData metadata, PartitionColumns queried) + { + return new ColumnFilter(true, metadata, queried, null); + } + /** * The columns that needs to be fetched internally for this filter. * diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java index 0f6a9184f97e..6407300f8911 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java @@ -186,6 +186,10 @@ public void testConditionalDelete() throws Throwable execute("INSERT INTO %s (k, s, i, v) VALUES ('k', 's', 0, 'v')"); assertRows(execute("DELETE v FROM %s WHERE k='k' AND i=0 IF EXISTS"), row(true)); assertRows(execute("DELETE FROM %s WHERE k='k' AND i=0 IF EXISTS"), row(true)); + assertRows(execute("SELECT * FROM %s"), row("k", null, "s", null)); + assertRows(execute("DELETE v FROM %s WHERE k='k' AND i=0 IF s = 'z'"), row(false, "s")); + assertRows(execute("DELETE v FROM %s WHERE k='k' AND i=0 IF v = 'z'"), row(false)); + assertRows(execute("DELETE v FROM %s WHERE k='k' AND i=0 IF v = 'z' AND s = 'z'"), row(false, null, "s")); assertRows(execute("DELETE v FROM %s WHERE k='k' AND i=0 IF EXISTS"), row(false)); assertRows(execute("DELETE FROM %s WHERE k='k' AND i=0 IF EXISTS"), row(false)); @@ -220,6 +224,28 @@ public void testConditionalDelete() throws Throwable assertRows(execute("DELETE FROM %s WHERE k = 1 AND i = 2 IF s = 1"), row(true)); assertEmpty(execute("SELECT * FROM %s WHERE k = 1 AND i = 2")); assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, null, 1, null)); + + createTable("CREATE TABLE %s (k int, i int, v1 int, v2 int, s int static, PRIMARY KEY (k, i))"); + execute("INSERT INTO %s (k, i, v1, v2, s) VALUES (?, ?, ?, ?, ?)", + 1, 1, 1, 1, 1); + assertRows(execute("DELETE v1 FROM %s WHERE k = 1 AND i = 1 IF EXISTS"), + row(true)); + assertRows(execute("DELETE v2 FROM %s WHERE k = 1 AND i = 1 IF EXISTS"), + row(true)); + assertRows(execute("DELETE FROM %s WHERE k = 1 AND i = 1 IF EXISTS"), + row(true)); + assertRows(execute("select * from %s"), + row(1, null, 1, null, null)); + assertRows(execute("DELETE v1 FROM %s WHERE k = 1 AND i = 1 IF EXISTS"), + row(false)); + assertRows(execute("DELETE v1 FROM %s WHERE k = 1 AND i = 1 IF s = 5"), + row(false, 1)); + assertRows(execute("DELETE v1 FROM %s WHERE k = 1 AND i = 1 IF v1 = 1 AND v2 = 1"), + row(false)); + assertRows(execute("DELETE v1 FROM %s WHERE k = 1 AND i = 1 IF v1 = 1 AND v2 = 1 AND s = 1"), + row(false, null, null, 1)); + assertRows(execute("DELETE v1 FROM %s WHERE k = 1 AND i = 5 IF s = 1"), + row(true)); } /** @@ -249,13 +275,17 @@ public void testStaticColumnsCas() throws Throwable assertRows(execute("UPDATE %s SET v='bar', version=2 WHERE id=0 AND k='k2' IF version = 1"), row(true)); assertRows(execute("SELECT * FROM %s"), row(0, "k1", 2, "foo"), row(0, "k2", 2, "bar")); + // Batch output is slightly different from non-batch CAS, since a full PK is included to disambiguate + // cases when conditions span across multiple rows. + assertRows(execute("UPDATE %1$s SET version=3 WHERE id=0 IF version=1; "), + row(false, 2)); // Testing batches assertRows(execute("BEGIN BATCH " + "UPDATE %1$s SET v='foobar' WHERE id=0 AND k='k1'; " + "UPDATE %1$s SET v='barfoo' WHERE id=0 AND k='k2'; " + "UPDATE %1$s SET version=3 WHERE id=0 IF version=1; " + "APPLY BATCH "), - row(false, 0, null, 2)); + row(false, 0, "k1", 2)); assertRows(execute("BEGIN BATCH " + "UPDATE %1$s SET v = 'foobar' WHERE id = 0 AND k = 'k1'; " +