Repository: cassandra
Updated Branches:
  refs/heads/cassandra-2.2 68398ada7 -> 5a096274d


Remove distinction between non-existing static columns and existing but null in 
LWTs

Path by Alex Petrov; reviewed by Benjamin Lerer for CASSANDRA-9842


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/85f2bbfd
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/85f2bbfd
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/85f2bbfd

Branch: refs/heads/cassandra-2.2
Commit: 85f2bbfd1c6803977ecc1c2053527363078bce22
Parents: a56bc16
Author: Alex Petrov <oleksandr.pet...@gmail.com>
Authored: Tue Jun 21 16:22:23 2016 +0200
Committer: Benjamin Lerer <b.le...@gmail.com>
Committed: Tue Jun 21 16:22:23 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../cql3/statements/ModificationStatement.java  |   5 +-
 .../apache/cassandra/service/StorageProxy.java  |   5 +-
 .../operations/InsertUpdateIfConditionTest.java | 291 ++++++++++++++++++-
 4 files changed, 298 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/85f2bbfd/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 64cfdaa..7944967 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 2.1.15
+ * Remove distinction between non-existing static columns and existing but 
null in LWTs (CASSANDRA-9842)
  * Support mlockall on IBM POWER arch (CASSANDRA-11576)
  * Cache local ranges when calculating repair neighbors (CASSANDRA-11933)
  * Allow LWT operation on static column with only partition keys 
(CASSANDRA-10532)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/85f2bbfd/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java 
b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
index a9f65e1..f84188a 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
@@ -697,10 +697,11 @@ public abstract class ModificationStatement implements 
CQLStatement
 
         Row row = readCommand.getRow(keyspace);
         ColumnFamily current = row.cf;
+        if (current == null)
+            current = ArrayBackedSortedColumns.factory.create(metadata);
+
         if (!request.appliesTo(current))
         {
-            if (current == null)
-                current = ArrayBackedSortedColumns.factory.create(metadata);
             return current;
         }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/85f2bbfd/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 58a2874..845a732 100644
--- a/src/java/org/apache/cassandra/service/StorageProxy.java
+++ b/src/java/org/apache/cassandra/service/StorageProxy.java
@@ -233,12 +233,15 @@ public class StorageProxy implements StorageProxyMBean
                                                                   ? 
ConsistencyLevel.LOCAL_QUORUM
                                                                   : 
ConsistencyLevel.QUORUM);
                 ColumnFamily current = rows.get(0).cf;
+                if (current == null)
+                    current = 
ArrayBackedSortedColumns.factory.create(metadata);
+
                 if (!request.appliesTo(current))
                 {
                     Tracing.trace("CAS precondition does not match current 
values {}", current);
                     // We should not return null as this means success
                     casWriteMetrics.conditionNotMet.inc();
-                    return current == null ? 
ArrayBackedSortedColumns.factory.create(metadata) : current;
+                    return current;
                 }
 
                 // finish the paxos round w/ the desired updates

http://git-wip-us.apache.org/repos/asf/cassandra/blob/85f2bbfd/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java
----------------------------------------------------------------------
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 05ba09d..900cc7d 100644
--- 
a/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java
+++ 
b/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java
@@ -140,7 +140,7 @@ public class InsertUpdateIfConditionTest extends CQLTester
     @Test
     public void testConditionalDelete() throws Throwable
     {
-        createTable("CREATE TABLE %s (k int PRIMARY KEY, v1 int,)");
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, v1 int)");
 
         assertRows(execute("DELETE FROM %s WHERE k=1 IF EXISTS"), row(false));
 
@@ -981,4 +981,293 @@ public class InsertUpdateIfConditionTest extends CQLTester
         execute("DROP TYPE IF EXISTS mytype");
         assertEmpty(execute("SELECT type_name from system.schema_usertypes 
where keyspace_name = ? and type_name = ?", KEYSPACE, "mytype"));
     }
+
+    @Test
+    public void testConditionalUpdatesOnStaticColumns() throws Throwable
+    {
+        createTable("CREATE TABLE %s (a int, b int, s int static, d text, 
PRIMARY KEY (a, b))");
+
+        // pre-existing row
+        execute("INSERT INTO %s (a, b, s, d) values (6, 6, 100, 'a')");
+        assertRows(execute("UPDATE %s SET s = 6 WHERE a = 6 IF s = 100"),
+                   row(true));
+        assertRows(execute("SELECT * FROM %s WHERE a = 6"),
+                   row(6, 6, 6, "a"));
+
+        // pre-existing row with null in the static column
+        execute("INSERT INTO %s (a, b, d) values (7, 7, 'a')");
+        assertRows(execute("UPDATE %s SET s = 7 WHERE a = 7 IF s = NULL"),
+                   row(true));
+        assertRows(execute("SELECT * FROM %s WHERE a = 7"),
+                   row(7, 7, 7, "a"));
+
+        // deleting row before CAS
+        execute("DELETE FROM %s WHERE a = 8;");
+        assertRows(execute("UPDATE %s SET s = 8 WHERE a = 8 IF s = NULL"),
+                   row(true));
+        assertRows(execute("SELECT * FROM %s WHERE a = 8"),
+                   row(8, null, 8, null));
+    }
+
+    @Test
+    public void testConditionalUpdatesWithNullValues() throws Throwable
+    {
+        createTable("CREATE TABLE %s (a int, b int, s int static, d text, 
PRIMARY KEY (a, b))");
+
+        // pre-populate, leave out static column
+        for (int i = 1; i <= 5; i++)
+            execute("INSERT INTO %s (a, b) VALUES (?, ?)", i, i);
+
+        conditionalUpdatesWithNonExistingOrNullValues();
+
+        // rejected: IN doesn't contain null
+        assertRows(execute("UPDATE %s SET s = 30 WHERE a = 3 IF s IN 
(10,20,30)"),
+                   row(false));
+        assertRows(execute("SELECT * FROM %s WHERE a = 3"),
+                   row(3, 3, null, null));
+
+        // rejected: comparing number with NULL always returns false
+        for (String operator: new String[] { ">", "<", ">=", "<=", "="})
+        {
+            assertRows(execute("UPDATE %s SET s = 50 WHERE a = 5 IF s " + 
operator + " 3"),
+                       row(false));
+            assertRows(execute("SELECT * FROM %s WHERE a = 5"),
+                       row(5, 5, null, null));
+        }
+
+    }
+
+    @Test
+    public void testConditionalUpdatesWithNonExistingValues() throws Throwable
+    {
+        createTable("CREATE TABLE %s (a int, b int, s int static, d text, 
PRIMARY KEY (a, b))");
+
+        conditionalUpdatesWithNonExistingOrNullValues();
+
+        // rejected: IN doesn't contain null
+        assertRows(execute("UPDATE %s SET s = 3 WHERE a = 3 IF s IN 
(10,20,30)"),
+                   row(false));
+        assertEmpty(execute("SELECT a, s, d FROM %s WHERE a = 3"));
+
+        // rejected: comparing number with NULL always returns false
+        for (String operator : new String[]{ ">", "<", ">=", "<=", "=" })
+        {
+            assertRows(execute("UPDATE %s SET s = 50 WHERE a = 5 IF s " + 
operator + " 3"),
+                       row(false));
+            assertEmpty(execute("SELECT * FROM %s WHERE a = 5"));
+        }
+    }
+
+    private void conditionalUpdatesWithNonExistingOrNullValues() throws 
Throwable
+    {
+        assertRows(execute("UPDATE %s SET s = 1 WHERE a = 1 IF s = NULL"),
+                   row(true));
+        assertRows(execute("SELECT a, s, d FROM %s WHERE a = 1"),
+                   row(1, 1, null));
+
+        assertRows(execute("UPDATE %s SET s = 2 WHERE a = 2 IF s IN 
(10,20,NULL)"),
+                   row(true));
+        assertRows(execute("SELECT a, s, d FROM %s WHERE a = 2"),
+                   row(2, 2, null));
+
+        assertRows(execute("UPDATE %s SET s = 4 WHERE a = 4 IF s != 4"),
+                   row(true));
+        assertRows(execute("SELECT a, s, d FROM %s WHERE a = 4"),
+                   row(4, 4, null));
+    }
+
+    @Test
+    public void testConditionalUpdatesWithNullValuesWithBatch() throws 
Throwable
+    {
+        createTable("CREATE TABLE %s (a int, b int, s int static, d text, 
PRIMARY KEY (a, b))");
+
+        // pre-populate, leave out static column
+        for (int i = 1; i <= 6; i++)
+            execute("INSERT INTO %s (a, b) VALUES (?, ?)", i, i);
+
+        testConditionalUpdatesWithNonExistingOrNullValuesWithBatch();
+
+        // rejected: comparing number with null value always returns false
+        for (String operator: new String[] { ">", "<", ">=", "<=", "="})
+        {
+            assertRows(execute("BEGIN BATCH\n"
+                               + "INSERT INTO %1$s (a, b, s, d) values (3, 3, 
40, 'a');\n"
+                               + "UPDATE %1$s SET s = 30 WHERE a = 3 IF s " + 
operator + " 5;\n"
+                               + "APPLY BATCH"),
+                       row(false));
+            assertRows(execute("SELECT * FROM %s WHERE a = 3"),
+                       row(3, 3, null, null));
+        }
+
+        // rejected: IN doesn't contain null
+        assertRows(execute("BEGIN BATCH\n"
+                           + "INSERT INTO %1$s (a, b, s, d) values (6, 6, 70, 
'a');\n"
+                           + "UPDATE %1$s SET s = 60 WHERE a = 6 IF s IN 
(1,2,3);\n"
+                           + "APPLY BATCH"),
+                   row(false));
+        assertRows(execute("SELECT * FROM %s WHERE a = 6"),
+                   row(6, 6, null, null));
+
+    }
+
+    @Test
+    public void testConditionalUpdatesWithNonExistingValuesWithBatch() throws 
Throwable
+    {
+        createTable("CREATE TABLE %s (a int, b int, s int static, d text, 
PRIMARY KEY (a, b))");
+
+        testConditionalUpdatesWithNonExistingOrNullValuesWithBatch();
+
+        // rejected: comparing number with non-existing value always returns 
false
+        for (String operator: new String[] { ">", "<", ">=", "<=", "="})
+        {
+            assertRows(execute("BEGIN BATCH\n"
+                               + "INSERT INTO %1$s (a, b, s, d) values (3, 3, 
3, 'a');\n"
+                               + "UPDATE %1$s SET s = 3 WHERE a = 3 IF s " + 
operator + " 5;\n"
+                               + "APPLY BATCH"),
+                       row(false));
+            assertEmpty(execute("SELECT * FROM %s WHERE a = 3"));
+        }
+
+        // rejected: IN doesn't contain null
+        assertRows(execute("BEGIN BATCH\n"
+                           + "INSERT INTO %1$s (a, b, s, d) values (6, 6, 6, 
'a');\n"
+                           + "UPDATE %1$s SET s = 7 WHERE a = 6 IF s IN 
(1,2,3);\n"
+                           + "APPLY BATCH"),
+                   row(false));
+        assertEmpty(execute("SELECT * FROM %s WHERE a = 6"));
+    }
+
+    private void testConditionalUpdatesWithNonExistingOrNullValuesWithBatch() 
throws Throwable
+    {
+        // applied: null is indistiguishable from empty value, lwt condition 
is executed before INSERT
+        assertRows(execute("BEGIN BATCH\n"
+                           + "INSERT INTO %1$s (a, b, d) values (2, 2, 'a');\n"
+                           + "UPDATE %1$s SET s = 2 WHERE a = 2 IF s = null;\n"
+                           + "APPLY BATCH"),
+                   row(true));
+        assertRows(execute("SELECT * FROM %s WHERE a = 2"),
+                   row(2, 2, 2, "a"));
+
+        // applied: lwt condition is executed before INSERT, update is applied 
after it
+        assertRows(execute("BEGIN BATCH\n"
+                           + "INSERT INTO %1$s (a, b, s, d) values (4, 4, 4, 
'a');\n"
+                           + "UPDATE %1$s SET s = 5 WHERE a = 4 IF s = null;\n"
+                           + "APPLY BATCH"),
+                   row(true));
+        assertRows(execute("SELECT * FROM %s WHERE a = 4"),
+                   row(4, 4, 5, "a"));
+
+        assertRows(execute("BEGIN BATCH\n"
+                           + "INSERT INTO %1$s (a, b, s, d) values (5, 5, 5, 
'a');\n"
+                           + "UPDATE %1$s SET s = 6 WHERE a = 5 IF s IN 
(1,2,null);\n"
+                           + "APPLY BATCH"),
+                   row(true));
+        assertRows(execute("SELECT * FROM %s WHERE a = 5"),
+                   row(5, 5, 6, "a"));
+
+        assertRows(execute("BEGIN BATCH\n"
+                           + "INSERT INTO %1$s (a, b, s, d) values (7, 7, 7, 
'a');\n"
+                           + "UPDATE %1$s SET s = 8 WHERE a = 7 IF s != 7;\n"
+                           + "APPLY BATCH"),
+                   row(true));
+        assertRows(execute("SELECT * FROM %s WHERE a = 7"),
+                   row(7, 7, 8, "a"));
+    }
+
+    @Test
+    public void testConditionalDeleteWithNullValues() throws Throwable
+    {
+        createTable("CREATE TABLE %s (a int, b int, s1 int static, s2 int 
static, v int, PRIMARY KEY (a, b))");
+
+        for (int i = 1; i <= 5; i++)
+            execute("INSERT INTO %s (a, b, s1, s2, v) VALUES (?, ?, ?, ?, ?)", 
i, i, i, null, i);
+
+        assertRows(execute("DELETE s1 FROM %s WHERE a = 1 IF s2 = NULL"),
+                   row(true));
+        assertRows(execute("SELECT * FROM %s WHERE a = 1"),
+                   row(1, 1, null, null, 1));
+
+        // rejected: IN doesn't contain null
+        assertRows(execute("DELETE s1 FROM %s WHERE a = 2 IF s2 IN 
(10,20,30)"),
+                   row(false, null));
+        assertRows(execute("SELECT * FROM %s WHERE a = 2"),
+                   row(2, 2, 2, null, 2));
+
+        assertRows(execute("DELETE s1 FROM %s WHERE a = 3 IF s2 IN 
(NULL,20,30)"),
+                   row(true));
+        assertRows(execute("SELECT * FROM %s WHERE a = 3"),
+                   row(3, 3, null, null, 3));
+
+        assertRows(execute("DELETE s1 FROM %s WHERE a = 4 IF s2 != 4"),
+                   row(true));
+        assertRows(execute("SELECT * FROM %s WHERE a = 4"),
+                   row(4, 4, null, null, 4));
+
+        // rejected: comparing number with NULL always returns false
+        for (String operator : new String[]{ ">", "<", ">=", "<=", "=" })
+        {
+            assertRows(execute("DELETE s1 FROM %s WHERE a = 5 IF s2 " + 
operator + " 3"),
+                       row(false, null));
+            assertRows(execute("SELECT * FROM %s WHERE a = 5"),
+                       row(5, 5, 5, null, 5));
+        }
+    }
+
+    @Test
+    public void testConditionalDeletesWithNonExistingValuesWithBatch() throws 
Throwable
+    {
+        createTable("CREATE TABLE %s (a int, b int, s1 int static, s2 int 
static, v int, PRIMARY KEY (a, b))");
+
+        // applied: null is indistiguishable from empty value, lwt condition 
is executed before INSERT
+        assertRows(execute("BEGIN BATCH\n"
+                           + "INSERT INTO %1$s (a, b, s1, v) values (2, 2, 2, 
2);\n"
+                           + "DELETE s1 FROM %1$s WHERE a = 2 IF s2 = null;\n"
+                           + "APPLY BATCH"),
+                   row(true));
+        assertRows(execute("SELECT * FROM %s WHERE a = 2"),
+                   row(2, 2, null, null, 2));
+
+        // rejected: comparing number with non-existing value always returns 
false
+        for (String operator: new String[] { ">", "<", ">=", "<=", "="})
+        {
+            assertRows(execute("BEGIN BATCH\n"
+                               + "INSERT INTO %1$s (a, b, s1, v) values (3, 3, 
3, 3);\n"
+                               + "DELETE s1 FROM %1$s WHERE a = 3 IF s2 " + 
operator + " 5;\n"
+                               + "APPLY BATCH"),
+                       row(false));
+            assertEmpty(execute("SELECT * FROM %s WHERE a = 3"));
+        }
+
+        // rejected: IN doesn't contain null
+        assertRows(execute("BEGIN BATCH\n"
+                           + "INSERT INTO %1$s (a, b, s1, v) values (6, 6, 6, 
6);\n"
+                           + "DELETE s1 FROM %1$s WHERE a = 6 IF s2 IN 
(1,2,3);\n"
+                           + "APPLY BATCH"),
+                   row(false));
+        assertEmpty(execute("SELECT * FROM %s WHERE a = 6"));
+
+        assertRows(execute("BEGIN BATCH\n"
+                           + "INSERT INTO %1$s (a, b, s1, v) values (4, 4, 4, 
4);\n"
+                           + "DELETE s1 FROM %1$s WHERE a = 4 IF s2 = null;\n"
+                           + "APPLY BATCH"),
+                   row(true));
+        assertRows(execute("SELECT * FROM %s WHERE a = 4"),
+                   row(4, 4, null, null, 4));
+
+        assertRows(execute("BEGIN BATCH\n"
+                           + "INSERT INTO %1$s (a, b, s1, v) VALUES (5, 5, 5, 
5);\n"
+                           + "DELETE s1 FROM %1$s WHERE a = 5 IF s1 IN 
(1,2,null);\n"
+                           + "APPLY BATCH"),
+                   row(true));
+        assertRows(execute("SELECT * FROM %s WHERE a = 5"),
+                   row(5, 5, null, null, 5));
+
+        assertRows(execute("BEGIN BATCH\n"
+                           + "INSERT INTO %1$s (a, b, s1, v) values (7, 7, 7, 
7);\n"
+                           + "DELETE s1 FROM %1$s WHERE a = 7 IF s2 != 7;\n"
+                           + "APPLY BATCH"),
+                   row(true));
+        assertRows(execute("SELECT * FROM %s WHERE a = 7"),
+                   row(7, 7, null, null, 7));
+    }
 }

Reply via email to