List<AliasedNode> aliasedNodes = statement.getSelect();
// Setup projected columns in Scan
SelectClauseVisitor selectVisitor = new SelectClauseVisitor(context, groupBy);
List<ExpressionProjector> projectedColumns = new ArrayList<ExpressionProjector>();
TableRef tableRef = context.getResolver().getTables().get(0);
PTable table = tableRef.getTable();
boolean isWildcard = false;
Scan scan = context.getScan();
int index = 0;
List<Expression> projectedExpressions = Lists.newArrayListWithExpectedSize(aliasedNodes.size());
List<byte[]> projectedFamilies = Lists.newArrayListWithExpectedSize(aliasedNodes.size());
for (AliasedNode aliasedNode : aliasedNodes) {
ParseNode node = aliasedNode.getNode();
// TODO: visitor?
if (node instanceof WildcardParseNode) {
if (statement.isAggregate()) {
ExpressionCompiler.throwNonAggExpressionInAggException(node.toString());
}
isWildcard = true;
if (tableRef.getTable().getType() == PTableType.INDEX && ((WildcardParseNode)node).isRewrite()) {
projectAllIndexColumns(context, tableRef, projectedExpressions, projectedColumns);
} else {
projectAllTableColumns(context, tableRef, projectedExpressions, projectedColumns);
}
} else if (node instanceof FamilyWildcardParseNode){
// Project everything for SELECT cf.*
// TODO: support cf.* expressions for multiple tables the same way with *.
String cfName = ((FamilyWildcardParseNode) node).getName();
// Delay projecting to scan, as when any other column in the column family gets
// added to the scan, it overwrites that we want to project the entire column
// family. Instead, we do the projection at the end.
// TODO: consider having a ScanUtil.addColumn and ScanUtil.addFamily to work
// around this, as this code depends on this function being the last place where
// columns are projected (which is currently true, but could change).
projectedFamilies.add(Bytes.toBytes(cfName));
if (tableRef.getTable().getType() == PTableType.INDEX && ((FamilyWildcardParseNode)node).isRewrite()) {
projectIndexColumnFamily(context, cfName, tableRef, projectedExpressions, projectedColumns);
} else {
projectTableColumnFamily(context, cfName, tableRef, projectedExpressions, projectedColumns);
}
} else {
Expression expression = node.accept(selectVisitor);
projectedExpressions.add(expression);
if (index < targetColumns.size()) {
PDatum targetColumn = targetColumns.get(index);
if (targetColumn.getDataType() != expression.getDataType()) {
PDataType targetType = targetColumn.getDataType();
// Check if coerce allowed using more relaxed isCastableTo check, since we promote INTEGER to LONG
// during expression evaluation and then convert back to INTEGER on UPSERT SELECT (and we don't have
// (an actual value we can specifically check against).
if (expression.getDataType() != null && !expression.getDataType().isCastableTo(targetType)) {
throw new ArgumentTypeMismatchException(targetType, expression.getDataType(), "column: " + targetColumn);
}
expression = CoerceExpression.create(expression, targetType);
}
}
if (node instanceof BindParseNode) {
context.getBindManager().addParamMetaData((BindParseNode)node, expression);
}
if (!node.isStateless()) {
if (!selectVisitor.isAggregate() && statement.isAggregate()) {
ExpressionCompiler.throwNonAggExpressionInAggException(expression.toString());
}
}
String columnAlias = aliasedNode.getAlias() != null ? aliasedNode.getAlias() : SchemaUtil.normalizeIdentifier(aliasedNode.getNode().getAlias());
boolean isCaseSensitive = (columnAlias != null && (aliasedNode.isCaseSensitve() || SchemaUtil.isCaseSensitive(columnAlias))) || selectVisitor.isCaseSensitive;
String name = columnAlias == null ? expression.toString() : columnAlias;
projectedColumns.add(new ExpressionProjector(name, table.getName().getString(), expression, isCaseSensitive));
}
selectVisitor.reset();
index++;
}
table = context.getCurrentTable().getTable(); // switch to current table for scan projection
// TODO make estimatedByteSize more accurate by counting the joined columns.
int estimatedKeySize = table.getRowKeySchema().getEstimatedValueLength();
int estimatedByteSize = 0;
for (Map.Entry<byte[],NavigableSet<byte[]>> entry : scan.getFamilyMap().entrySet()) {
PColumnFamily family = table.getColumnFamily(entry.getKey());
if (entry.getValue() == null) {
for (PColumn column : family.getColumns()) {
Integer byteSize = column.getByteSize();
estimatedByteSize += SizedUtil.KEY_VALUE_SIZE + estimatedKeySize + (byteSize == null ? RowKeySchema.ESTIMATED_VARIABLE_LENGTH_SIZE : byteSize);
}
} else {
for (byte[] cq : entry.getValue()) {
PColumn column = family.getColumn(cq);
Integer byteSize = column.getByteSize();
estimatedByteSize += SizedUtil.KEY_VALUE_SIZE + estimatedKeySize + (byteSize == null ? RowKeySchema.ESTIMATED_VARIABLE_LENGTH_SIZE : byteSize);
}
}
}
selectVisitor.compile();
// Since we don't have the empty key value in read-only tables,
// we must project everything.
boolean isProjectEmptyKeyValue = table.getType() != PTableType.VIEW && table.getViewType() != ViewType.MAPPED && !isWildcard;
if (isProjectEmptyKeyValue) {
for (byte[] family : projectedFamilies) {
projectColumnFamily(table, scan, family);
}
} else {