docs: public static String toSchema(Catalog catalog)
      {
          StringBuilder sb = new StringBuilder();
  
          sb.append("-- This file was generated by VoltDB version ");
          sb.append(VoltDB.instance().getVersionString());
          SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
          String time = sdf.format(System.currentTimeMillis());
          sb.append(" on: " + time + ".\n");
  
          sb.append("-- This file represents the current database schema.\n");
          sb.append("-- Use this file as input to reproduce the current database structure in another database instance.\n");
          sb.append("--\n");
  
          sb.append(batchSpecificComments);
  
          sb.append("-- If the schema declares Java stored procedures, be sure to load the .jar file\n");
          sb.append("-- with the classes before loading the schema. For example:\n");
          sb.append("--\n");
          sb.append("-- LOAD CLASSES voltdb-procs.jar;\n");
          sb.append("-- FILE ddl.sql;\n");
  
          for (Cluster cluster : catalog.getClusters()) {
              for (Database db : cluster.getDatabases()) {
                  for (Group grp : db.getGroups()) {
                      toSchema(sb, grp);
                  }
                  sb.append("\n");
  
                  List<Table> viewList = new ArrayList<>();
  
                  CatalogMap<Table> tables = db.getTables();
                  if (! tables.isEmpty()) {
                      sb.append(startBatch);
                      for (Table table : tables) {
                          Object annotation = table.getAnnotation();
                          if (annotation != null && ((TableAnnotation) annotation).ddl != null
                                  && table.getMaterializer() != null) {
                              viewList.add(table);
                              continue;
                          }
                          toSchema(sb, table, null, TableType.isStream(table.getTabletype()),
                                  (table.getPartitioncolumn() != null ? table.getPartitioncolumn().getName() : null), CatalogUtil.getExportTargetIfExportTableOrNullOtherwise(db, table));
                      }
                      // A View cannot precede a table that it depends on in the DDL
                      for (Table table : viewList) {
                          String viewQuery = ((TableAnnotation) table.getAnnotation()).ddl;
                          toSchema(sb, table, viewQuery, false, null, null);
                      }
                  }
  
                  CatalogMap<Procedure> procedures = db.getProcedures();
                  if (! procedures.isEmpty()) {
                      for (Procedure proc : procedures) {
                          toSchema(sb, proc);
                      }
                  }
  
                  CatalogMap<Function> functions = db.getFunctions();
                  if (! functions.isEmpty()) {
                      for (Function func : functions) {
                          toSchema(sb, func);
                      }
                  }
  
                  if (! tables.isEmpty()) {
                      sb.append(endBatch);
                  }
              }
          }
  
          if (dumpSchema) {
              String ts = new SimpleDateFormat("MMddHHmmssSSS").format(new Date());
              File f = new File(String.format("/tmp/canonical-%s.sql", ts));
              try {
                  FileWriter fw = new FileWriter(f);
                  fw.write(sb.toString());
                  fw.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
  
          return sb.toString();
      }