@Test
  public void testGetPartitionSplitsTableOfflinePartition() throws Exception {
    ConnectorTableHandle tableHandle = getTableHandle(tableOfflinePartition);
    assertNotNull(tableHandle);

    ConnectorColumnHandle dsColumn = metadata.getColumnHandle(tableHandle, "ds");
    assertNotNull(dsColumn);

    Domain domain = Domain.singleValue(utf8Slice("2012-12-30"));
    TupleDomain<ConnectorColumnHandle> tupleDomain =
        TupleDomain.withColumnDomains(ImmutableMap.of(dsColumn, domain));
    ConnectorPartitionResult partitionResult = splitManager.getPartitions(tableHandle, tupleDomain);
    for (ConnectorPartition partition : partitionResult.getPartitions()) {
      if (domain.equals(partition.getTupleDomain().getDomains().get(dsColumn))) {
        try {
          getSplitCount(splitManager.getPartitionSplits(tableHandle, ImmutableList.of(partition)));
          fail("Expected PartitionOfflineException");
        } catch (PartitionOfflineException e) {
          assertEquals(e.getTableName(), tableOfflinePartition);
          assertEquals(e.getPartition(), "ds=2012-12-30");
        }
      } else {
        getSplitCount(splitManager.getPartitionSplits(tableHandle, ImmutableList.of(partition)));
      }
    }
  }
  private static Set<List<Comparable<?>>> getPartitionKeysSet(
      CassandraTable table, TupleDomain<ColumnHandle> tupleDomain) {
    ImmutableList.Builder<Set<Comparable<?>>> partitionColumnValues = ImmutableList.builder();
    for (CassandraColumnHandle columnHandle : table.getPartitionKeyColumns()) {
      Domain domain = tupleDomain.getDomains().get(columnHandle);

      // if there is no constraint on a partition key, return an empty set
      if (domain == null) {
        return ImmutableSet.of();
      }

      // todo does cassandra allow null partition keys?
      if (domain.isNullAllowed()) {
        return ImmutableSet.of();
      }

      ImmutableSet.Builder<Comparable<?>> columnValues = ImmutableSet.builder();
      for (Range range : domain.getRanges()) {
        // if the range is not a single value, we can not perform partition pruning
        if (!range.isSingleValue()) {
          return ImmutableSet.of();
        }
        Comparable<?> value = range.getSingleValue();

        CassandraType valueType = columnHandle.getCassandraType();
        columnValues.add(valueType.getValueForPartitionKey(value));
      }
      partitionColumnValues.add(columnValues.build());
    }
    return Sets.cartesianProduct(partitionColumnValues.build());
  }
 @Test
 public void testGetPartitionsWithBindings() throws Exception {
   ConnectorTableHandle tableHandle = getTableHandle(table);
   ConnectorPartitionResult partitionResult =
       splitManager.getPartitions(
           tableHandle,
           TupleDomain.withColumnDomains(
               ImmutableMap.<ConnectorColumnHandle, Domain>of(intColumn, Domain.singleValue(5L))));
   assertExpectedPartitions(partitionResult.getPartitions());
 }
  @Override
  public ConnectorPartitionResult getPartitions(
      ConnectorSession session,
      ConnectorTableHandle tableHandle,
      TupleDomain<ColumnHandle> tupleDomain) {
    CassandraTableHandle cassandraTableHandle =
        checkType(tableHandle, CassandraTableHandle.class, "tableHandle");
    checkNotNull(tupleDomain, "tupleDomain is null");
    CassandraTable table = schemaProvider.getTable(cassandraTableHandle);
    List<CassandraColumnHandle> partitionKeys = table.getPartitionKeyColumns();

    // fetch the partitions
    List<CassandraPartition> allPartitions = getCassandraPartitions(table, tupleDomain);
    log.debug(
        "%s.%s #partitions: %d",
        cassandraTableHandle.getSchemaName(),
        cassandraTableHandle.getTableName(),
        allPartitions.size());

    // do a final pass to filter based on fields that could not be used to build the prefix
    List<ConnectorPartition> partitions =
        allPartitions
            .stream()
            .filter(partition -> tupleDomain.overlaps(partition.getTupleDomain()))
            .collect(toList());

    // All partition key domains will be fully evaluated, so we don't need to include those
    TupleDomain<ColumnHandle> remainingTupleDomain = TupleDomain.none();
    if (!tupleDomain.isNone()) {
      if (partitions.size() == 1 && ((CassandraPartition) partitions.get(0)).isUnpartitioned()) {
        remainingTupleDomain = tupleDomain;
      } else {
        @SuppressWarnings({"rawtypes", "unchecked"})
        List<ColumnHandle> partitionColumns = (List) partitionKeys;
        remainingTupleDomain =
            TupleDomain.withColumnDomains(
                Maps.filterKeys(tupleDomain.getDomains(), not(in(partitionColumns))));
      }
    }

    // push down indexed column fixed value predicates only for unpartitioned partition which uses
    // token range query
    if (partitions.size() == 1 && ((CassandraPartition) partitions.get(0)).isUnpartitioned()) {
      Map<ColumnHandle, Domain> domains = tupleDomain.getDomains();
      List<ColumnHandle> indexedColumns = new ArrayList<>();
      // compose partitionId by using indexed column
      StringBuilder sb = new StringBuilder();
      for (Map.Entry<ColumnHandle, Domain> entry : domains.entrySet()) {
        CassandraColumnHandle column = (CassandraColumnHandle) entry.getKey();
        Domain domain = entry.getValue();
        if (column.isIndexed() && domain.isSingleValue()) {
          sb.append(CassandraCqlUtils.validColumnName(column.getName()))
              .append(" = ")
              .append(
                  CassandraCqlUtils.cqlValue(
                      toCQLCompatibleString(entry.getValue().getSingleValue()),
                      column.getCassandraType()));
          indexedColumns.add(column);
          // Only one indexed column predicate can be pushed down.
          break;
        }
      }
      if (sb.length() > 0) {
        CassandraPartition partition = (CassandraPartition) partitions.get(0);
        TupleDomain<ColumnHandle> filterIndexedColumn =
            TupleDomain.withColumnDomains(
                Maps.filterKeys(remainingTupleDomain.getDomains(), not(in(indexedColumns))));
        partitions = new ArrayList<>();
        partitions.add(
            new CassandraPartition(partition.getKey(), sb.toString(), filterIndexedColumn, true));
        return new ConnectorPartitionResult(partitions, filterIndexedColumn);
      }
    }
    return new ConnectorPartitionResult(partitions, remainingTupleDomain);
  }