/**
   * If the key is to be associated with a valid value, a mutation is created for it with the given
   * table and columns. In the event the value in the column is missing (i.e., null), then it is
   * marked for {@link Deletion}. Similarly, if the entire value for a key is missing (i.e., null),
   * then the entire key is marked for {@link Deletion}.
   *
   * @param keyColumns the key to write.
   * @param values the values to write.
   * @throws IOException
   */
  @Override
  public void write(Map<String, ByteBuffer> keyColumns, List<ByteBuffer> values)
      throws IOException {
    TokenRange range = ringCache.getRange(getPartitionKey(keyColumns));

    // get the client for the given range, or create a new one
    final InetAddress address = ringCache.getEndpoints(range).get(0);
    RangeClient client = clients.get(address);
    if (client == null) {
      // haven't seen keys for this range: create new client
      client = new RangeClient(ringCache.getEndpoints(range));
      client.start();
      clients.put(address, client);
    }

    // add primary key columns to the bind variables
    List<ByteBuffer> allValues = new ArrayList<ByteBuffer>(values);
    for (ColumnMetadata column : partitionKeyColumns)
      allValues.add(keyColumns.get(column.getName()));
    for (ColumnMetadata column : clusterColumns) allValues.add(keyColumns.get(column.getName()));

    client.put(allValues);

    if (progressable != null) progressable.progress();
    if (context != null) HadoopCompat.progress(context);
  }
  @Test
  public void testAlter() throws Exception {

    CreateTable desired =
        TableBuilder.create("test_keyspace", "test_table")
            .column("col1", "text")
            .column("col2", "bigint")
            .column("col3", "int")
            .column("col4", "text")
            .primaryKey("col1");

    TableMetadata existing = mock(TableMetadata.class);

    ColumnMetadata col1 = mock(ColumnMetadata.class);
    when(existing.getColumn(eq("col1"))).thenReturn(col1);
    when(col1.getType()).thenReturn(DataType.text());

    ColumnMetadata col2 = mock(ColumnMetadata.class);
    when(existing.getColumn(eq("col2"))).thenReturn(col2);
    when(col2.getType()).thenReturn(DataType.cint());

    List<AlterTable> statements = TableBuilder.alter(existing, desired);

    assertEquals(3, statements.size());
    assertEquals(
        "ALTER TABLE test_keyspace.test_table ALTER col2 TYPE bigint",
        statements.get(0).toString());
    assertEquals("ALTER TABLE test_keyspace.test_table ADD col3 int", statements.get(1).toString());
    assertEquals(
        "ALTER TABLE test_keyspace.test_table ADD col4 text", statements.get(2).toString());
  }
  /** add where clauses for partition keys and cluster columns */
  private String appendKeyWhereClauses(String cqlQuery) {
    String keyWhereClause = "";

    for (ColumnMetadata partitionKey : partitionKeyColumns)
      keyWhereClause +=
          String.format(
              "%s = ?",
              keyWhereClause.isEmpty()
                  ? quote(partitionKey.getName())
                  : (" AND " + quote(partitionKey.getName())));
    for (ColumnMetadata clusterColumn : clusterColumns)
      keyWhereClause += " AND " + quote(clusterColumn.getName()) + " = ?";

    return cqlQuery + " WHERE " + keyWhereClause;
  }
  private void validateColumn(
      TableMetadata tableMetaData, String columnName, Class<?> columnJavaType, boolean indexed) {

    log.debug(
        "Validate existing column {} from table {} against type {}",
        columnName,
        tableMetaData.getName(),
        columnJavaType);

    String tableName = tableMetaData.getName();
    ColumnMetadata columnMetadata = tableMetaData.getColumn(columnName);
    Name expectedType = toCQLType(columnJavaType);

    Validator.validateTableTrue(
        columnMetadata != null, "Cannot find column '%s' in the table '%s'", columnName, tableName);

    boolean columnIsIndexed = columnMetadata.getIndex() != null;

    Validator.validateTableFalse(
        (columnIsIndexed ^ indexed),
        "Column '%s' in the table '%s' is indexed (or not) whereas metadata indicates it"
            + " is (or not)",
        columnName,
        tableName);
    Name realType = columnMetadata.getType().getName();

    /*
     * See JIRA
     */
    if (realType == Name.CUSTOM) {
      realType = Name.BLOB;
    }

    Validator.validateTableTrue(
        expectedType == realType,
        "Column '%s' of table '%s' of type '%s' should be of type '%s' indeed",
        columnName,
        tableName,
        realType,
        expectedType);
  }
  public void validateAchillesCounter(KeyspaceMetadata keyspaceMetaData, String keyspaceName) {
    log.debug("Validate existing Achilles Counter table");
    Name textTypeName = text().getName();
    Name counterTypeName = counter().getName();

    TableMetadata tableMetaData = keyspaceMetaData.getTable(CQL_COUNTER_TABLE);
    Validator.validateTableTrue(
        tableMetaData != null,
        "Cannot find table '%s' from keyspace '%s'",
        CQL_COUNTER_TABLE,
        keyspaceName);

    ColumnMetadata fqcnColumn = tableMetaData.getColumn(CQL_COUNTER_FQCN);
    Validator.validateTableTrue(
        fqcnColumn != null,
        "Cannot find column '%s' from table '%s'",
        CQL_COUNTER_FQCN,
        CQL_COUNTER_TABLE);
    Validator.validateTableTrue(
        fqcnColumn.getType().getName() == textTypeName,
        "Column '%s' of type '%s' should be of type '%s'",
        CQL_COUNTER_FQCN,
        fqcnColumn.getType().getName(),
        textTypeName);
    Validator.validateBeanMappingTrue(
        hasColumnMeta(tableMetaData.getPartitionKey(), fqcnColumn),
        "Column '%s' of table '%s' should be a partition key component",
        CQL_COUNTER_FQCN,
        CQL_COUNTER_TABLE);

    ColumnMetadata pkColumn = tableMetaData.getColumn(CQL_COUNTER_PRIMARY_KEY);
    Validator.validateTableTrue(
        pkColumn != null,
        "Cannot find column '%s' from table '%s'",
        CQL_COUNTER_PRIMARY_KEY,
        CQL_COUNTER_TABLE);
    Validator.validateTableTrue(
        pkColumn.getType().getName() == textTypeName,
        "Column '%s' of type '%s' should be of type '%s'",
        CQL_COUNTER_PRIMARY_KEY,
        pkColumn.getType().getName(),
        textTypeName);
    Validator.validateBeanMappingTrue(
        hasColumnMeta(tableMetaData.getPartitionKey(), pkColumn),
        "Column '%s' of table '%s' should be a partition key component",
        CQL_COUNTER_PRIMARY_KEY,
        CQL_COUNTER_TABLE);

    ColumnMetadata propertyNameColumn = tableMetaData.getColumn(CQL_COUNTER_PROPERTY_NAME);
    Validator.validateTableTrue(
        propertyNameColumn != null,
        "Cannot find column '%s' from table '%s'",
        CQL_COUNTER_PROPERTY_NAME,
        CQL_COUNTER_TABLE);
    Validator.validateTableTrue(
        propertyNameColumn.getType().getName() == textTypeName,
        "Column '%s' of type '%s' should be of type '%s'",
        CQL_COUNTER_PROPERTY_NAME,
        propertyNameColumn.getType().getName(),
        textTypeName);
    Validator.validateBeanMappingTrue(
        hasColumnMeta(tableMetaData.getClusteringColumns(), propertyNameColumn),
        "Column '%s' of table '%s' should be a clustering key component",
        CQL_COUNTER_PROPERTY_NAME,
        CQL_COUNTER_TABLE);

    ColumnMetadata counterValueColumn = tableMetaData.getColumn(CQL_COUNTER_VALUE);
    Validator.validateTableTrue(
        counterValueColumn != null,
        "Cannot find column '%s' from table '%s'",
        CQL_COUNTER_VALUE,
        CQL_COUNTER_TABLE);
    Validator.validateTableTrue(
        counterValueColumn.getType().getName() == counterTypeName,
        "Column '%s' of type '%s' should be of type '%s'",
        CQL_COUNTER_VALUE,
        counterValueColumn.getType().getName(),
        counterTypeName);
  }
  private void validateCollectionAndMapColumn(TableMetadata tableMetadata, PropertyMeta pm) {

    log.debug("Validate existing collection/map column {} from table {}");

    String columnName = pm.getPropertyName().toLowerCase();
    String tableName = tableMetadata.getName();
    ColumnMetadata columnMetadata = tableMetadata.getColumn(columnName);

    Validator.validateTableTrue(
        columnMetadata != null, "Cannot find column '%s' in the table '%s'", columnName, tableName);
    Name realType = columnMetadata.getType().getName();
    Name expectedValueType = toCQLType(pm.getValueClassForTableCreation());

    switch (pm.type()) {
      case LIST:
        Validator.validateTableTrue(
            realType == Name.LIST,
            "Column '%s' of table '%s' of type '%s' should be of type '%s' indeed",
            columnName,
            tableName,
            realType,
            Name.LIST);
        Name realListValueType = columnMetadata.getType().getTypeArguments().get(0).getName();
        Validator.validateTableTrue(
            realListValueType == expectedValueType,
            "Column '%s' of table '%s' of type 'List<%s>' should be of type 'List<%s>' indeed",
            columnName,
            tableName,
            realListValueType,
            expectedValueType);

        break;
      case SET:
        Validator.validateTableTrue(
            realType == Name.SET,
            "Column '%s' of table '%s' of type '%s' should be of type '%s' indeed",
            columnName,
            tableName,
            realType,
            Name.SET);
        Name realSetValueType = columnMetadata.getType().getTypeArguments().get(0).getName();

        Validator.validateTableTrue(
            realSetValueType == expectedValueType,
            "Column '%s' of table '%s' of type 'Set<%s>' should be of type 'Set<%s>' indeed",
            columnName,
            tableName,
            realSetValueType,
            expectedValueType);
        break;
      case MAP:
        Validator.validateTableTrue(
            realType == Name.MAP,
            "Column '%s' of table '%s' of type '%s' should be of type '%s' indeed",
            columnName,
            tableName,
            realType,
            Name.MAP);

        Name expectedMapKeyType = toCQLType(pm.getKeyClass());
        Name realMapKeyType = columnMetadata.getType().getTypeArguments().get(0).getName();
        Name realMapValueType = columnMetadata.getType().getTypeArguments().get(1).getName();
        Validator.validateTableTrue(
            realMapKeyType == expectedMapKeyType,
            "Column %s' of table '%s' of type 'Map<%s,?>' should be of type 'Map<%s,?>' indeed",
            columnName,
            tableName,
            realMapKeyType,
            expectedMapKeyType);

        Validator.validateTableTrue(
            realMapValueType == expectedValueType,
            "Column '%s' of table '%s' of type 'Map<?,%s>' should be of type 'Map<?,%s>' indeed",
            columnName,
            tableName,
            realMapValueType,
            expectedValueType);
        break;
      default:
        break;
    }
  }