docs: public boolean processMVBasedQueryFix(StmtTableScan mvTableScan, Set<SchemaColumn> scanColumns, JoinNode joinTree,
              List<ParsedColInfo> displayColumns, List<ParsedColInfo> groupByColumns) {
  
          // Check valid cases first
          //@TODO
          if  ( ! (mvTableScan instanceof StmtTargetTableScan)) {
              return false;
          }
          Table table = ((StmtTargetTableScan)mvTableScan).getTargetTable();
          assert (table != null);
          String mvTableName = table.getTypeName();
          Table srcTable = table.getMaterializer();
          if (srcTable == null) {
              return false;
          }
          if (table.getIsreplicated()) {
              return false;
          }
  
          // Justify whether partition column is in group by column list or not
          if (table.getPartitioncolumn() != null) {
              return false;
          }
  
          m_mvTableScan = mvTableScan;
  
          Set<String> mvDDLGroupbyColumnNames = new HashSet<>();
          List<Column> mvColumnArray =
                  CatalogUtil.getSortedCatalogItems(table.getColumns(), "index");
  
          String mvTableAlias = getMVTableAlias();
  
          // Get the number of group-by columns.
          int numOfGroupByColumns;
          MaterializedViewInfo mvInfo = srcTable.getViews().get(mvTableName);
          if (mvInfo != null) {
              // single table view
              String complexGroupbyJson = mvInfo.getGroupbyexpressionsjson();
              if (complexGroupbyJson.length() > 0) {
                  List<AbstractExpression> mvComplexGroupbyCols = null;
                  try {
                      mvComplexGroupbyCols = AbstractExpression.fromJSONArrayString(complexGroupbyJson, null);
                  }
                  catch (JSONException e) {
                      e.printStackTrace();
                  }
                  numOfGroupByColumns = mvComplexGroupbyCols.size();
              }
              else {
                  numOfGroupByColumns = mvInfo.getGroupbycols().size();
              }
          }
          else {
              // joined table view
              MaterializedViewHandlerInfo mvHandlerInfo = table.getMvhandlerinfo().get("mvHandlerInfo");
              numOfGroupByColumns = mvHandlerInfo.getGroupbycolumncount();
          }
  
          if (scanColumns.isEmpty() && numOfGroupByColumns == 0) {
              // This is an edge case that can happen if the view
              // has no group by keys, and we are just
              // doing a count(*) on the output of the view.
              //
              // Having no GB keys or scan columns would cause us to
              // produce plan nodes that have a 0-column output schema.
              // We can't handle this in several places, so add the
              // count(*) column from the view to the scan columns.
              Column mvCol = mvColumnArray.get(0); // this is the "count(*)" column.
              TupleValueExpression tve = new TupleValueExpression(
                      mvTableName, mvTableAlias, mvCol, 0);
              tve.setOrigStmtId(mvTableScan.getStatementId());
  
              String colName = mvCol.getName();
              SchemaColumn scol = new SchemaColumn(mvTableName, mvTableAlias,
                      colName, colName, tve);
              scanColumns.add(scol);
          }
  
          // Start to do real materialized view processing to fix the duplicates problem.
          // (1) construct new projection columns for scan plan node.
          Set<SchemaColumn> mvDDLGroupbyColumns = new HashSet<>();
          NodeSchema inlineProjSchema = new NodeSchema();
          for (SchemaColumn scol: scanColumns) {
              inlineProjSchema.addColumn(scol);
          }
  
  
          for (int i = 0; i < numOfGroupByColumns; i++) {
              Column mvCol = mvColumnArray.get(i);
              String colName = mvCol.getName();
  
              TupleValueExpression tve = new TupleValueExpression(
                      mvTableName, mvTableAlias, mvCol, i);
              tve.setOrigStmtId(mvTableScan.getStatementId());
  
              mvDDLGroupbyColumnNames.add(colName);
  
              SchemaColumn scol = new SchemaColumn(mvTableName, mvTableAlias,
                      colName, colName, tve);
              mvDDLGroupbyColumns.add(scol);
              if (!scanColumns.contains(scol)) {
                  scanColumns.add(scol);
                  // construct new projection columns for scan plan node.
                  inlineProjSchema.addColumn(scol);
              }
          }
  
          // Record the re-aggregation type for each scan columns.
          Map<String, ExpressionType> mvColumnReAggType = new HashMap<>();
          for (int i = numOfGroupByColumns; i < mvColumnArray.size(); i++) {
              Column mvCol = mvColumnArray.get(i);
              ExpressionType reAggType = ExpressionType.get(mvCol.getAggregatetype());
  
              if (reAggType == ExpressionType.AGGREGATE_COUNT_STAR ||
                      reAggType == ExpressionType.AGGREGATE_COUNT) {
                  reAggType = ExpressionType.AGGREGATE_SUM;
              }
              mvColumnReAggType.put(mvCol.getName(), reAggType);
          }
  
          assert (inlineProjSchema.size() > 0);
          m_scanInlinedProjectionNode =
                  new ProjectionPlanNode(inlineProjSchema);
  
          // (2) Construct the reAggregation Node.
  
          // Construct the reAggregation plan node's aggSchema
          m_reAggNode = new HashAggregatePlanNode();
          int outputColumnIndex = 0;
          // inlineProjSchema contains the group by columns, while aggSchema may do not.
          NodeSchema aggSchema = new NodeSchema();
  
          // Construct reAggregation node's aggregation and group by list.
          for (SchemaColumn scol: inlineProjSchema) {
              if (mvDDLGroupbyColumns.contains(scol)) {
                  // Add group by expression.
                  m_reAggNode.addGroupByExpression(scol.getExpression());
              }
              else {
                  ExpressionType reAggType = mvColumnReAggType.get(scol.getColumnName());
                  assert(reAggType != null);
                  AbstractExpression agg_input_expr = scol.getExpression();
                  assert(agg_input_expr instanceof TupleValueExpression);
                  // Add aggregation information.
                  m_reAggNode.addAggregate(reAggType, false, outputColumnIndex, agg_input_expr);
              }
              aggSchema.addColumn(scol);
              outputColumnIndex++;
          }
  
          assert (aggSchema.size() > 0);
          m_reAggNode.setOutputSchema(aggSchema);
  
  
          // Collect all TVEs that need to be do re-aggregation in coordinator.
          List<TupleValueExpression> needReAggTVEs = new ArrayList<>();
          List<AbstractExpression> aggPostExprs = new ArrayList<>();
  
          for (int i = numOfGroupByColumns; i < mvColumnArray.size(); i++) {
              Column mvCol = mvColumnArray.get(i);
  
              TupleValueExpression tve = new TupleValueExpression(
                      mvTableName, mvTableAlias, mvCol, -1);
              tve.setOrigStmtId(mvTableScan.getStatementId());
  
              needReAggTVEs.add(tve);
          }
  
          collectReAggNodePostExpressions(joinTree, needReAggTVEs, aggPostExprs);
  
          AbstractExpression aggPostExpr = ExpressionUtil.combinePredicates(aggPostExprs);
          // Add post filters for the reAggregation node.
          m_reAggNode.setPostPredicate(aggPostExpr);
  
  
          // ENG-5386
          if (m_edgeCaseQueryNoFixNeeded &&
                  edgeCaseQueryNoFixNeeded(mvDDLGroupbyColumnNames, mvColumnReAggType, displayColumns, groupByColumns)) {
              return false;
          }
  
          m_needed = true;
          return true;
      }