private void checkViewMeetsSpec(String viewName, ParsedSelectStmt stmt) throws VoltCompilerException {
        int groupColCount = stmt.groupByColumns().size();
        int displayColCount = stmt.m_displayColumns.size();
        StringBuffer msg = new StringBuffer();
        msg.append("Materialized view \"" + viewName + "\" ");

        if (stmt.getParameters().length > 0) {
            msg.append("contains placeholders (?), which are not allowed in the SELECT query for a view.");
            throw m_compiler.new VoltCompilerException(msg.toString());
        }

        List <AbstractExpression> checkExpressions = new ArrayList<>();
        int i;
        // First, check the group by columns.  They are at
        // the beginning of the display list.
        for (i = 0; i < groupColCount; i++) {
            ParsedColInfo gbcol = stmt.groupByColumns().get(i);
            ParsedColInfo outcol = stmt.m_displayColumns.get(i);
            // The columns must be equal.
            if (!outcol.m_expression.equals(gbcol.m_expression)) {
                msg.append("must exactly match the GROUP BY clause at index " + String.valueOf(i) + " of SELECT list.");
                throw m_compiler.new VoltCompilerException(msg.toString());
            }
            // check if the expression return type is not unique indexable
            StringBuffer exprMsg = new StringBuffer();
            if (!outcol.m_expression.isValueTypeUniqueIndexable(exprMsg)) {
                msg.append("with " + exprMsg + " in GROUP BY clause not supported.");
                throw m_compiler.new VoltCompilerException(msg.toString());
            }

            // collect all the expressions and we will check
            // for other guards on all of them together
            checkExpressions.add(outcol.m_expression);
        }

        // check for count star in the display list
        boolean countStarFound = false;

        UnsafeOperatorsForDDL unsafeOps = new UnsafeOperatorsForDDL();

        // Finally, the display columns must have aggregate
        // calls.  But these are not any aggregate calls. They
        // must be count(), min(), max() or sum().
        for (; i < displayColCount; i++) {
            ParsedColInfo outcol = stmt.m_displayColumns.get(i);
            // Note that this expression does not catch all aggregates.
            // An instance of avg() would cause the exception.
            // ENG-10945 - We can have count(*) anywhere after the group by columns and multiple count(*)(s)
            if ( outcol.m_expression.getExpressionType() == ExpressionType.AGGREGATE_COUNT_STAR) {
                if ( countStarFound == false )
                    countStarFound = true;
                continue;
            }

            if ((outcol.m_expression.getExpressionType() != ExpressionType.AGGREGATE_COUNT) &&
                    (outcol.m_expression.getExpressionType() != ExpressionType.AGGREGATE_SUM) &&
                    (outcol.m_expression.getExpressionType() != ExpressionType.AGGREGATE_MIN) &&
                    (outcol.m_expression.getExpressionType() != ExpressionType.AGGREGATE_MAX)) {
                msg.append("must have non-group by columns aggregated by sum, count, min or max.");
                throw m_compiler.new VoltCompilerException(msg.toString());
            }
            // Don't push the expression, though.  Push the argument.
            // We will check for aggregate calls and fail, and we don't
            // want to fail on legal aggregate expressions.
            if (outcol.m_expression.getLeft() != null) {
                checkExpressions.add(outcol.m_expression.getLeft());
            }
            // Check if the aggregation is safe for non-empty view source table.
            outcol.m_expression.findUnsafeOperatorsForDDL(unsafeOps);
            assert(outcol.m_expression.getRight() == null);
            assert(outcol.m_expression.getArgs() == null || outcol.m_expression.getArgs().size() == 0);
        }

        // Users can create SINGLE TABLE VIEWS without declaring count(*) in the stmt.
        // Multiple table views still need this restriction.
        if (stmt.m_tableList.size() > 1 && countStarFound == false) {
            msg.append("joins multiple tables, therefore must include COUNT(*) after any GROUP BY columns.");
            throw m_compiler.new VoltCompilerException(msg.toString());
        }

        AbstractExpression where = stmt.getSingleTableFilterExpression();
        if (where != null) {
            checkExpressions.add(where);
        }

        /*
         * Gather up all the join expressions.  The ParsedSelectStatement
         * has not been analyzed yet, so it's not clear where these are.  But
         * the stmt knows.
         */
        stmt.gatherJoinExpressions(checkExpressions);
        if (stmt.getHavingPredicate() != null) {
            checkExpressions.add(stmt.getHavingPredicate());
        }
        // Check all the subexpressions we gathered up.
        if (!AbstractExpression.validateExprsForIndexesAndMVs(checkExpressions, msg, true)) {
            // The error message will be in the StringBuffer msg.
            throw m_compiler.new VoltCompilerException(msg.toString());
        }

        // Check some other materialized view specific things.
        //
        // Check to see if the expression is safe for creating
        // views on nonempty tables.
        for (AbstractExpression expr : checkExpressions) {
            expr.findUnsafeOperatorsForDDL(unsafeOps);
        }
        if (unsafeOps.isUnsafe()) {
            stmt.setUnsafeDDLMessage(unsafeOps.toString());
        }

        if (stmt.hasSubquery()) {
            msg.append("cannot contain subquery sources.");
            throw m_compiler.new VoltCompilerException(msg.toString());
        }

        if (! stmt.m_joinTree.allInnerJoins()) {
            throw m_compiler.new VoltCompilerException("Materialized view only supports INNER JOIN.");
        }

        if (stmt.orderByColumns().size() != 0) {
            msg.append("with an ORDER BY clause is not supported.");
            throw m_compiler.new VoltCompilerException(msg.toString());
        }

        if (stmt.hasLimitOrOffset()) {
            msg.append("with a LIMIT or OFFSET clause is not supported.");
            throw m_compiler.new VoltCompilerException(msg.toString());
        }

        if (stmt.getHavingPredicate() != null) {
            msg.append("with a HAVING clause is not supported.");
            throw m_compiler.new VoltCompilerException(msg.toString());
        }

        // ENG-10892, since count(*) can be removed from SV table
        if ((stmt.m_tableList.size() > 1 && displayColCount <= groupColCount) ||
                displayColCount < groupColCount) {
            msg.append("has too few columns.");
            throw m_compiler.new VoltCompilerException(msg.toString());
        }

        checkViewSources(stmt.m_tableList);
     }
private void checkViewMeetsSpec ( String viewName , ParsedSelectStmt stmt ) throws VoltCompilerException { int groupColCount = stmt . groupByColumns ( ) . size ( ) ; int displayColCount = stmt . m_displayColumns . size ( ) ; StringBuffer msg = new StringBuffer ( ) ; msg . append ( "Materialized view \"" + viewName + "\" " ) ; if ( stmt . getParameters ( ) . length > 0 ) { msg . append ( "contains placeholders (?), which are not allowed in the SELECT query for a view." ) ; throw m_compiler . new VoltCompilerException ( msg . toString ( ) ) ; } List < AbstractExpression > checkExpressions = new ArrayList < > ( ) ; int i ; for ( i = 0 ; i < groupColCount ; i ++ ) { ParsedColInfo gbcol = stmt . groupByColumns ( ) . get ( i ) ; ParsedColInfo outcol = stmt . m_displayColumns . get ( i ) ; if ( ! outcol . m_expression . equals ( gbcol . m_expression ) ) { msg . append ( "must exactly match the GROUP BY clause at index " + String . valueOf ( i ) + " of SELECT list." ) ; throw m_compiler . new VoltCompilerException ( msg . toString ( ) ) ; } StringBuffer exprMsg = new StringBuffer ( ) ; if ( ! outcol . m_expression . isValueTypeUniqueIndexable ( exprMsg ) ) { msg . append ( "with " + exprMsg + " in GROUP BY clause not supported." ) ; throw m_compiler . new VoltCompilerException ( msg . toString ( ) ) ; } checkExpressions . add ( outcol . m_expression ) ; } boolean countStarFound = false ; UnsafeOperatorsForDDL unsafeOps = new UnsafeOperatorsForDDL ( ) ; for ( ; i < displayColCount ; i ++ ) { ParsedColInfo outcol = stmt . m_displayColumns . get ( i ) ; if ( outcol . m_expression . getExpressionType ( ) == ExpressionType . AGGREGATE_COUNT_STAR ) { if ( countStarFound == false ) countStarFound = true ; continue ; } if ( ( outcol . m_expression . getExpressionType ( ) != ExpressionType . AGGREGATE_COUNT ) && ( outcol . m_expression . getExpressionType ( ) != ExpressionType . AGGREGATE_SUM ) && ( outcol . m_expression . getExpressionType ( ) != ExpressionType . AGGREGATE_MIN ) && ( outcol . m_expression . getExpressionType ( ) != ExpressionType . AGGREGATE_MAX ) ) { msg . append ( "must have non-group by columns aggregated by sum, count, min or max." ) ; throw m_compiler . new VoltCompilerException ( msg . toString ( ) ) ; } if ( outcol . m_expression . getLeft ( ) != null ) { checkExpressions . add ( outcol . m_expression . getLeft ( ) ) ; } outcol . m_expression . findUnsafeOperatorsForDDL ( unsafeOps ) ; assert ( outcol . m_expression . getRight ( ) == null ) ; assert ( outcol . m_expression . getArgs ( ) == null || outcol . m_expression . getArgs ( ) . size ( ) == 0 ) ; } if ( stmt . m_tableList . size ( ) > 1 && countStarFound == false ) { msg . append ( "joins multiple tables, therefore must include COUNT(*) after any GROUP BY columns." ) ; throw m_compiler . new VoltCompilerException ( msg . toString ( ) ) ; } AbstractExpression where = stmt . getSingleTableFilterExpression ( ) ; if ( where != null ) { checkExpressions . add ( where ) ; } stmt . gatherJoinExpressions ( checkExpressions ) ; if ( stmt . getHavingPredicate ( ) != null ) { checkExpressions . add ( stmt . getHavingPredicate ( ) ) ; } if ( ! AbstractExpression . validateExprsForIndexesAndMVs ( checkExpressions , msg , true ) ) { throw m_compiler . new VoltCompilerException ( msg . toString ( ) ) ; } for ( AbstractExpression expr : checkExpressions ) { expr . findUnsafeOperatorsForDDL ( unsafeOps ) ; } if ( unsafeOps . isUnsafe ( ) ) { stmt . setUnsafeDDLMessage ( unsafeOps . toString ( ) ) ; } if ( stmt . hasSubquery ( ) ) { msg . append ( "cannot contain subquery sources." ) ; throw m_compiler . new VoltCompilerException ( msg . toString ( ) ) ; } if ( ! stmt . m_joinTree . allInnerJoins ( ) ) { throw m_compiler . new VoltCompilerException ( "Materialized view only supports INNER JOIN." ) ; } if ( stmt . orderByColumns ( ) . size ( ) != 0 ) { msg . append ( "with an ORDER BY clause is not supported." ) ; throw m_compiler . new VoltCompilerException ( msg . toString ( ) ) ; } if ( stmt . hasLimitOrOffset ( ) ) { msg . append ( "with a LIMIT or OFFSET clause is not supported." ) ; throw m_compiler . new VoltCompilerException ( msg . toString ( ) ) ; } if ( stmt . getHavingPredicate ( ) != null ) { msg . append ( "with a HAVING clause is not supported." ) ; throw m_compiler . new VoltCompilerException ( msg . toString ( ) ) ; } if ( ( stmt . m_tableList . size ( ) > 1 && displayColCount <= groupColCount ) || displayColCount < groupColCount ) { msg . append ( "has too few columns." ) ; throw m_compiler . new VoltCompilerException ( msg . toString ( ) ) ; } checkViewSources ( stmt . m_tableList ) ; }
Verify the materialized view meets our arcane rules about what can and can't
go in a materialized view. Throw hopefully helpful error messages when these
rules are inevitably borked.

@param viewName The name of the view being checked.
@param stmt The output from the parser describing the select statement that creates the view.
@throws VoltCompilerException