docs: static boolean compileStatementAndUpdateCatalog(VoltCompiler compiler, HSQLInterface hsql,
              Database db, DatabaseEstimates estimates,
              Statement catalogStmt, VoltXMLElement xml, String stmt, String joinOrder,
              DeterminismMode detMode, StatementPartitioning partitioning)
      throws VoltCompiler.VoltCompilerException {
  
          // Cleanup whitespace newlines for catalog compatibility
          // and to make statement parsing easier.
          stmt = stmt.replaceAll("\n", " ");
          stmt = stmt.trim();
          compiler.addInfo("Compiling Statement: " + stmt);
  
          // put the data in the catalog that we have
          if (!stmt.endsWith(";")) {
              stmt += ";";
          }
  
          // if this key + sql is the same, then a cached stmt can be used
          String keyPrefix = compiler.getKeyPrefix(partitioning, detMode, joinOrder);
          // if the key is cache-able, look for a previous statement
          if (keyPrefix != null) {
              Statement previousStatement = compiler.getCachedStatement(keyPrefix, stmt);
              // check if the stmt exists and if it's the same sql text
              if (previousStatement != null) {
                  if (m_logger.isDebugEnabled()) {
                      Procedure prevproc = (Procedure)previousStatement.getParent();
                      Procedure currproc = (Procedure)catalogStmt.getParent();
                      m_logger.debug(String.format("Recovering statement %s.%s from statement %s.%s\n",
                                                   currproc.getTypeName(),
                                                   catalogStmt.getTypeName(),
                                                   prevproc.getTypeName(),
                                                   previousStatement.getTypeName()));
                  }
                  catalogStmt.setAnnotation(previousStatement.getAnnotation());
                  catalogStmt.setAttachment(previousStatement.getAttachment());
                  catalogStmt.setCachekeyprefix(previousStatement.getCachekeyprefix());
                  catalogStmt.setCost(previousStatement.getCost());
                  catalogStmt.setExplainplan(previousStatement.getExplainplan());
                  catalogStmt.setIscontentdeterministic(previousStatement.getIscontentdeterministic());
                  catalogStmt.setIsorderdeterministic(previousStatement.getIsorderdeterministic());
                  catalogStmt.setNondeterminismdetail(previousStatement.getNondeterminismdetail());
                  catalogStmt.setQuerytype(previousStatement.getQuerytype());
                  catalogStmt.setReadonly(previousStatement.getReadonly());
                  catalogStmt.setReplicatedtabledml(previousStatement.getReplicatedtabledml());
                  catalogStmt.setSeqscancount(previousStatement.getSeqscancount());
                  catalogStmt.setSinglepartition(previousStatement.getSinglepartition());
                  catalogStmt.setSqltext(previousStatement.getSqltext());
                  catalogStmt.setTablesread(previousStatement.getTablesread());
                  catalogStmt.setTablesupdated(previousStatement.getTablesupdated());
                  catalogStmt.setIndexesused(previousStatement.getIndexesused());
                  copyUDFDependees(compiler, catalogStmt, previousStatement, db.getFunctions());
                  for (StmtParameter oldSp : previousStatement.getParameters()) {
                      StmtParameter newSp = catalogStmt.getParameters().add(oldSp.getTypeName());
                      newSp.setAnnotation(oldSp.getAnnotation());
                      newSp.setAttachment(oldSp.getAttachment());
                      newSp.setIndex(oldSp.getIndex());
                      newSp.setIsarray(oldSp.getIsarray());
                      newSp.setJavatype(oldSp.getJavatype());
                      newSp.setSqltype(oldSp.getSqltype());
                  }
  
                  for (PlanFragment oldFrag : previousStatement.getFragments()) {
                      PlanFragment newFrag = catalogStmt.getFragments().add(oldFrag.getTypeName());
                      newFrag.setAnnotation(oldFrag.getAnnotation());
                      newFrag.setAttachment(oldFrag.getAttachment());
                      newFrag.setHasdependencies(oldFrag.getHasdependencies());
                      newFrag.setMultipartition(oldFrag.getMultipartition());
                      newFrag.setNontransactional(oldFrag.getNontransactional());
                      newFrag.setPlanhash(oldFrag.getPlanhash());
                      newFrag.setPlannodetree(oldFrag.getPlannodetree());
                  }
  
                  return true;
              }
          }
  
  
          if (m_logger.isDebugEnabled()) {
              m_logger.debug(String.format("Compiling %s.%s: sql = \"%s\"\n",
                                           catalogStmt.getParent().getTypeName(),
                                           catalogStmt.getTypeName(),
                                           catalogStmt.getSqltext()));
          }
  
          // determine the type of the query
          QueryType qtype = QueryType.getFromSQL(stmt);
  
          catalogStmt.setReadonly(qtype.isReadOnly());
          catalogStmt.setQuerytype(qtype.getValue());
  
          // might be null if not cacheable
          catalogStmt.setCachekeyprefix(keyPrefix);
  
  
          catalogStmt.setSqltext(stmt);
          catalogStmt.setSinglepartition(partitioning.wasSpecifiedAsSingle());
  
          String name = catalogStmt.getParent().getTypeName() + "-" + catalogStmt.getTypeName();
          String sql = catalogStmt.getSqltext();
          String stmtName = catalogStmt.getTypeName();
          String procName = catalogStmt.getParent().getTypeName();
          TrivialCostModel costModel = new TrivialCostModel();
  
          CompiledPlan plan = null;
  
          try {
              // This try-with-resources block acquires a global lock on all planning
              // This is required until we figure out how to do parallel planning.
              try (QueryPlanner planner = new QueryPlanner(
                      sql, stmtName, procName,  db,
                      partitioning, hsql, estimates, false,
                      costModel, null, joinOrder, detMode, false)) {
                  if (xml != null) {
                      planner.parseFromXml(xml);
                  }
                  else {
                      planner.parse();
                  }
  
                  plan = planner.plan();
                  assert(plan != null);
              }
              catch (Exception e) {
                  // These are normal expectable errors -- don't normally need a stack-trace.
                  String msg = "Failed to plan for statement (" + catalogStmt.getTypeName() + ") \"" +
                          catalogStmt.getSqltext() + "\".";
                  if (e.getMessage() != null) {
                      msg += " Error: \"" + e.getMessage() + "\"";
                  }
                  throw compiler.new VoltCompilerException(msg);
              }
  
              // There is a hard-coded limit to the number of parameters that can be passed to the EE.
              if (plan.getParameters().length > CompiledPlan.MAX_PARAM_COUNT) {
                  throw compiler.new VoltCompilerException(
                      "The statement's parameter count " + plan.getParameters().length +
                      " must not exceed the maximum " + CompiledPlan.MAX_PARAM_COUNT);
              }
  
              // Check order and content determinism before accessing the detail which
              // it caches.
              boolean orderDeterministic = plan.isOrderDeterministic();
              catalogStmt.setIsorderdeterministic(orderDeterministic);
              boolean contentDeterministic = plan.isContentDeterministic()
                                             && (orderDeterministic || !plan.hasLimitOrOffset());
              catalogStmt.setIscontentdeterministic(contentDeterministic);
              String nondeterminismDetail = plan.nondeterminismDetail();
              catalogStmt.setNondeterminismdetail(nondeterminismDetail);
  
              catalogStmt.setSeqscancount(plan.countSeqScans());
  
              // Input Parameters
              // We will need to update the system catalogs with this new information
              for (int i = 0; i < plan.getParameters().length; ++i) {
                  StmtParameter catalogParam = catalogStmt.getParameters().add(String.valueOf(i));
                  catalogParam.setJavatype(plan.getParameters()[i].getValueType().getValue());
                  catalogParam.setIsarray(plan.getParameters()[i].getParamIsVector());
                  catalogParam.setIndex(i);
              }
  
              catalogStmt.setReplicatedtabledml(plan.replicatedTableDML);
  
              // output the explained plan to disk (or caller) for debugging
              StringBuilder planDescription = new StringBuilder(1000); // Initial capacity estimate.
              planDescription.append("SQL: ").append(plan.sql);
              // Only output the cost in debug mode.
              // Cost seems to only confuse people who don't understand how this number is used/generated.
              if (VoltCompiler.DEBUG_MODE) {
                  planDescription.append("\nCOST: ").append(plan.cost);
              }
              planDescription.append("\nPLAN:\n");
              planDescription.append(plan.explainedPlan);
              String planString = planDescription.toString();
              // only write to disk if compiler is in standalone mode
              if (compiler.standaloneCompiler) {
                  BuildDirectoryUtils.writeFile(null, name + ".txt", planString, false);
              }
              compiler.captureDiagnosticContext(planString);
  
              // build usage links for report generation and put them in the catalog
              CatalogUtil.updateUsageAnnotations(db, catalogStmt, plan.rootPlanGraph, plan.subPlanGraph);
  
              // set the explain plan output into the catalog (in hex) for reporting
              catalogStmt.setExplainplan(Encoder.hexEncode(plan.explainedPlan));
  
              // Add the UDF dependences.
              CatalogMap<Function> functions = db.getFunctions();
              for (String dependee : plan.getUDFDependees()) {
                  Function function = functions.get(dependee);
                  assert(function != null);
                  addUDFDependences(function, catalogStmt);
              }
              // compute a hash of the plan
              MessageDigest md = null;
              try {
                  md = MessageDigest.getInstance("SHA-1");
              }
              catch (NoSuchAlgorithmException e) {
                  e.printStackTrace();
                  assert(false);
                  System.exit(-1); // should never happen with healthy jvm
              }
  
              // Now update our catalog information
              PlanFragment planFragment = catalogStmt.getFragments().add("0");
              planFragment.setHasdependencies(plan.subPlanGraph != null);
              // mark a fragment as non-transactional if it never touches a persistent table
              planFragment.setNontransactional(!fragmentReferencesPersistentTable(plan.rootPlanGraph));
              planFragment.setMultipartition(plan.subPlanGraph != null);
              byte[] planBytes = writePlanBytes(compiler, planFragment, plan.rootPlanGraph);
              md.update(planBytes, 0, planBytes.length);
              // compute the 40 bytes of hex from the 20 byte sha1 hash of the plans
              md.reset();
              md.update(planBytes);
              planFragment.setPlanhash(Encoder.hexEncode(md.digest()));
  
              if (plan.subPlanGraph != null) {
                  planFragment = catalogStmt.getFragments().add("1");
                  planFragment.setHasdependencies(false);
                  planFragment.setNontransactional(false);
                  planFragment.setMultipartition(true);
                  byte[] subBytes = writePlanBytes(compiler, planFragment, plan.subPlanGraph);
                  // compute the 40 bytes of hex from the 20 byte sha1 hash of the plans
                  md.reset();
                  md.update(subBytes);
                  planFragment.setPlanhash(Encoder.hexEncode(md.digest()));
              }
  
              // Planner should have rejected with an exception any statement with an unrecognized type.
              int validType = catalogStmt.getQuerytype();
              assert(validType != QueryType.INVALID.getValue());
  
              return false;
          }
          catch (StackOverflowError error) {
              String msg = "Failed to plan for statement (" + catalogStmt.getTypeName() + ") \"" +
                      catalogStmt.getSqltext() + "\". Error: \"Encountered stack overflow error. " +
                      "Try reducing the number of predicate expressions in the query.\"";
              throw compiler.new VoltCompilerException(msg);
          }
      }