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