public static RowProjector compile()

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);
    }