/*
 *
 * 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 org.junit.BeforeClass;
import org.junit.Test;

import org.apache.cassandra.Util;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.dht.ByteOrderedPartitioner;
import org.apache.cassandra.service.StorageService;

public class SelectLimitTest extends CQLTester
{
    // This method will be ran instead of the CQLTester#setUpClass
    @BeforeClass
    public static void setUpClass()
    {
        daemonInitialization();
        StorageService.instance.setPartitionerUnsafe(ByteOrderedPartitioner.instance);
        prepareServer();
    }

    /**
     * Test limit queries on a sparse table,
     * migrated from cql_tests.py:TestCQL.limit_sparse_test()
     */
    @Test
    public void testSparseTable() throws Throwable
    {
        createTable("CREATE TABLE %s (userid int, url text, day int, month text, year int, PRIMARY KEY (userid, url))");

        for (int i = 0; i < 100; i++)
            for (String tld : new String[] { "com", "org", "net" })
                execute("INSERT INTO %s (userid, url, day, month, year) VALUES (?, ?, 1, 'jan', 2012)", i, String.format("http://foo.%s", tld));

        assertRowCount(execute("SELECT * FROM %s LIMIT 4"), 4);

    }

    @Test
    public void testPerPartitionLimit() throws Throwable
    {
        String query = "CREATE TABLE %s (a int, b int, c int, PRIMARY KEY (a, b))";

        createTable(query);

        for (int i = 0; i < 5; i++)
        {
            for (int j = 0; j < 5; j++)
            {
                execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", i, j, j);
            }
        }

        assertInvalidMessage("LIMIT must be strictly positive",
                             "SELECT * FROM %s PER PARTITION LIMIT ?", 0);
        assertInvalidMessage("LIMIT must be strictly positive",
                             "SELECT * FROM %s PER PARTITION LIMIT ?", -1);

        assertRowsIgnoringOrder(execute("SELECT * FROM %s PER PARTITION LIMIT ?", 2),
                                row(0, 0, 0),
                                row(0, 1, 1),
                                row(1, 0, 0),
                                row(1, 1, 1),
                                row(2, 0, 0),
                                row(2, 1, 1),
                                row(3, 0, 0),
                                row(3, 1, 1),
                                row(4, 0, 0),
                                row(4, 1, 1));

        // Combined Per Partition and "global" limit
        assertRowCount(execute("SELECT * FROM %s PER PARTITION LIMIT ? LIMIT ?", 2, 6),
                       6);

        // odd amount of results
        assertRowCount(execute("SELECT * FROM %s PER PARTITION LIMIT ? LIMIT ?", 2, 5),
                       5);

        // IN query
        assertRows(execute("SELECT * FROM %s WHERE a IN (2,3) PER PARTITION LIMIT ?", 2),
                   row(2, 0, 0),
                   row(2, 1, 1),
                   row(3, 0, 0),
                   row(3, 1, 1));

        assertRows(execute("SELECT * FROM %s WHERE a IN (2,3) PER PARTITION LIMIT ? LIMIT 3", 2),
                   row(2, 0, 0),
                   row(2, 1, 1),
                   row(3, 0, 0));

        assertRows(execute("SELECT * FROM %s WHERE a IN (1,2,3) PER PARTITION LIMIT ? LIMIT 3", 2),
                   row(1, 0, 0),
                   row(1, 1, 1),
                   row(2, 0, 0));

        // with restricted partition key
        assertRows(execute("SELECT * FROM %s WHERE a = ? PER PARTITION LIMIT ?", 2, 3),
                   row(2, 0, 0),
                   row(2, 1, 1),
                   row(2, 2, 2));

        // with ordering
        assertRows(execute("SELECT * FROM %s WHERE a IN (3, 2) ORDER BY b DESC PER PARTITION LIMIT ?", 2),
                   row(2, 4, 4),
                   row(3, 4, 4),
                   row(2, 3, 3),
                   row(3, 3, 3));

        assertRows(execute("SELECT * FROM %s WHERE a IN (3, 2) ORDER BY b DESC PER PARTITION LIMIT ? LIMIT ?", 3, 4),
                   row(2, 4, 4),
                   row(3, 4, 4),
                   row(2, 3, 3),
                   row(3, 3, 3));

        assertRows(execute("SELECT * FROM %s WHERE a = ? ORDER BY b DESC PER PARTITION LIMIT ?", 2, 3),
                   row(2, 4, 4),
                   row(2, 3, 3),
                   row(2, 2, 2));

        // with filtering
        assertRows(execute("SELECT * FROM %s WHERE a = ? AND b > ? PER PARTITION LIMIT ? ALLOW FILTERING", 2, 0, 2),
                   row(2, 1, 1),
                   row(2, 2, 2));

        assertRows(execute("SELECT * FROM %s WHERE a = ? AND b > ? ORDER BY b DESC PER PARTITION LIMIT ? ALLOW FILTERING", 2, 2, 2),
                   row(2, 4, 4),
                   row(2, 3, 3));

        assertInvalidMessage("PER PARTITION LIMIT is not allowed with SELECT DISTINCT queries",
                             "SELECT DISTINCT a FROM %s PER PARTITION LIMIT ?", 3);
        assertInvalidMessage("PER PARTITION LIMIT is not allowed with SELECT DISTINCT queries",
                             "SELECT DISTINCT a FROM %s PER PARTITION LIMIT ? LIMIT ?", 3, 4);
        assertInvalidMessage("PER PARTITION LIMIT is not allowed with aggregate queries.",
                             "SELECT COUNT(*) FROM %s PER PARTITION LIMIT ?", 3);
    }

    @Test
    public void testPerPartitionLimitWithStaticDataAndPaging() throws Throwable
    {
        String query = "CREATE TABLE %s (a int, b int, s int static, c int, PRIMARY KEY (a, b))";

        createTable(query);

        for (int i = 0; i < 5; i++)
        {
            execute("INSERT INTO %s (a, s) VALUES (?, ?)", i, i);
        }

        for (int pageSize = 1; pageSize < 8; pageSize++)
        {
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s PER PARTITION LIMIT 2", pageSize),
                          row(0, null, 0, null),
                          row(1, null, 1, null),
                          row(2, null, 2, null),
                          row(3, null, 3, null),
                          row(4, null, 4, null));

            // Combined Per Partition and "global" limit
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s PER PARTITION LIMIT 2 LIMIT 4", pageSize),
                          row(0, null, 0, null),
                          row(1, null, 1, null),
                          row(2, null, 2, null),
                          row(3, null, 3, null));

            // odd amount of results
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s PER PARTITION LIMIT 2 LIMIT 3", pageSize),
                          row(0, null, 0, null),
                          row(1, null, 1, null),
                          row(2, null, 2, null));

            // IN query
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE a IN (1,3,4) PER PARTITION LIMIT 2", pageSize),
                          row(1, null, 1, null),
                          row(3, null, 3, null),
                          row(4, null, 4, null));

            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE a IN (1,3,4) PER PARTITION LIMIT 2 LIMIT 2",
                                               pageSize),
                          row(1, null, 1, null),
                          row(3, null, 3, null));

            // with restricted partition key
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE a = 2 PER PARTITION LIMIT 3", pageSize),
                          row(2, null, 2, null));

            // with ordering
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE a = 2 ORDER BY b DESC PER PARTITION LIMIT 3",
                                               pageSize),
                          row(2, null, 2, null));

            // with filtering
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE a = 2 AND s > 0 PER PARTITION LIMIT 2 ALLOW FILTERING",
                                               pageSize),
                          row(2, null, 2, null));
        }

        for (int i = 0; i < 5; i++)
        {
            if (i != 1)
            {
                for (int j = 0; j < 5; j++)
                {
                    execute("INSERT INTO %s (a, b, s, c) VALUES (?, ?, ?, ?)", i, j, i, j);
                }
            }
        }

        assertInvalidMessage("LIMIT must be strictly positive",
                             "SELECT * FROM %s PER PARTITION LIMIT ?", 0);
        assertInvalidMessage("LIMIT must be strictly positive",
                             "SELECT * FROM %s PER PARTITION LIMIT ?", -1);

        for (int pageSize = 1; pageSize < 8; pageSize++)
        {
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s PER PARTITION LIMIT 2", pageSize),
                          row(0, 0, 0, 0),
                          row(0, 1, 0, 1),
                          row(1, null, 1, null),
                          row(2, 0, 2, 0),
                          row(2, 1, 2, 1),
                          row(3, 0, 3, 0),
                          row(3, 1, 3, 1),
                          row(4, 0, 4, 0),
                          row(4, 1, 4, 1));

            // Combined Per Partition and "global" limit
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s PER PARTITION LIMIT 2 LIMIT 4", pageSize),
                          row(0, 0, 0, 0),
                          row(0, 1, 0, 1),
                          row(1, null, 1, null),
                          row(2, 0, 2, 0));

            // odd amount of results
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s PER PARTITION LIMIT 2 LIMIT 5", pageSize),
                          row(0, 0, 0, 0),
                          row(0, 1, 0, 1),
                          row(1, null, 1, null),
                          row(2, 0, 2, 0),
                          row(2, 1, 2, 1));

            // IN query
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE a IN (2,3) PER PARTITION LIMIT 2", pageSize),
                          row(2, 0, 2, 0),
                          row(2, 1, 2, 1),
                          row(3, 0, 3, 0),
                          row(3, 1, 3, 1));

            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE a IN (2,3) PER PARTITION LIMIT 2 LIMIT 3",
                                               pageSize),
                          row(2, 0, 2, 0),
                          row(2, 1, 2, 1),
                          row(3, 0, 3, 0));

            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE a IN (1,2,3) PER PARTITION LIMIT 2 LIMIT 3",
                                               pageSize),
                          row(1, null, 1, null),
                          row(2, 0, 2, 0),
                          row(2, 1, 2, 1));

            // with restricted partition key
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE a = 2 PER PARTITION LIMIT 3", pageSize),
                          row(2, 0, 2, 0),
                          row(2, 1, 2, 1),
                          row(2, 2, 2, 2));

            // with ordering
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE a = 2 ORDER BY b DESC PER PARTITION LIMIT 3",
                                               pageSize),
                          row(2, 4, 2, 4),
                          row(2, 3, 2, 3),
                          row(2, 2, 2, 2));

            // with filtering
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE a = 2 AND b > 0 PER PARTITION LIMIT 2 ALLOW FILTERING",
                                               pageSize),
                          row(2, 1, 2, 1),
                          row(2, 2, 2, 2));

            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE a = 2 AND b > 2 ORDER BY b DESC PER PARTITION LIMIT 2 ALLOW FILTERING",
                                               pageSize),
                          row(2, 4, 2, 4),
                          row(2, 3, 2, 3));
        }

        assertInvalidMessage("PER PARTITION LIMIT is not allowed with SELECT DISTINCT queries",
                             "SELECT DISTINCT a FROM %s PER PARTITION LIMIT ?", 3);
        assertInvalidMessage("PER PARTITION LIMIT is not allowed with SELECT DISTINCT queries",
                             "SELECT DISTINCT a FROM %s PER PARTITION LIMIT ? LIMIT ?", 3, 4);
        assertInvalidMessage("PER PARTITION LIMIT is not allowed with aggregate queries.",
                             "SELECT COUNT(*) FROM %s PER PARTITION LIMIT ?", 3);
    }

    @Test
    public void testLimitWithDeletedRowsAndStaticColumns() throws Throwable
    {
        createTable("CREATE TABLE %s (pk int, c int, v int, s int static, PRIMARY KEY (pk, c))");

        execute("INSERT INTO %s (pk, c, v, s) VALUES (1, -1, 1, 1)");
        execute("INSERT INTO %s (pk, c, v, s) VALUES (2, -1, 1, 1)");
        execute("INSERT INTO %s (pk, c, v, s) VALUES (3, -1, 1, 1)");
        execute("INSERT INTO %s (pk, c, v, s) VALUES (4, -1, 1, 1)");
        execute("INSERT INTO %s (pk, c, v, s) VALUES (5, -1, 1, 1)");

        assertRows(execute("SELECT * FROM %s"),
                   row(1, -1, 1, 1),
                   row(2, -1, 1, 1),
                   row(3, -1, 1, 1),
                   row(4, -1, 1, 1),
                   row(5, -1, 1, 1));

        execute("DELETE FROM %s WHERE pk = 2");

        assertRows(execute("SELECT * FROM %s"),
                   row(1, -1, 1, 1),
                   row(3, -1, 1, 1),
                   row(4, -1, 1, 1),
                   row(5, -1, 1, 1));

        assertRows(execute("SELECT * FROM %s LIMIT 2"),
                   row(1, -1, 1, 1),
                   row(3, -1, 1, 1));
    }

    @Test
    public void testFilteringOnClusteringColumnsWithLimitAndStaticColumns() throws Throwable
    {
        // With only one clustering column
        createTable("CREATE TABLE %s (a int, b int, s int static, c int, primary key (a, b))"
             + " WITH caching = {'keys': 'ALL', 'rows_per_partition' : 'ALL'}");

        for (int i = 0; i < 4; i++)
        {
            execute("INSERT INTO %s (a, s) VALUES (?, ?)", i, i);
                for (int j = 0; j < 3; j++)
                    if (!((i == 0 || i == 3) && j == 1))
                        execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", i, j, i + j);
        }

        beforeAndAfterFlush(() ->
        {
            assertRows(execute("SELECT * FROM %s"),
                       row(0, 0, 0, 0),
                       row(0, 2, 0, 2),
                       row(1, 0, 1, 1),
                       row(1, 1, 1, 2),
                       row(1, 2, 1, 3),
                       row(2, 0, 2, 2),
                       row(2, 1, 2, 3),
                       row(2, 2, 2, 4),
                       row(3, 0, 3, 3),
                       row(3, 2, 3, 5));

            assertRows(execute("SELECT * FROM %s WHERE b = 1 ALLOW FILTERING"),
                       row(1, 1, 1, 2),
                       row(2, 1, 2, 3));

            // The problem was that the static row of the partition 0 used to be only filtered in SelectStatement and was
            // by consequence counted as a row. In which case the query was returning one row less.
            assertRows(execute("SELECT * FROM %s WHERE b = 1 LIMIT 2 ALLOW FILTERING"),
                       row(1, 1, 1, 2),
                       row(2, 1, 2, 3));

            assertRows(execute("SELECT * FROM %s WHERE b >= 1 AND b <= 1 LIMIT 2 ALLOW FILTERING"),
                       row(1, 1, 1, 2),
                       row(2, 1, 2, 3));

            // Test with paging
            for (int pageSize = 1; pageSize < 4; pageSize++)
            {
                assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE b = 1 LIMIT 2 ALLOW FILTERING", pageSize),
                              row(1, 1, 1, 2),
                              row(2, 1, 2, 3));

                assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE b >= 1 AND b <= 1 LIMIT 2 ALLOW FILTERING", pageSize),
                              row(1, 1, 1, 2),
                              row(2, 1, 2, 3));

                assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE b = 1 GROUP BY a LIMIT 2 ALLOW FILTERING", pageSize),
                              row(1, 1, 1, 2),
                              row(2, 1, 2, 3));

                assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE b >= 1 AND b <= 1 GROUP BY a LIMIT 2 ALLOW FILTERING", pageSize),
                              row(1, 1, 1, 2),
                              row(2, 1, 2, 3));
            }
        });

        assertRows(execute("SELECT * FROM %s WHERE a IN (0, 1, 2, 3) AND b = 1 LIMIT 2 ALLOW FILTERING"),
                   row(1, 1, 1, 2),
                   row(2, 1, 2, 3));

        assertRows(execute("SELECT * FROM %s WHERE a IN (0, 1, 2, 3) AND b >= 1 AND b <= 1 LIMIT 2 ALLOW FILTERING"),
                   row(1, 1, 1, 2),
                   row(2, 1, 2, 3));

        assertRows(execute("SELECT * FROM %s WHERE a IN (0, 1, 2, 3) AND b = 1 ORDER BY b DESC LIMIT 2 ALLOW FILTERING"),
                   row(1, 1, 1, 2),
                   row(2, 1, 2, 3));

        assertRows(execute("SELECT * FROM %s WHERE a IN (0, 1, 2, 3) AND b >= 1 AND b <= 1 ORDER BY b DESC LIMIT 2 ALLOW FILTERING"),
                   row(1, 1, 1, 2),
                   row(2, 1, 2, 3));

        execute("SELECT * FROM %s WHERE a IN (0, 1, 2, 3)"); // Load all data in the row cache

        // Partition range queries
        assertRows(execute("SELECT * FROM %s WHERE b = 1 LIMIT 2 ALLOW FILTERING"),
                   row(1, 1, 1, 2),
                   row(2, 1, 2, 3));

        assertRows(execute("SELECT * FROM %s WHERE b >= 1 AND b <= 1 LIMIT 2 ALLOW FILTERING"),
                   row(1, 1, 1, 2),
                   row(2, 1, 2, 3));

        // Multiple partitions queries
        assertRows(execute("SELECT * FROM %s WHERE a IN (0, 1, 2) AND b = 1 LIMIT 2 ALLOW FILTERING"),
                   row(1, 1, 1, 2),
                   row(2, 1, 2, 3));

        assertRows(execute("SELECT * FROM %s WHERE a IN (0, 1, 2) AND b >= 1 AND b <= 1 LIMIT 2 ALLOW FILTERING"),
                   row(1, 1, 1, 2),
                   row(2, 1, 2, 3));

        // Test with paging
        for (int pageSize = 1; pageSize < 4; pageSize++)
        {
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE b = 1 LIMIT 2 ALLOW FILTERING", pageSize),
                          row(1, 1, 1, 2),
                          row(2, 1, 2, 3));

            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE b >= 1 AND b <= 1 LIMIT 2 ALLOW FILTERING", pageSize),
                          row(1, 1, 1, 2),
                          row(2, 1, 2, 3));
        }

        // With multiple clustering columns
        createTable("CREATE TABLE %s (a int, b int, c int, s int static, d int, primary key (a, b, c))"
           + " WITH caching = {'keys': 'ALL', 'rows_per_partition' : 'ALL'}");

        for (int i = 0; i < 3; i++)
        {
            execute("INSERT INTO %s (a, s) VALUES (?, ?)", i, i);
                for (int j = 0; j < 3; j++)
                    if (!(i == 0 && j == 1))
                        execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", i, j, j, i + j);
        }

        beforeAndAfterFlush(() ->
        {
            assertRows(execute("SELECT * FROM %s"),
                       row(0, 0, 0, 0, 0),
                       row(0, 2, 2, 0, 2),
                       row(1, 0, 0, 1, 1),
                       row(1, 1, 1, 1, 2),
                       row(1, 2, 2, 1, 3),
                       row(2, 0, 0, 2, 2),
                       row(2, 1, 1, 2, 3),
                       row(2, 2, 2, 2, 4));

            assertRows(execute("SELECT * FROM %s WHERE b = 1 ALLOW FILTERING"),
                       row(1, 1, 1, 1, 2),
                       row(2, 1, 1, 2, 3));

            assertRows(execute("SELECT * FROM %s WHERE b IN (1, 2, 3, 4) AND c >= 1 AND c <= 1 LIMIT 2 ALLOW FILTERING"),
                       row(1, 1, 1, 1, 2),
                       row(2, 1, 1, 2, 3));

            // Test with paging
            for (int pageSize = 1; pageSize < 4; pageSize++)
            {
                assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE b = 1 LIMIT 2 ALLOW FILTERING", pageSize),
                              row(1, 1, 1, 1, 2),
                              row(2, 1, 1, 2, 3));

                assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE b IN (1, 2, 3, 4) AND c >= 1 AND c <= 1 LIMIT 2 ALLOW FILTERING", pageSize),
                              row(1, 1, 1, 1, 2),
                              row(2, 1, 1, 2, 3));

                assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE b = 1 GROUP BY a, b LIMIT 2 ALLOW FILTERING", pageSize),
                              row(1, 1, 1, 1, 2),
                              row(2, 1, 1, 2, 3));

                assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE b IN (1, 2, 3, 4) AND c >= 1 AND c <= 1 GROUP BY a, b LIMIT 2 ALLOW FILTERING", pageSize),
                              row(1, 1, 1, 1, 2),
                              row(2, 1, 1, 2, 3));
            }
        });

        execute("SELECT * FROM %s WHERE a IN (0, 1, 2)"); // Load data in the row cache

        assertRows(execute("SELECT * FROM %s WHERE b = 1 ALLOW FILTERING"),
                   row(1, 1, 1, 1, 2),
                   row(2, 1, 1, 2, 3));

        assertRows(execute("SELECT * FROM %s WHERE b IN (1, 2, 3, 4) AND c >= 1 AND c <= 1 LIMIT 2 ALLOW FILTERING"),
                   row(1, 1, 1, 1, 2),
                   row(2, 1, 1, 2, 3));

        assertRows(execute("SELECT * FROM %s WHERE a IN (1, 2, 3, 4) AND b = 1 ALLOW FILTERING"),
                   row(1, 1, 1, 1, 2),
                   row(2, 1, 1, 2, 3));

        assertRows(execute("SELECT * FROM %s WHERE a IN (1, 2, 3, 4) AND b IN (1, 2, 3, 4) AND c >= 1 AND c <= 1 LIMIT 2 ALLOW FILTERING"),
                   row(1, 1, 1, 1, 2),
                   row(2, 1, 1, 2, 3));

        // Test with paging
        for (int pageSize = 1; pageSize < 4; pageSize++)
        {
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE b = 1 LIMIT 2 ALLOW FILTERING", pageSize),
                          row(1, 1, 1, 1, 2),
                          row(2, 1, 1, 2, 3));

            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE b IN (1, 2, 3, 4) AND c >= 1 AND c <= 1 LIMIT 2 ALLOW FILTERING", pageSize),
                          row(1, 1, 1, 1, 2),
                          row(2, 1, 1, 2, 3));
        }
    }

    @Test
    public void testIndexOnRegularColumnWithPartitionWithoutRows()
    {
        Util.assumeLegacySecondaryIndex();
        createTable("CREATE TABLE %s (pk int, c int, s int static, v int, PRIMARY KEY(pk, c))");
        createIndex("CREATE INDEX ON %s (v)");

        execute("INSERT INTO %s (pk, c, s, v) VALUES (?, ?, ?, ?)", 1, 1, 9, 1);
        execute("INSERT INTO %s (pk, c, s, v) VALUES (?, ?, ?, ?)", 1, 2, 9, 2);
        execute("INSERT INTO %s (pk, c, s, v) VALUES (?, ?, ?, ?)", 3, 1, 9, 1);
        execute("INSERT INTO %s (pk, c, s, v) VALUES (?, ?, ?, ?)", 4, 1, 9, 1);
        flush();

        assertRows(execute("SELECT * FROM %s WHERE v = ?", 1),
                   row(1, 1, 9, 1),
                   row(3, 1, 9, 1),
                   row(4, 1, 9, 1));

        execute("DELETE FROM %s WHERE pk = ? and c = ?", 3, 1);

        // Test without paging
        assertRows(execute("SELECT * FROM %s WHERE v = ? LIMIT 2", 1),
                   row(1, 1, 9, 1),
                   row(4, 1, 9, 1));

        assertRows(execute("SELECT * FROM %s WHERE v = ? GROUP BY pk LIMIT 2", 1),
                   row(1, 1, 9, 1),
                   row(4, 1, 9, 1));

        // Test with paging
        for (int pageSize = 1; pageSize < 4; pageSize++)
        {
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE v = 1 LIMIT 2", pageSize),
                          row(1, 1, 9, 1),
                          row(4, 1, 9, 1));

            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE v = 1 GROUP BY pk LIMIT 2", pageSize),
                          row(1, 1, 9, 1),
                          row(4, 1, 9, 1));
        }
    }

    @Test
    public void testFilteringWithPartitionWithoutRows() throws Throwable
    {
        createTable("CREATE TABLE %s (pk int, c int, s int static, v int, PRIMARY KEY(pk, c))");

        execute("INSERT INTO %s (pk, c, s, v) VALUES (?, ?, ?, ?)", 1, 1, 9, 1);
        execute("INSERT INTO %s (pk, c, s, v) VALUES (?, ?, ?, ?)", 1, 2, 9, 2);
        execute("INSERT INTO %s (pk, c, s, v) VALUES (?, ?, ?, ?)", 3, 1, 9, 1);
        execute("INSERT INTO %s (pk, c, s, v) VALUES (?, ?, ?, ?)", 4, 1, 9, 1);
        flush();

        assertRows(execute("SELECT * FROM %s WHERE v = ? ALLOW FILTERING", 1),
                   row(1, 1, 9, 1),
                   row(3, 1, 9, 1),
                   row(4, 1, 9, 1));

        execute("DELETE FROM %s WHERE pk = ? and c = ?", 3, 1);

        // Test without paging
        assertRows(execute("SELECT * FROM %s WHERE v = ? LIMIT 2 ALLOW FILTERING", 1),
                   row(1, 1, 9, 1),
                   row(4, 1, 9, 1));

        assertRows(execute("SELECT * FROM %s WHERE pk IN ? AND v = ? LIMIT 2 ALLOW FILTERING", list(1, 3, 4), 1),
                   row(1, 1, 9, 1),
                   row(4, 1, 9, 1));

        // Test with paging
        for (int pageSize = 1; pageSize < 4; pageSize++)
        {
            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE v = 1 LIMIT 2 ALLOW FILTERING", pageSize),
                          row(1, 1, 9, 1),
                          row(4, 1, 9, 1));

            assertRowsNet(executeNetWithPaging("SELECT * FROM %s WHERE pk IN (1, 3, 4) AND v = 1 LIMIT 2 ALLOW FILTERING", pageSize),
                          row(1, 1, 9, 1),
                          row(4, 1, 9, 1));
        }
    }
}
