Skip to content

Commit

Permalink
Make distinction between unset row and non-existing partition for LWTs
Browse files Browse the repository at this point in the history
Patch by Alex Petrov; reviewed by Sylvain Lebresne for CASSANDRA-12964.
  • Loading branch information
ifesdjeen authored and Sylvain Lebresne committed Dec 9, 2016
1 parent 1e06774 commit 1cbacea
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 5 deletions.
Expand Up @@ -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.*;
Expand Down Expand Up @@ -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);
}

/**
Expand Down
9 changes: 9 additions & 0 deletions src/java/org/apache/cassandra/db/filter/ColumnFilter.java
Expand Up @@ -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.
*
Expand Down
Expand Up @@ -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));

Expand Down Expand Up @@ -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));
}

/**
Expand Down Expand Up @@ -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'; " +
Expand Down

0 comments on commit 1cbacea

Please sign in to comment.