content: /*
   * 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.
   */
  
  package org.apache.cassandra.cql3.validation.operations;
  
  import java.util.Collection;
  
  import org.junit.AfterClass;
  import org.junit.Before;
  import org.junit.BeforeClass;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  import org.junit.runners.Parameterized;
  
  import org.apache.cassandra.ServerTestUtils;
  import org.apache.cassandra.cql3.CQLTester;
  
  /* InsertUpdateIfConditionCollectionsTest class has been split into multiple ones because of timeout issues (CASSANDRA-16670)
   * Any changes here check if they apply to the other classes
   * - InsertUpdateIfConditionStaticsTest
   * - InsertUpdateIfConditionCollectionsTest
   * - InsertUpdateIfConditionTest
   */
  @RunWith(Parameterized.class)
  public class InsertUpdateIfConditionStaticsTest extends CQLTester
  {
      @Parameterized.Parameters(name = "{index}: clusterMinVersion={0}")
      public static Collection<Object[]> data()
      {
          ServerTestUtils.daemonInitialization();
          return InsertUpdateIfConditionTest.data();
      }
  
      @Parameterized.Parameter(0)
      public String clusterMinVersion;
  
      @BeforeClass
      public static void beforeClass()
      {
          InsertUpdateIfConditionTest.beforeClass();
      }
  
      @Before
      public void before()
      {
          InsertUpdateIfConditionTest.beforeSetup(clusterMinVersion);
      }
  
      @AfterClass
      public static void afterClass()
      {
          InsertUpdateIfConditionTest.afterClass();
      }
  
      /**
       * Migrated from cql_tests.py:TestCQL.static_columns_cas_test()
       */
      @Test
      public void testStaticColumnsCas() throws Throwable
      {
          createTable("CREATE TABLE %s (id int, k text, version int static, v text, PRIMARY KEY (id, k))");
  
          // Test that INSERT IF NOT EXISTS concerns only the static column if no clustering nor regular columns
          // is provided, but concerns the CQL3 row targetted by the clustering columns otherwise
          execute("INSERT INTO %s (id, k, v) VALUES (1, 'foo', 'foo')");
          assertRows(execute("INSERT INTO %s (id, k, version) VALUES (1, 'foo', 1) IF NOT EXISTS"), row(false, 1, "foo", null, "foo"));
          assertRows(execute("INSERT INTO %s (id, version) VALUES (1, 1) IF NOT EXISTS"), row(true));
          assertRows(execute("SELECT * FROM %s"), row(1, "foo", 1, "foo"));
          execute("DELETE FROM %s WHERE id = 1");
  
          execute("INSERT INTO %s(id, version) VALUES (0, 0)");
  
          assertRows(execute("UPDATE %s SET v='foo', version=1 WHERE id=0 AND k='k1' IF version = ?", 0), row(true));
          assertRows(execute("SELECT * FROM %s"), row(0, "k1", 1, "foo"));
  
          assertRows(execute("UPDATE %s SET v='bar', version=1 WHERE id=0 AND k='k2' IF version = ?", 0), row(false, 1));
          assertRows(execute("SELECT * FROM %s"), row(0, "k1", 1, "foo"));
  
          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, "k1", 2));
  
          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 = 2; " +
                             "APPLY BATCH "),
                     row(true));
  
          assertRows(execute("SELECT * FROM %s"),
                     row(0, "k1", 3, "foobar"),
                     row(0, "k2", 3, "barfoo"));
  
          assertRows(execute("BEGIN BATCH " +
                             "UPDATE %1$s SET version = 4 WHERE id = 0 IF version = 3; " +
                             "UPDATE %1$s SET v='row1' WHERE id=0 AND k='k1' IF v='foo'; " +
                             "UPDATE %1$s SET v='row2' WHERE id=0 AND k='k2' IF v='bar'; " +
                             "APPLY BATCH "),
                     row(false, 0, "k1", 3, "foobar"),
                     row(false, 0, "k2", 3, "barfoo"));
  
          assertRows(execute("BEGIN BATCH " +
                             "UPDATE %1$s SET version = 4 WHERE id = 0 IF version = 3; " +
                             "UPDATE %1$s SET v='row1' WHERE id = 0 AND k='k1' IF v='foobar'; " +
                             "UPDATE %1$s SET v='row2' WHERE id = 0 AND k='k2' IF v='barfoo'; " +
                             "APPLY BATCH "),
                     row(true));
  
          assertRows(execute("SELECT * FROM %s"),
                     row(0, "k1", 4, "row1"),
                     row(0, "k2", 4, "row2"));
  
          assertInvalid("BEGIN BATCH " +
                        "UPDATE %1$s SET version=5 WHERE id=0 IF version=4; " +
                        "UPDATE %1$s SET v='row1' WHERE id=0 AND k='k1'; " +
                        "UPDATE %1$s SET v='row2' WHERE id=1 AND k='k2'; " +
                        "APPLY BATCH ");
  
          assertRows(execute("BEGIN BATCH " +
                             "INSERT INTO %1$s (id, k, v) VALUES (1, 'k1', 'val1') IF NOT EXISTS; " +
                             "INSERT INTO %1$s (id, k, v) VALUES (1, 'k2', 'val2') IF NOT EXISTS; " +
                             "APPLY BATCH "),
                     row(true));
  
          assertRows(execute("SELECT * FROM %s WHERE id=1"),
                     row(1, "k1", null, "val1"),
                     row(1, "k2", null, "val2"));
  
          assertRows(execute("INSERT INTO %s (id, k, v) VALUES (1, 'k2', 'val2') IF NOT EXISTS"), row(false, 1, "k2", null, "val2"));
  
          assertRows(execute("BEGIN BATCH " +
                             "INSERT INTO %1$s (id, k, v) VALUES (1, 'k2', 'val2') IF NOT EXISTS; " +
                             "INSERT INTO %1$s (id, k, v) VALUES (1, 'k3', 'val3') IF NOT EXISTS; " +
                             "APPLY BATCH"),
                     row(false, 1, "k2", null, "val2"));
  
          assertRows(execute("BEGIN BATCH " +
                             "UPDATE %1$s SET v = 'newVal' WHERE id = 1 AND k = 'k2' IF v = 'val0'; " +
                             "INSERT INTO %1$s (id, k, v) VALUES (1, 'k3', 'val3') IF NOT EXISTS; " +
                             "APPLY BATCH"),
                     row(false, 1, "k2", null, "val2"));
  
          assertRows(execute("SELECT * FROM %s WHERE id=1"),
                     row(1, "k1", null, "val1"),
                     row(1, "k2", null, "val2"));
  
          assertRows(execute("BEGIN BATCH " +
                             "UPDATE %1$s SET v = 'newVal' WHERE id = 1 AND k = 'k2' IF v = 'val2'; " +
                             "INSERT INTO %1$s (id, k, v, version) VALUES(1, 'k3', 'val3', 1) IF NOT EXISTS; " +
                             "APPLY BATCH"),
                     row(true));
  
          assertRows(execute("SELECT * FROM %s WHERE id=1"),
                     row(1, "k1", 1, "val1"),
                     row(1, "k2", 1, "newVal"),
                     row(1, "k3", 1, "val3"));
  
          assertRows(execute("BEGIN BATCH " +
                             "UPDATE %1$s SET v = 'newVal1' WHERE id = 1 AND k = 'k2' IF v = 'val2'; " +
                             "UPDATE %1$s SET v = 'newVal2' WHERE id = 1 AND k = 'k2' IF v = 'val3'; " +
                             "APPLY BATCH"),
                     row(false, 1, "k2", "newVal"));
      }
  
      /**
       * Test CASSANDRA-10532
       */
      @Test
      public void testStaticColumnsCasDelete() throws Throwable
      {
          createTable("CREATE TABLE %s (pk int, ck int, static_col int static, value int, PRIMARY KEY (pk, ck))");
          execute("INSERT INTO %s (pk, ck, value) VALUES (?, ?, ?)", 1, 1, 2);
          execute("INSERT INTO %s (pk, ck, value) VALUES (?, ?, ?)", 1, 3, 4);
          execute("INSERT INTO %s (pk, ck, value) VALUES (?, ?, ?)", 1, 5, 6);
          execute("INSERT INTO %s (pk, ck, value) VALUES (?, ?, ?)", 1, 7, 8);
          execute("INSERT INTO %s (pk, ck, value) VALUES (?, ?, ?)", 2, 1, 2);
          execute("INSERT INTO %s (pk, static_col) VALUES (?, ?)", 1, 1);
  
          assertRows(execute("DELETE static_col FROM %s WHERE pk = ? IF static_col = ?", 1, 2), row(false, 1));
          assertRows(execute("DELETE static_col FROM %s WHERE pk = ? IF static_col = ?", 1, 1), row(true));
  
          assertRows(execute("SELECT pk, ck, static_col, value FROM %s WHERE pk = 1"),
                     row(1, 1, null, 2),
                     row(1, 3, null, 4),
                     row(1, 5, null, 6),
                     row(1, 7, null, 8));
          execute("INSERT INTO %s (pk, static_col) VALUES (?, ?)", 1, 1);
  
          assertInvalidMessage("Some partition key parts are missing: pk",
                               "DELETE static_col FROM %s WHERE ck = ? IF static_col = ?", 1, 1);
  
          assertInvalidMessage("Invalid restrictions on clustering columns since the DELETE statement modifies only static columns",
                               "DELETE static_col FROM %s WHERE pk = ? AND ck = ? IF static_col = ?", 1, 1, 1);
  
          assertInvalidMessage("DELETE statements must restrict all PRIMARY KEY columns with equality relations in order to delete non static columns",
                               "DELETE static_col, value FROM %s WHERE pk = ? IF static_col = ?", 1, 1);
  
          // Same query but with an invalid condition
          assertInvalidMessage("DELETE statements must restrict all PRIMARY KEY columns with equality relations in order to delete non static columns",
                               "DELETE static_col, value FROM %s WHERE pk = ? IF static_col = ?", 1, 2);
  
          // DELETE of an underspecified PRIMARY KEY should not succeed if static is not only restriction
          assertInvalidMessage("DELETE statements must restrict all PRIMARY KEY columns with equality relations" +
                               " in order to use IF condition on non static columns",
                               "DELETE static_col FROM %s WHERE pk = ? IF value = ? AND static_col = ?", 1, 2, 1);
  
          assertRows(execute("DELETE value FROM %s WHERE pk = ? AND ck = ? IF value = ? AND static_col = ?", 1, 1, 2, 2), row(false, 2, 1));
          assertRows(execute("DELETE value FROM %s WHERE pk = ? AND ck = ? IF value = ? AND static_col = ?", 1, 1, 2, 1), row(true));
          assertRows(execute("SELECT pk, ck, static_col, value FROM %s WHERE pk = 1"),
                     row(1, 1, 1, null),
                     row(1, 3, 1, 4),
                     row(1, 5, 1, 6),
                     row(1, 7, 1, 8));
  
          assertRows(execute("DELETE static_col FROM %s WHERE pk = ? AND ck = ? IF value = ?", 1, 5, 10), row(false, 6));
          assertRows(execute("DELETE static_col FROM %s WHERE pk = ? AND ck = ? IF value = ?", 1, 5, 6), row(true));
          assertRows(execute("SELECT pk, ck, static_col, value FROM %s WHERE pk = 1"),
                     row(1, 1, null, null),
                     row(1, 3, null, 4),
                     row(1, 5, null, 6),
                     row(1, 7, null, 8));
      }
  
      @Test
      public void testStaticColumnsCasUpdate() throws Throwable
      {
          createTable("CREATE TABLE %s (pk int, ck int, static_col int static, value int, PRIMARY KEY (pk, ck))");
          execute("INSERT INTO %s (pk, ck, value) VALUES (?, ?, ?)", 1, 1, 2);
          execute("INSERT INTO %s (pk, ck, value) VALUES (?, ?, ?)", 1, 3, 4);
          execute("INSERT INTO %s (pk, ck, value) VALUES (?, ?, ?)", 1, 5, 6);
          execute("INSERT INTO %s (pk, ck, value) VALUES (?, ?, ?)", 1, 7, 8);
          execute("INSERT INTO %s (pk, ck, value) VALUES (?, ?, ?)", 2, 1, 2);
          execute("INSERT INTO %s (pk, static_col) VALUES (?, ?)", 1, 1);
  
          assertRows(execute("UPDATE %s SET static_col = ? WHERE pk = ? IF static_col = ?", 3, 1, 2), row(false, 1));
          assertRows(execute("UPDATE %s SET static_col = ? WHERE pk = ? IF static_col = ?", 2, 1, 1), row(true));
  
          assertRows(execute("SELECT pk, ck, static_col, value FROM %s WHERE pk = 1"),
                     row(1, 1, 2, 2),
                     row(1, 3, 2, 4),
                     row(1, 5, 2, 6),
                     row(1, 7, 2, 8));
  
          assertInvalidMessage("Some partition key parts are missing: pk",
                               "UPDATE %s SET static_col = ? WHERE ck = ? IF static_col = ?", 3, 1, 1);
  
          assertInvalidMessage("Invalid restrictions on clustering columns since the UPDATE statement modifies only static columns",
                               "UPDATE %s SET static_col = ? WHERE pk = ? AND ck = ? IF static_col = ?", 3, 1, 1, 1);
  
          assertInvalidMessage("Some clustering keys are missing: ck",
                               "UPDATE %s SET static_col = ?, value = ? WHERE pk = ? IF static_col = ?", 3, 1, 1, 2);
  
          // Same query but with an invalid condition
          assertInvalidMessage("Some clustering keys are missing: ck",
                               "UPDATE %s SET static_col = ?, value = ? WHERE pk = ? IF static_col = ?", 3, 1, 1, 1);
  
          assertInvalidMessage("Some clustering keys are missing: ck",
                               "UPDATE %s SET static_col = ? WHERE pk = ? IF value = ? AND static_col = ?", 3, 1, 4, 2);
  
          assertRows(execute("UPDATE %s SET value = ? WHERE pk = ? AND ck = ? IF value = ? AND static_col = ?", 3, 1, 1, 3, 2), row(false, 2, 2));
          assertRows(execute("UPDATE %s SET value = ? WHERE pk = ? AND ck = ? IF value = ? AND static_col = ?", 1, 1, 1, 2, 2), row(true));
          assertRows(execute("SELECT pk, ck, static_col, value FROM %s WHERE pk = 1"),
                     row(1, 1, 2, 1),
                     row(1, 3, 2, 4),
                     row(1, 5, 2, 6),
                     row(1, 7, 2, 8));
  
          assertRows(execute("UPDATE %s SET static_col = ? WHERE pk = ? AND ck = ? IF value = ?", 3, 1, 1, 2), row(false, 1));
          assertRows(execute("UPDATE %s SET static_col = ? WHERE pk = ? AND ck = ? IF value = ?", 1, 1, 1, 1), row(true));
          assertRows(execute("SELECT pk, ck, static_col, value FROM %s WHERE pk = 1"),
                     row(1, 1, 1, 1),
                     row(1, 3, 1, 4),
                     row(1, 5, 1, 6),
                     row(1, 7, 1, 8));
      }
  
      @Test
      public void testConditionalUpdatesOnStaticColumns() throws Throwable
      {
          createTable("CREATE TABLE %s (a int, b int, s int static, d text, PRIMARY KEY (a, b))");
  
          assertInvalidMessage("Invalid 'unset' value in condition", "UPDATE %s SET s = 6 WHERE a = 6 IF s = ?", unset());
  
          // 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"));
  
          execute("INSERT INTO %s (a, b, s, d) values (7, 7, 100, 'a')");
          assertRows(execute("UPDATE %s SET s = 7 WHERE a = 7 IF s = 101"),
                     row(false, 100));
          assertRows(execute("SELECT * FROM %s WHERE a = 7"),
                     row(7, 7, 100, "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(false, 100));
          assertRows(execute("SELECT * FROM %s WHERE a = 7"),
                     row(7, 7, 100, "a"));
  
          // deleting row before CAS makes it effectively non-existing
          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 testStaticsWithMultipleConditions() throws Throwable
      {
          createTable("CREATE TABLE %s (a int, b int, s1 int static, s2 int static, d int, PRIMARY KEY (a, b))");
  
          for (int i = 1; i <= 5; i++)
          {
              execute("INSERT INTO %s (a, b, d) VALUES (?, ?, ?)", i, 1, 5);
              execute("INSERT INTO %s (a, b, d) VALUES (?, ?, ?)", i, 2, 6);
          }
  
          assertRows(execute("BEGIN BATCH\n"
                             + "UPDATE %1$s SET s2 = 102 WHERE a = 1 IF s1 = null;\n"
                             + "UPDATE %1$s SET s1 = 101 WHERE a = 1 IF s2 = null;\n"
                             + "APPLY BATCH"),
                     row(true));
          assertRows(execute("SELECT * FROM %s WHERE a = 1"),
                     row(1, 1, 101, 102, 5),
                     row(1, 2, 101, 102, 6));
  
          assertRows(execute("BEGIN BATCH\n"
                             + "UPDATE %1$s SET s2 = 202 WHERE a = 2 IF s1 = null;\n"
                             + "UPDATE %1$s SET s1 = 201 WHERE a = 2 IF s2 = null;\n"
                             + "UPDATE %1$s SET d = 203 WHERE a = 2 AND b = 1 IF d = 5;\n"
                             + "UPDATE %1$s SET d = 204 WHERE a = 2 AND b = 2 IF d = 6;\n"
                             + "APPLY BATCH"),
                     row(true));
  
          assertRows(execute("SELECT * FROM %s WHERE a = 2"),
                     row(2, 1, 201, 202, 203),
                     row(2, 2, 201, 202, 204));
  
          assertRows(execute("BEGIN BATCH\n"
                             + "UPDATE %1$s SET s2 = 202 WHERE a = 20 IF s1 = null;\n"
                             + "UPDATE %1$s SET s1 = 201 WHERE a = 20 IF s2 = null;\n"
                             + "UPDATE %1$s SET d = 203 WHERE a = 20 AND b = 1 IF d = 5;\n"
                             + "UPDATE %1$s SET d = 204 WHERE a = 20 AND b = 2 IF d = 6;\n"
                             + "APPLY BATCH"),
                     row(false));
      }
  
      @Test
      public void testStaticColumnsCasUpdateWithNullStaticColumn() throws Throwable
      {
          createTable("CREATE TABLE %s (pk int, ck int, s1 int static, s2 int static, value int, PRIMARY KEY (pk, ck))");
          execute("INSERT INTO %s (pk, s1, s2) VALUES (1, 1, 1) USING TIMESTAMP 1000");
          execute("INSERT INTO %s (pk, s1, s2) VALUES (2, 1, 1) USING TIMESTAMP 1001");
          flush();
          execute("INSERT INTO %s (pk, s1) VALUES (1, 2) USING TIMESTAMP 2000");
          execute("INSERT INTO %s (pk, s1) VALUES (2, 2) USING TIMESTAMP 2001");
          flush();
          execute("DELETE s1 FROM %s USING TIMESTAMP 3000 WHERE pk = 1");
          execute("DELETE s1 FROM %s USING TIMESTAMP 3001 WHERE pk = 2");
          flush();
  
          assertRows(execute("UPDATE %s SET s1 = ? WHERE pk = ? IF s1 = NULL", 2, 1), row(true));
          assertRows(execute("SELECT * FROM %s WHERE pk = ?", 1), row(1, null, 2, 1, null));
          assertRows(execute("UPDATE %s SET s1 = ? WHERE pk = ? IF EXISTS", 2, 2), row(true));
          assertRows(execute("SELECT * FROM %s WHERE pk = ?", 2), row(2, null, 2, 1, null));
      }
  
      @Test
      public void testStaticColumnsCasDeleteWithNullStaticColumn() throws Throwable
      {
          createTable("CREATE TABLE %s (pk int, ck int, s1 int static, s2 int static, value int, PRIMARY KEY (pk, ck))");
          execute("INSERT INTO %s (pk, s1, s2) VALUES (1, 1, 1) USING TIMESTAMP 1000");
          execute("INSERT INTO %s (pk, s1, s2) VALUES (2, 1, 1) USING TIMESTAMP 1001");
          flush();
          execute("INSERT INTO %s (pk, s1) VALUES (1, 2) USING TIMESTAMP 2000");
          execute("INSERT INTO %s (pk, s1) VALUES (2, 2) USING TIMESTAMP 2001");
          flush();
          execute("DELETE s1 FROM %s USING TIMESTAMP 3000 WHERE pk = 1");
          execute("DELETE s1 FROM %s USING TIMESTAMP 3001 WHERE pk = 2");
          flush();
  
          assertRows(execute("DELETE s2 FROM %s WHERE pk = ? IF s1 = NULL", 1), row(true));
          assertRows(execute("SELECT * FROM %s WHERE pk = ?", 1));
          assertRows(execute("DELETE s2 FROM %s WHERE pk = ? IF EXISTS", 2), row(true));
          assertRows(execute("SELECT * FROM %s WHERE pk = ?", 2));
      }
  }
  