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.entities;
  
  import java.util.Arrays;
  
  import org.junit.Test;
  
  import org.apache.cassandra.cql3.CQLTester;
  
  import static org.junit.Assert.assertNull;
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertTrue;
  
  public class StaticColumnsTest extends CQLTester
  {
  
      /**
       * Migrated from cql_tests.py:TestCQL.static_columns_test()
       */
      @Test
      public void testStaticColumns() throws Throwable
      {
          testStaticColumns(false);
          testStaticColumns(true);
      }
  
      private void testStaticColumns(boolean forceFlush) throws Throwable
      {
          createTable("CREATE TABLE %s ( k int, p int, s int static, v int, PRIMARY KEY (k, p))");
  
          execute("INSERT INTO %s(k, s) VALUES (0, 42)");
          flush(forceFlush);
  
          assertRows(execute("SELECT * FROM %s"), row(0, null, 42, null));
  
          // Check that writetime works (//7081) -- we can't predict the exact value easily so
          // we just check that it's non zero
          Object[][] row = getRows(execute("SELECT s, writetime(s) FROM %s WHERE k=0"));
          assertEquals(42, row[0][0]);
          assertTrue((Long)row[0][1] > 0);
  
          execute("INSERT INTO %s (k, p, s, v) VALUES (0, 0, 12, 0)");
          execute("INSERT INTO %s (k, p, s, v) VALUES (0, 1, 24, 1)");
          flush(forceFlush);
  
          // Check the static columns in indeed "static"
          assertRows(execute("SELECT * FROM %s"), row(0, 0, 24, 0), row(0, 1, 24, 1));
  
          // Check we do correctly get the static column value with a SELECT *, even
          // if we're only slicing part of the partition
          assertRows(execute("SELECT * FROM %s WHERE k=0 AND p=0"), row(0, 0, 24, 0));
          assertRows(execute("SELECT * FROM %s WHERE k=0 AND p=1"), row(0, 1, 24, 1));
  
          // Test for IN on the clustering key (//6769)
          assertRows(execute("SELECT * FROM %s WHERE k=0 AND p IN (0, 1)"), row(0, 0, 24, 0), row(0, 1, 24, 1));
  
          // Check things still work if we don't select the static column. We also want
          // this to not request the static columns internally at all, though that part
          // require debugging to assert
          assertRows(execute("SELECT p, v FROM %s WHERE k=0 AND p=1"), row(1, 1));
  
          // Check selecting only a static column with distinct only yield one value
          // (as we only query the static columns)
          assertRows(execute("SELECT DISTINCT s FROM %s WHERE k=0"), row(24));
          // But without DISTINCT, we still get one result per row
          assertRows(execute("SELECT s FROM %s WHERE k=0"),row(24),row(24));
          // but that querying other columns does correctly yield the full partition
          assertRows(execute("SELECT s, v FROM %s WHERE k=0"),row(24, 0),row(24, 1));
          assertRows(execute("SELECT s, v FROM %s WHERE k=0 AND p=1"),row(24, 1));
          assertRows(execute("SELECT p, s FROM %s WHERE k=0 AND p=1"), row(1, 24));
          assertRows(execute("SELECT k, p, s FROM %s WHERE k=0 AND p=1"),row(0, 1, 24));
  
          // Check that deleting a row don't implicitely deletes statics
          execute("DELETE FROM %s WHERE k=0 AND p=0");
          flush(forceFlush);
          assertRows(execute("SELECT * FROM %s"),row(0, 1, 24, 1));
  
          // But that explicitely deleting the static column does remove it
          execute("DELETE s FROM %s WHERE k=0");
          flush(forceFlush);
          assertRows(execute("SELECT * FROM %s"), row(0, 1, null, 1));
  
          // Check we can add a static column ...
          execute("ALTER TABLE %s ADD s2 int static");
          assertRows(execute("SELECT * FROM %s"), row(0, 1, null, null, 1));
          execute("INSERT INTO %s (k, p, s2, v) VALUES(0, 2, 42, 2)");
          assertRows(execute("SELECT * FROM %s"), row(0, 1, null, 42, 1), row(0, 2, null, 42, 2));
          // ... and that we can drop it
          execute("ALTER TABLE %s DROP s2");
          assertRows(execute("SELECT * FROM %s"), row(0, 1, null, 1), row(0, 2, null, 2));
      }
  
      /**
       * Migrated from cql_tests.py:TestCQL.static_columns_with_2i_test()
       */
      @Test
      public void testStaticColumnsWithSecondaryIndex() throws Throwable
      {
          createTable(" CREATE TABLE %s (k int, p int, s int static, v int, PRIMARY KEY (k, p) ) ");
  
          createIndex("CREATE INDEX ON %s (v)");
  
          execute("INSERT INTO %s (k, p, s, v) VALUES (0, 0, 42, 1)");
          execute("INSERT INTO %s (k, p, v) VALUES (0, 1, 1)");
          execute("INSERT INTO %s (k, p, v) VALUES (0, 2, 2)");
  
          assertRows(execute("SELECT * FROM %s WHERE v = 1"), row(0, 0, 42, 1), row(0, 1, 42, 1));
          assertRows(execute("SELECT p, s FROM %s WHERE v = 1"), row(0, 42), row(1, 42));
          assertRows(execute("SELECT p FROM %s WHERE v = 1"), row(0), row(1));
          assertRows(execute("SELECT s FROM %s WHERE v = 1"), row(42), row(42));
      }
  
      /**
       * Migrated from cql_tests.py:TestCQL.static_columns_with_distinct_test()
       */
      @Test
      public void testStaticColumnsWithDistinct() throws Throwable
      {
          createTable("CREATE TABLE %s( k int, p int, s int static, PRIMARY KEY (k, p) ) ");
  
          execute("INSERT INTO %s (k, p) VALUES (1, 1)");
          execute("INSERT INTO %s (k, p) VALUES (1, 2)");
  
          assertRows(execute("SELECT k, s FROM %s"), row(1, null), row(1, null));
          assertRows(execute("SELECT DISTINCT k, s FROM %s"), row(1, null));
  
          Object[][] rows = getRows(execute("SELECT DISTINCT s FROM %s WHERE k=1"));
          assertNull(rows[0][0]);
  
          assertEmpty(execute("SELECT DISTINCT s FROM %s WHERE k=2"));
  
          execute("INSERT INTO %s (k, p, s) VALUES (2, 1, 3)");
          execute("INSERT INTO %s (k, p) VALUES (2, 2)");
  
          assertRows(execute("SELECT k, s FROM %s"), row(1, null), row(1, null), row(2, 3), row(2, 3));
          assertRows(execute("SELECT DISTINCT k, s FROM %s"), row(1, null), row(2, 3));
          rows = getRows(execute("SELECT DISTINCT s FROM %s WHERE k=1"));
          assertNull(rows[0][0]);
          assertRows(execute("SELECT DISTINCT s FROM %s WHERE k=2"), row(3));
  
          assertInvalid("SELECT DISTINCT s FROM %s");
  
          // paging to test for CASSANDRA-8108
          execute("TRUNCATE %s");
          for (int i = 0; i < 10; i++)
              for (int j = 0; j < 10; j++)
                  execute("INSERT INTO %s (k, p, s) VALUES (?, ?, ?)", i, j, i);
  
          rows = getRows(execute("SELECT DISTINCT k, s FROM %s"));
          checkDistinctRows(rows, true, 0, 10, 0, 10);
  
          String keys = "0, 1, 2, 3, 4, 5, 6, 7, 8, 9";
          rows = getRows(execute("SELECT DISTINCT k, s FROM %s WHERE k IN (" + keys + ")"));
          checkDistinctRows(rows, false, 0, 10, 0, 10);
  
          // additional testing for CASSANRA-8087
          createTable("CREATE TABLE %s( k int, c1 int, c2 int, s1 int static, s2 int static, PRIMARY KEY (k, c1, c2))");
  
          for (int i = 0; i < 10; i++)
              for (int j = 0; j < 5; j++)
                  for (int k = 0; k < 5; k++)
                      execute("INSERT INTO %s (k, c1, c2, s1, s2) VALUES (?, ?, ?, ?, ?)", i, j, k, i, i + 1);
  
          rows = getRows(execute("SELECT DISTINCT k, s1 FROM %s"));
          checkDistinctRows(rows, true, 0, 10, 0, 10);
  
          rows = getRows(execute("SELECT DISTINCT k, s2 FROM %s"));
          checkDistinctRows(rows, true, 0, 10, 1, 11);
  
          rows = getRows(execute("SELECT DISTINCT k, s1 FROM %s LIMIT 10"));
          checkDistinctRows(rows, true, 0, 10, 0, 10);
  
          rows = getRows(execute("SELECT DISTINCT k, s1 FROM %s WHERE k IN (" + keys + ")"));
          checkDistinctRows(rows, false, 0, 10, 0, 10);
  
          rows = getRows(execute("SELECT DISTINCT k, s2 FROM %s WHERE k IN (" + keys + ")"));
          checkDistinctRows(rows, false, 0, 10, 1, 11);
  
          rows = getRows(execute("SELECT DISTINCT k, s1 FROM %s WHERE k IN (" + keys + ")"));
          checkDistinctRows(rows, true, 0, 10, 0, 10);
      }
  
      void checkDistinctRows(Object[][] rows, boolean sort, int... ranges)
      {
          assertTrue(ranges.length % 2 == 0);
  
          int numdim = ranges.length / 2;
          int[] from = new int[numdim];
          int[] to = new int[numdim];
  
          for (int i = 0, j = 0; i < ranges.length && j < numdim; i+= 2, j++)
          {
              from[j] = ranges[i];
              to[j] = ranges[i+1];
          }
  
          //sort the rows
          for (int i = 0; i < numdim; i++)
          {
              int[] vals = new int[rows.length];
              for (int j = 0; j < rows.length; j++)
                  vals[j] = (Integer)rows[j][i];
  
              if (sort)
                  Arrays.sort(vals);
  
              for (int j = from[i]; j < to[i]; j++)
                  assertEquals(j, vals[j - from[i]]);
          }
      }
  
      /**
       * Test LIMIT when static columns are present (#6956),
       * migrated from cql_tests.py:TestCQL.static_with_limit_test()
       */
      @Test
      public void testStaticColumnsWithLimit() throws Throwable
      {
          createTable(" CREATE TABLE %s (k int, s int static, v int, PRIMARY KEY (k, v))");
  
          execute("INSERT INTO %s (k, s) VALUES(0, 42)");
          for (int i = 0; i < 4; i++)
              execute("INSERT INTO %s(k, v) VALUES(0, ?)", i);
  
          assertRows(execute("SELECT * FROM %s WHERE k = 0 LIMIT 1"),
                     row(0, 0, 42));
          assertRows(execute("SELECT * FROM %s WHERE k = 0 LIMIT 2"),
                     row(0, 0, 42),
                     row(0, 1, 42));
          assertRows(execute("SELECT * FROM %s WHERE k = 0 LIMIT 3"),
                     row(0, 0, 42),
                     row(0, 1, 42),
                     row(0, 2, 42));
      }
  
      /**
       * Test for bug of #7455,
       * migrated from cql_tests.py:TestCQL.static_with_empty_clustering_test()
       */
      @Test
      public void testStaticColumnsWithEmptyClustering() throws Throwable
      {
          createTable("CREATE TABLE %s (pkey text, ckey text, value text, static_value text static, PRIMARY KEY(pkey, ckey))");
  
          execute("INSERT INTO %s (pkey, static_value) VALUES ('partition1', 'static value')");
          execute("INSERT INTO %s (pkey, ckey, value) VALUES('partition1', '', 'value')");
  
          assertRows(execute("SELECT * FROM %s"),
                     row("partition1", "", "static value", "value"));
      }
  
      /**
       * Migrated from cql_tests.py:TestCQL.alter_clustering_and_static_test()
       */
      @Test
      public void testAlterClusteringAndStatic() throws Throwable
      {
          createTable("CREATE TABLE %s (bar int, PRIMARY KEY (bar))");
  
          // We shouldn 't allow static when there is not clustering columns
          assertInvalid("ALTER TABLE %s ADD bar2 text static");
      }
  
      /**
       * Ensure that deleting and compacting a static row that should be purged doesn't throw.
       * This is a test for #11988.
       */
      @Test
      public void testStaticColumnPurging() throws Throwable
      {
          createTable("CREATE TABLE %s (pkey text, ckey text, value text, static_value text static, PRIMARY KEY(pkey, ckey)) WITH gc_grace_seconds = 0");
  
          execute("INSERT INTO %s (pkey, ckey, static_value, value) VALUES (?, ?, ?, ?)", "k1", "c1", "s1", "v1");
  
          flush();
  
          execute("DELETE static_value FROM %s WHERE pkey = ?", "k1");
  
          flush();
  
          Thread.sleep(1000);
  
          compact();
  
          assertRows(execute("SELECT * FROM %s"), row("k1", "c1", null, "v1"));
      }
  }
  