/**
   * @param idx Column index.
   * @param rows Rows.
   * @return Column as list.
   */
  private static <X> List<X> column(int idx, List<List<?>> rows) {
    List<X> res = new ArrayList<>(rows.size());

    for (List<?> row : rows) res.add((X) row.get(idx));

    return res;
  }
  /**
   * Tests offset and limit clauses for query.
   *
   * @throws Exception If failed.
   */
  public void testOffsetLimit() throws Exception {
    IgniteCache<Integer, Integer> c =
        ignite(0).getOrCreateCache(cacheConfig("ints", true, Integer.class, Integer.class));

    try {
      List<Integer> res = new ArrayList<>();

      Random rnd = new GridRandom();

      for (int i = 0; i < 10; i++) {
        int val = rnd.nextInt(100);

        c.put(i, val);
        res.add(val);
      }

      Collections.sort(res);

      String qry = "select _val from Integer order by _val ";

      assertEqualsCollections(res, columnQuery(c, qry));
      assertEqualsCollections(res.subList(0, 0), columnQuery(c, qry + "limit ?", 0));
      assertEqualsCollections(res.subList(0, 3), columnQuery(c, qry + "limit ?", 3));
      assertEqualsCollections(res.subList(0, 9), columnQuery(c, qry + "limit ? offset ?", 9, 0));
      assertEqualsCollections(res.subList(3, 7), columnQuery(c, qry + "limit ? offset ?", 4, 3));
      assertEqualsCollections(res.subList(7, 9), columnQuery(c, qry + "limit ? offset ?", 2, 7));
      assertEqualsCollections(res.subList(8, 10), columnQuery(c, qry + "limit ? offset ?", 2, 8));
      assertEqualsCollections(res.subList(9, 10), columnQuery(c, qry + "limit ? offset ?", 1, 9));
      assertEqualsCollections(res.subList(10, 10), columnQuery(c, qry + "limit ? offset ?", 1, 10));
      assertEqualsCollections(
          res.subList(9, 10), columnQuery(c, qry + "limit ? offset abs(-(4 + ?))", 1, 5));
    } finally {
      c.destroy();
    }
  }
  /** @throws Exception If failed. */
  public void testGroupIndexOperations() throws Exception {
    IgniteCache<Integer, GroupIndexTestValue> c =
        ignite(0)
            .getOrCreateCache(cacheConfig("grp", false, Integer.class, GroupIndexTestValue.class));

    try {
      // Check group index usage.
      String qry = "select 1 from GroupIndexTestValue ";

      String plan = columnQuery(c, "explain " + qry + "where a = 1 and b > 0").get(0).toString();

      info("Plan: " + plan);

      assertTrue(plan.contains("grpIdx"));

      // Sorted list
      List<GroupIndexTestValue> list =
          F.asList(
              new GroupIndexTestValue(0, 0),
              new GroupIndexTestValue(0, 5),
              new GroupIndexTestValue(1, 1),
              new GroupIndexTestValue(1, 3),
              new GroupIndexTestValue(2, -1),
              new GroupIndexTestValue(2, 2));

      // Fill cache.
      for (int i = 0; i < list.size(); i++) c.put(i, list.get(i));

      // Check results.
      assertEquals(1, columnQuery(c, qry + "where a = 1 and b = 1").size());
      assertEquals(2, columnQuery(c, qry + "where a = 1 and b < 4").size());
      assertEquals(2, columnQuery(c, qry + "where a = 1 and b <= 3").size());
      assertEquals(1, columnQuery(c, qry + "where a = 1 and b < 3").size());
      assertEquals(2, columnQuery(c, qry + "where a = 1 and b > 0").size());
      assertEquals(1, columnQuery(c, qry + "where a = 1 and b > 1").size());
      assertEquals(2, columnQuery(c, qry + "where a = 1 and b >= 1").size());
      assertEquals(4, columnQuery(c, qry + "where a > 0 and b > 0").size());
      assertEquals(4, columnQuery(c, qry + "where a > 0 and b >= 1").size());
      assertEquals(3, columnQuery(c, qry + "where a > 0 and b > 1").size());
    } finally {
      c.destroy();
    }
  }