package com.netflix.astyanax.cql.reads;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicReference;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.RegularStatement;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select.Builder;
import com.datastax.driver.core.querybuilder.Select.Where;
import com.netflix.astyanax.cql.schema.CqlColumnFamilyDefinitionImpl;
import com.netflix.astyanax.ddl.ColumnDefinition;
import com.netflix.astyanax.model.ColumnFamily;
import com.netflix.astyanax.serializers.AnnotatedCompositeSerializer;
import com.netflix.astyanax.serializers.AnnotatedCompositeSerializer.ComponentSerializer;
public class CFColumnQueryGen {
private AtomicReference<Session> sessionRef = new AtomicReference<Session>(null);
private final String keyspace;
private final CqlColumnFamilyDefinitionImpl cfDef;
private final String partitionKeyCol;
private final List<ColumnDefinition> clusteringKeyCols;
private final List<ColumnDefinition> regularCols;
private boolean isCompositeColumn = false;
private boolean isFlatTable = false;
private static final String BIND_MARKER = "?";
public CFColumnQueryGen(Session session, String keyspaceName, CqlColumnFamilyDefinitionImpl cfDefinition) {
this.keyspace = keyspaceName;
this.cfDef = cfDefinition;
this.sessionRef.set(session);
partitionKeyCol = cfDef.getPartitionKeyColumnDefinition().getName();
clusteringKeyCols = cfDef.getClusteringKeyColumnDefinitionList();
regularCols = cfDef.getRegularColumnDefinitionList();
isCompositeColumn = (clusteringKeyCols.size() > 1);
isFlatTable = (clusteringKeyCols.size() == 0);
}
private QueryGenCache<CqlColumnQueryImpl<?>> ColumnQueryWithClusteringKey = new QueryGenCache<CqlColumnQueryImpl<?>>(sessionRef) {
@Override
public Callable<RegularStatement> getQueryGen(final CqlColumnQueryImpl<?> columnQuery) {
return new Callable<RegularStatement>() {
@Override
public RegularStatement call() throws Exception {
if (clusteringKeyCols.size() != 1) {
throw new RuntimeException("Cannot use this query for this schema, clustetingKeyCols.size: " + clusteringKeyCols.size());
}
String valueColName = regularCols.get(0).getName();
return QueryBuilder.select()
.column(valueColName).ttl(valueColName).writeTime(valueColName)
.from(keyspace, cfDef.getName())
.where(eq(partitionKeyCol, BIND_MARKER))
.and(eq(clusteringKeyCols.get(0).getName(), BIND_MARKER));
}
};
}
@Override
public BoundStatement bindValues(PreparedStatement pStatement, CqlColumnQueryImpl<?> columnQuery) {
return pStatement.bind(columnQuery.getRowKey(), columnQuery.getColumnName());
}
};
private QueryGenCache<CqlColumnQueryImpl<?>> ColumnQueryWithCompositeColumn = new QueryGenCache<CqlColumnQueryImpl<?>>(sessionRef) {
@Override
public Callable<RegularStatement> getQueryGen(final CqlColumnQueryImpl<?> columnQuery) {
return new Callable<RegularStatement>() {
@Override
public RegularStatement call() throws Exception {
if (clusteringKeyCols.size() <= 1) {
throw new RuntimeException("Cannot use this query for this schema, clustetingKeyCols.size: " + clusteringKeyCols.size());
}
String valueColName = regularCols.get(0).getName();
ColumnFamily<?,?> cf = columnQuery.getCF();
AnnotatedCompositeSerializer<?> compSerializer = (AnnotatedCompositeSerializer<?>) cf.getColumnSerializer();
List<ComponentSerializer<?>> components = compSerializer.getComponents();
// select the individual columns as dictated by the no of component serializers
Builder select = QueryBuilder.select()
.column(valueColName).ttl(valueColName).writeTime(valueColName);
Where where = select.from(keyspace, cfDef.getName()).where(eq(partitionKeyCol, BIND_MARKER));
for (int index = 0; index<components.size(); index++) {
where.and(eq(clusteringKeyCols.get(index).getName(), BIND_MARKER));
}
return where;
}
};
}
@Override
public BoundStatement bindValues(PreparedStatement pStatement, CqlColumnQueryImpl<?> columnQuery) {
List<Object> values = new ArrayList<Object>();
values.add(columnQuery.getRowKey());
ColumnFamily<?,?> cf = columnQuery.getCF();
AnnotatedCompositeSerializer<?> compSerializer = (AnnotatedCompositeSerializer<?>) cf.getColumnSerializer();
List<ComponentSerializer<?>> components = compSerializer.getComponents();
Object columnName = columnQuery.getColumnName();
for (ComponentSerializer<?> component : components) {
values.add(component.getFieldValueDirectly(columnName));
}
return pStatement.bind(values.toArray());
}
};
private QueryGenCache<CqlColumnQueryImpl<?>> FlatTableColumnQuery = new QueryGenCache<CqlColumnQueryImpl<?>>(sessionRef) {
@Override
public Callable<RegularStatement> getQueryGen(final CqlColumnQueryImpl<?> columnQuery) {
return new Callable<RegularStatement>() {
@Override
public RegularStatement call() throws Exception {
if (clusteringKeyCols.size() != 0) {
throw new RuntimeException("Cannot use this query for this schema, clustetingKeyCols.size: " + clusteringKeyCols.size());
}
String columnNameString = (String)columnQuery.getColumnName();
return QueryBuilder.select()
.column(columnNameString).ttl(columnNameString).writeTime(columnNameString)
.from(keyspace, cfDef.getName())
.where(eq(partitionKeyCol, BIND_MARKER)); }
};
}
@Override
public BoundStatement bindValues(PreparedStatement pStatement, CqlColumnQueryImpl<?> columnQuery) {
return pStatement.bind(columnQuery.getRowKey());
}
};
public BoundStatement getQueryStatement(CqlColumnQueryImpl<?> columnQuery, boolean useCaching) {
if (isFlatTable) {
return FlatTableColumnQuery.getBoundStatement(columnQuery, useCaching);
}
if (isCompositeColumn) {
return ColumnQueryWithCompositeColumn.getBoundStatement(columnQuery, useCaching);
} else {
return ColumnQueryWithClusteringKey.getBoundStatement(columnQuery, useCaching);
}
}
}