in phoenix-core-client/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java [383:645]
public static RowProjector compile(StatementContext context, SelectStatement statement,
GroupBy groupBy, List<? extends PDatum> targetColumns, Expression where,
boolean wildcardIncludesDynamicCols) throws SQLException {
List<KeyValueColumnExpression> serverParsedKVRefs = new ArrayList<>();
List<ProjectedColumnExpression> serverParsedProjectedColumnRefs = new ArrayList<>();
List<Expression> serverParsedKVFuncs = new ArrayList<>();
List<Expression> serverParsedOldFuncs = new ArrayList<>();
Map<Expression, Integer> serverParsedExpressionCounts = new HashMap<>();
List<AliasedNode> aliasedNodes = statement.getSelect();
// Setup projected columns in Scan
SelectClauseVisitor
selectVisitor =
new SelectClauseVisitor(context, groupBy, serverParsedKVRefs, serverParsedKVFuncs,
serverParsedExpressionCounts, serverParsedProjectedColumnRefs,
serverParsedOldFuncs, statement);
List<ExpressionProjector> projectedColumns = new ArrayList<>();
ColumnResolver resolver = context.getResolver();
TableRef tableRef = context.getCurrentTable();
PTable table = tableRef.getTable();
boolean resolveColumn = !tableRef.equals(resolver.getTables().get(0));
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());
}
if (tableRef == TableRef.EMPTY_TABLE_REF) {
throw new SQLExceptionInfo.Builder(SQLExceptionCode.NO_TABLE_SPECIFIED_FOR_WILDCARD_SELECT).build().buildException();
}
isWildcard = true;
if (tableRef.getTable().getType() == PTableType.INDEX && ((WildcardParseNode)node).isRewrite()) {
projectAllIndexColumns(context, tableRef, resolveColumn, projectedExpressions, projectedColumns, targetColumns);
} else {
projectAllTableColumns(context, tableRef, resolveColumn, projectedExpressions, projectedColumns, targetColumns);
}
} else if (node instanceof TableWildcardParseNode) {
TableName tName = ((TableWildcardParseNode) node).getTableName();
TableRef tRef = resolver.resolveTable(tName.getSchemaName(), tName.getTableName());
if (tRef.equals(tableRef)) {
isWildcard = true;
}
if (tRef.getTable().getType() == PTableType.INDEX && ((TableWildcardParseNode)node).isRewrite()) {
projectAllIndexColumns(context, tRef, true, projectedExpressions, projectedColumns, targetColumns);
} else {
projectAllTableColumns(context, tRef, true, projectedExpressions, projectedColumns, targetColumns);
}
} else if (node instanceof FamilyWildcardParseNode) {
if (tableRef == TableRef.EMPTY_TABLE_REF) {
throw new SQLExceptionInfo.Builder(SQLExceptionCode.NO_TABLE_SPECIFIED_FOR_WILDCARD_SELECT).build().buildException();
}
// Project everything for SELECT cf.*
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, resolveColumn, projectedExpressions, projectedColumns);
} else {
projectTableColumnFamily(context, cfName, tableRef, resolveColumn, projectedExpressions, projectedColumns);
}
} else {
if (node instanceof PhoenixRowTimestampParseNode) {
if (statement.isAggregate()) {
ExpressionCompiler.throwNonAggExpressionInAggException(node.toString());
}
}
Expression expression = node.accept(selectVisitor);
projectedExpressions.add(expression);
expression = coerceIfNecessary(index, targetColumns, expression);
if (node instanceof BindParseNode) {
context.getBindManager().addParamMetaData((BindParseNode)node, expression);
}
if (!node.isStateless()) {
if (!selectVisitor.isAggregate() && statement.isAggregate()) {
ExpressionCompiler.throwNonAggExpressionInAggException(expression.toString());
}
}
String tableName = tableRef.getTableAlias() == null ?
(table.getName() == null ?
"" :
table.getName().getString()) :
tableRef.getTableAlias();
String colName = SchemaUtil.normalizeIdentifier(aliasedNode.getNode().getAlias());
String name = colName == null ? expression.toString() : colName;
boolean isCaseSensitive = aliasedNode.getAlias() != null ?
aliasedNode.isCaseSensitve() :
(colName != null ?
SchemaUtil.isCaseSensitive(aliasedNode.getNode().getAlias()) :
selectVisitor.isCaseSensitive);
if (null != aliasedNode.getAlias()){
projectedColumns.add(new ExpressionProjector(name, aliasedNode.getAlias(), tableName, expression, isCaseSensitive));
} else {
projectedColumns.add(new ExpressionProjector(name, name, tableName, expression, isCaseSensitive));
}
}
selectVisitor.reset();
index++;
}
for (int i = serverParsedProjectedColumnRefs.size() - 1; i >= 0; i--) {
Expression expression = serverParsedProjectedColumnRefs.get(i);
Integer count = serverParsedExpressionCounts.get(expression);
if (count != 0) {
serverParsedKVRefs.remove(i);
serverParsedKVFuncs.remove(i);
serverParsedOldFuncs.remove(i);
}
}
if (serverParsedKVFuncs.size() > 0 && serverParsedKVRefs.size() > 0) {
String[]
scanAttributes =
new String[] { BaseScannerRegionObserverConstants.SPECIFIC_ARRAY_INDEX,
BaseScannerRegionObserverConstants.JSON_VALUE_FUNCTION,
BaseScannerRegionObserverConstants.JSON_QUERY_FUNCTION,
BaseScannerRegionObserverConstants.BSON_VALUE_FUNCTION};
Map<String, Class> attributeToFunctionMap = new HashMap<String, Class>() {{
put(scanAttributes[0], ArrayIndexFunction.class);
put(scanAttributes[1], JsonValueFunction.class);
put(scanAttributes[2], JsonQueryFunction.class);
put(scanAttributes[3], BsonValueFunction.class);
}};
// This map is to keep track of the positions that get swapped with rearranging
// the functions in the serialized data to server.
Map<Integer, Integer> initialToShuffledPositionMap = new HashMap<>();
Map<String, List<Expression>>
serverAttributeToFuncExpressionMap =
new HashMap<String, List<Expression>>() {{
for (String attribute : attributeToFunctionMap.keySet()) {
put(attribute, new ArrayList<>());
}
}};
Map<String, List<KeyValueColumnExpression>>
serverAttributeToKVExpressionMap =
new HashMap<String, List<KeyValueColumnExpression>>() {{
for (String attribute : attributeToFunctionMap.keySet()) {
put(attribute, new ArrayList<>());
}
}};
int counter = 0;
for (String attribute : scanAttributes) {
for (int i = 0; i < serverParsedKVFuncs.size(); i++) {
if (attributeToFunctionMap.get(attribute)
.isInstance(serverParsedKVFuncs.get(i))) {
initialToShuffledPositionMap.put(i, counter++);
serverAttributeToFuncExpressionMap.get(attribute)
.add(serverParsedKVFuncs.get(i));
serverAttributeToKVExpressionMap.get(attribute)
.add(serverParsedKVRefs.get(i));
}
}
}
for (Map.Entry<String, Class> entry : attributeToFunctionMap.entrySet()) {
if (serverAttributeToFuncExpressionMap.get(entry.getKey()).size() > 0) {
serializeServerParsedExpressionInformationAndSetInScan(context, entry.getKey(),
serverAttributeToFuncExpressionMap.get(entry.getKey()),
serverAttributeToKVExpressionMap.get(entry.getKey()));
}
}
KeyValueSchemaBuilder builder = new KeyValueSchemaBuilder(0);
for (Expression expression : serverParsedKVRefs) {
builder.addField(expression);
}
KeyValueSchema kvSchema = builder.build();
ValueBitSet arrayIndexesBitSet = ValueBitSet.newInstance(kvSchema);
builder = new KeyValueSchemaBuilder(0);
for (Expression expression : serverParsedKVFuncs) {
builder.addField(expression);
}
KeyValueSchema arrayIndexesSchema = builder.build();
Map<Expression, Expression> replacementMap = new HashMap<>();
for (int i = 0; i < serverParsedOldFuncs.size(); i++) {
Expression function = serverParsedKVFuncs.get(i);
replacementMap.put(serverParsedOldFuncs.get(i),
new ArrayIndexExpression(initialToShuffledPositionMap.get(i),
function.getDataType(), arrayIndexesBitSet, arrayIndexesSchema));
}
ReplaceArrayFunctionExpressionVisitor
visitor =
new ReplaceArrayFunctionExpressionVisitor(replacementMap);
for (int i = 0; i < projectedColumns.size(); i++) {
ExpressionProjector projector = projectedColumns.get(i);
projectedColumns.set(i,
new ExpressionProjector(projector.getName(), projector.getLabel(),
tableRef.getTableAlias() == null ?
(table.getName() == null ?
"" :
table.getName().getString()) :
tableRef.getTableAlias(),
projector.getExpression().accept(visitor),
projector.isCaseSensitive()));
}
}
boolean isProjectEmptyKeyValue = false;
// Don't project known/declared column families into the scan if we want to support
// surfacing dynamic columns in wildcard queries
if (isWildcard && !wildcardIncludesDynamicCols) {
projectAllColumnFamilies(table, scan);
} else {
isProjectEmptyKeyValue = where == null || LiteralExpression.isTrue(where) || where.requiresFinalEvaluation();
for (byte[] family : projectedFamilies) {
try {
if (table.getColumnFamily(family) != null) {
projectColumnFamily(table, scan, family);
}
} catch (ColumnFamilyNotFoundException e) {
if (!IndexUtil.shouldIndexBeUsedForUncoveredQuery(tableRef)) {
throw e;
}
}
}
}
// 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()) {
try {
PColumnFamily family = table.getColumnFamily(entry.getKey());
if (entry.getValue() == null) {
for (PColumn column : family.getColumns()) {
Integer maxLength = column.getMaxLength();
int byteSize = column.getDataType().isFixedWidth() ? maxLength == null ? column.getDataType().getByteSize() : maxLength : RowKeySchema.ESTIMATED_VARIABLE_LENGTH_SIZE;
estimatedByteSize += SizedUtil.KEY_VALUE_SIZE + estimatedKeySize + byteSize;
}
} else {
for (byte[] cq : entry.getValue()) {
PColumn column = family.getPColumnForColumnQualifier(cq);
// Continue: If an EMPTY_COLUMN is in the projection list,
// since the table column list does not contain the EMPTY_COLUMN
// no value is returned.
if (column == null) {
continue;
}
Integer maxLength = column.getMaxLength();
int byteSize = column.getDataType().isFixedWidth() ? maxLength == null ? column.getDataType().getByteSize() : maxLength : RowKeySchema.ESTIMATED_VARIABLE_LENGTH_SIZE;
estimatedByteSize += SizedUtil.KEY_VALUE_SIZE + estimatedKeySize + byteSize;
}
}
} catch (ColumnFamilyNotFoundException e) {
// Ignore as this can happen for local indexes when the data table has a column family, but there are no covered columns in the family
}
}
return new RowProjector(projectedColumns, Math.max(estimatedKeySize, estimatedByteSize),
isProjectEmptyKeyValue, resolver.hasUDFs(), isWildcard,
wildcardIncludesDynamicCols);
}