public interface EdgeVersionDao extends RichVersionDao<EdgeVersion> {

  @Override
  EdgeVersion create(EdgeVersion nodeVersion, List<Long> parentIds) throws GroundException;

  @Override
  EdgeVersion retrieveFromDatabase(long id) throws GroundException;

  @Override
  default Class<EdgeVersion> getType() {
    return EdgeVersion.class;
  }
} public class GroundException extends Exception {

  private static final long serialVersionUID = 1L;

  private final String message;
  private final ExceptionType exceptionType;

  public enum ExceptionType {
    DB("Database Exception:", "%s"),
    ITEM_NOT_FOUND("GroundItemNotFoundException", "No %s \'%s\' found."),
    VERSION_NOT_FOUND("GroundVersionNotFoundException", "No %s \'%s\' found."),
    ITEM_ALREADY_EXISTS("GroundItemAlreadyExistsException", "%s %s already exists."),
    OTHER("GroundException", "%s");

    String name;
    String description;

    ExceptionType(String name, String description) {
      this.name = name;
      this.description = description;
    }

    public String format(String... values) {
      return String.format(this.description, values);
    }
  }

  public GroundException(ExceptionType exceptionType, String... values) {
    this.exceptionType = exceptionType;
    this.message = this.exceptionType.format(values);
  }

  public GroundException(Exception exception) {
    this.exceptionType = ExceptionType.OTHER;
    this.message = this.exceptionType.format(exception.getMessage());
  }

  @Override
  public String getMessage() {
    return this.message;
  }

  public ExceptionType getExceptionType() {
    return this.exceptionType;
  }
} public enum ExceptionType {
  DB("Database Exception:", "%s"),
  ITEM_NOT_FOUND("GroundItemNotFoundException", "No %s \'%s\' found."),
  VERSION_NOT_FOUND("GroundVersionNotFoundException", "No %s \'%s\' found."),
  ITEM_ALREADY_EXISTS("GroundItemAlreadyExistsException", "%s %s already exists."),
  OTHER("GroundException", "%s");

  String name;
  String description;

  ExceptionType(String name, String description) {
    this.name = name;
    this.description = description;
  }

  public String format(String... values) {
    return String.format(this.description, values);
  }
} public class Edge extends Item {

  // the name of this Edge
  @JsonProperty("name")
  private final String name;

  // the id of the Node that this EdgeVersion originates from
  @JsonProperty("fromNodeId")
  private final long fromNodeId;

  // the id of the Node that this EdgeVersion points to
  @JsonProperty("toNodeId")
  private final long toNodeId;

  // the source key for this Edge
  @JsonProperty("sourceKey")
  private final String sourceKey;

  /**
   * Construct a new Edge.
   *
   * @param id the edge id
   * @param name the edge name
   * @param sourceKey the user-generated unique key for the edge
   * @param fromNodeId the source node of this edge
   * @param toNodeId the destination node of this edge
   * @param tags the tags associated with this edge
   */
  @JsonCreator
  public Edge(@JsonProperty("itemId") long id,
               @JsonProperty("name") String name,
               @JsonProperty("sourceKey") String sourceKey,
               @JsonProperty("fromNodeId") long fromNodeId,
               @JsonProperty("toNodeId") long toNodeId,
               @JsonProperty("tags") Map<String, Tag> tags) {

    super(id, tags);

    this.name = name;
    this.fromNodeId = fromNodeId;
    this.toNodeId = toNodeId;
    this.sourceKey = sourceKey;
  }

  public Edge(long id, Edge other) {
    super(id, other.getTags());

    this.name = other.name;
    this.fromNodeId = other.fromNodeId;
    this.toNodeId = other.toNodeId;
    this.sourceKey = other.sourceKey;
  }

  public String getName() {
    return this.name;
  }

  public long getFromNodeId() {
    return this.fromNodeId;
  }

  public long getToNodeId() {
    return this.toNodeId;
  }

  public String getSourceKey() {
    return this.sourceKey;
  }

  @Override
  public boolean equals(Object other) {
    if (!(other instanceof Edge)) {
      return false;
    }

    Edge otherEdge = (Edge) other;

    return this.name.equals(otherEdge.name)
             && this.sourceKey.equals(otherEdge.sourceKey)
             && this.getId() == otherEdge.getId()
             && this.fromNodeId == otherEdge.fromNodeId
             && this.toNodeId == otherEdge.toNodeId
             && this.getTags().equals(otherEdge.getTags());
  }
} public class RichVersion extends Version {

  // the map of Keys to Tags associated with this RichVersion
  @JsonProperty("tags")
  private final Map<String, Tag> tags;

  // the StructureVersion associated with this RichVersion
  @JsonProperty("structureVersionId")
  private final Long structureVersionId;

  // the optional reference associated with this RichVersion
  @JsonProperty("reference")
  private final String reference;

  // the optional parameters associated with this RichVersion if there is a reference
  @JsonProperty("parameters")
  private final Map<String, String> parameters;

  /**
   * Create a new rich version.
   *
   * @param id the id of the version
   * @param tags the tags associated with this version
   * @param structureVersionId the id of the StructureVersion associated with this version
   * @param reference an optional external reference
   * @param referenceParameters the access parameters for the reference
   */
  @JsonCreator
  public RichVersion(
                      @JsonProperty("id") long id,
                      @JsonProperty("tags") Map<String, Tag> tags,
                      @JsonProperty("structureVersionId") Long structureVersionId,
                      @JsonProperty("reference") String reference,
                      @JsonProperty("parameters") Map<String, String> referenceParameters) {

    super(id);

    if (tags == null) {
      this.tags = new HashMap<>();
    } else {
      this.tags = tags;
    }

    if (structureVersionId == null || structureVersionId <= 0) {
      this.structureVersionId = -1L;
    } else {
      this.structureVersionId = structureVersionId;
    }

    if (reference == null || reference.equals("null")) {
      this.reference = null;
    } else {
      this.reference = reference;
    }

    if (referenceParameters == null) {
      this.parameters = new HashMap<>();
    } else {
      this.parameters = referenceParameters;
    }
  }

  public RichVersion(long id, RichVersion other) {
    super(id);

    this.tags = other.tags;
    this.structureVersionId = other.structureVersionId;
    this.reference = other.reference;
    this.parameters = other.parameters;
  }

  public Map<String, Tag> getTags() {
    return this.tags;
  }

  public Long getStructureVersionId() {
    return this.structureVersionId;
  }

  public String getReference() {
    return this.reference;
  }

  public Map<String, String> getParameters() {
    return this.parameters;
  }

  @Override
  public boolean equals(Object other) {
    if (!(other instanceof RichVersion)) {
      return false;
    }

    RichVersion otherRichVersion = (RichVersion) other;

    return this.getId() == otherRichVersion.getId()
             && this.tags.equals(otherRichVersion.tags)
             && this.structureVersionId == otherRichVersion.structureVersionId
             && Objects.equals(this.reference, otherRichVersion.reference)
             && Objects.equals(this.parameters, otherRichVersion.parameters);
  }
} public class VersionHistoryDag {

  // the id of the Version that's at the rootId of this DAG
  private final long itemId;

  // list of VersionSuccessors that make up this DAG
  private final List<Long> edgeIds;

  // map of parents to children
  private final Map<Long, List<Long>> parentChildMap;

  /**
   * Create a new version history DAG.
   *
   * @param itemId the id of the item of this DAG
   * @param edges the version successors in this DAG
   */
  public VersionHistoryDag(long itemId, List<VersionSuccessor> edges) {
    this.itemId = itemId;
    this.edgeIds = edges.stream().map(VersionSuccessor::getId).collect(Collectors.toList());
    this.parentChildMap = new HashMap<>();

    edges.forEach(edge -> this.addToParentChildMap(edge.getFromId(), edge.getToId()));
  }

  public long getItemId() {
    return this.itemId;
  }

  public List<Long> getEdgeIds() {
    return this.edgeIds;
  }

  /**
   * Checks if a given ID is in the DAG.
   *
   * @param id the ID to be checked
   * @return true if id is in the DAG, false otherwise
   */
  public boolean checkItemInDag(long id) {
    return this.parentChildMap.keySet().contains(id) || this.getLeaves().contains(id);
  }

  /**
   * Adds an edge to this DAG.
   *
   * @param parentId the id of the "from" of the edge
   * @param childId the id of the "to" of the edge
   */
  public void addEdge(long parentId, long childId, long successorId) {
    this.edgeIds.add(successorId);
    this.addToParentChildMap(parentId, childId);
  }

  /**
   * Return the parent(s) of a particular version.
   *
   * @param childId the query id
   * @return the list of parent version(s)
   */
  public List<Long> getParent(long childId) {
    return this.parentChildMap
             .entrySet()
             .stream()
             .filter(entry -> entry.getValue().contains(childId))
             .map(Map.Entry::getKey)
             .collect(Collectors.toList());
  }

  public Map<Long, Long> getParentChildPairs() {
    Map<Long, Long> result = new HashMap<>();

    for (Long parent : this.parentChildMap.keySet()) {
      List<Long> children = this.parentChildMap.get(parent);

      for (Long child : children) {
        result.put(parent, child);
      }
    }

    return result;
  }

  /**
   * Returns the leaves of the DAG (i.e., any version id that is not a parent of another version
   * id).
   *
   * @return the list of the IDs of the leaves of this DAG
   */
  public List<Long> getLeaves() {
    Set<Long> leaves = new HashSet<>();
    this.parentChildMap.values().forEach(leaves::addAll);
    leaves.removeAll(this.parentChildMap.keySet());

    return new ArrayList<>(leaves);
  }

  private void addToParentChildMap(long parent, long child) {
    List<Long> childList = this.parentChildMap.computeIfAbsent(parent, key -> new ArrayList<>());
    childList.add(child);
  }
} @Singleton
public class IdGenerator {

  private final long prefix;
  private long versionCounter;
  private long successorCounter;
  private long itemCounter;

  // If true, only one counter will be used. If false, all three counters will be used.
  private final boolean globallyUnique;

  public IdGenerator() {
    this.prefix = 0;
    this.versionCounter = 1;
    this.successorCounter = 1;
    this.itemCounter = 1;
    this.globallyUnique = true;
  }

  /**
   * Create a unique id generator.
   *
   * @param machineId the id of this machine
   * @param numMachines the total number of machines
   * @param globallyUnique if true, only one counter will be used for all version
   */
  public IdGenerator(long machineId, long numMachines, boolean globallyUnique) {
    long machineBits = 1;
    long fence = 2;

    while (fence < numMachines) {
      fence = fence * 2;
      machineBits++;
    }

    this.prefix = machineId << (64 - machineBits);

    // NOTE: Do not change this. The version counter is set to start a 1 because 0 is the default
    // empty version.
    this.versionCounter = 1;
    this.successorCounter = 1;
    this.itemCounter = 1;

    this.globallyUnique = globallyUnique;
  }

  public synchronized long generateVersionId() {
    return prefix | this.versionCounter++;
  }

  /**
   * Generate an id for version successors.
   *
   * @return a new id
   */
  public synchronized long generateSuccessorId() {
    if (this.globallyUnique) {
      return prefix | this.versionCounter++;
    } else {
      return prefix | this.successorCounter++;
    }
  }

  /**
   * Generate an id for items.
   *
   * @return a new id
   */
  public synchronized long generateItemId() {
    if (this.globallyUnique) {
      return prefix | this.versionCounter++;
    } else {
      return prefix | this.itemCounter++;
    }
  }
} public class SqlConstants {

  /* General insert statements */
  public static final String INSERT_GENERIC_ITEM_WITH_NAME = "INSERT INTO %s (item_id, source_key, name) VALUES (%d, \'%s\', \'%s\');";
  public static final String INSERT_GENERIC_ITEM_WITHOUT_NAME = "INSERT INTO %s (item_id, source_key, name) VALUES (%d, \'%s\', null);";

  /* General select statements */
  public static final String SELECT_STAR_BY_SOURCE_KEY = "SELECT * FROM %s WHERE source_key = \'%s\'";
  public static final String SELECT_STAR_ITEM_BY_ID = "SELECT * FROM %s WHERE item_id = %d;";
  public static final String SELECT_STAR_BY_ID = "SELECT * FROM %s WHERE id = %d;";
  public static final String DELETE_BY_ID = "DELETE FROM %s WHERE id = %d";

  /* Version-specific statements */
  public static final String INSERT_VERSION = "INSERT INTO version (id) VALUES (%d);";

  /* Version Successor-specific statements */
  public static final String INSERT_VERSION_SUCCESSOR = "INSERT INTO version_successor (id, from_version_id, to_version_id) VALUES (%d, %d, %d);";
  public static final String SELECT_VERSION_SUCCESSOR = "SELECT * FROM version_successor where id = %d;";
  public static final String SELECT_VERSION_SUCCESSOR_BY_ENDPOINT = "SELECT * FROM version_successor WHERE to_version_id = %d;";
  public static final String DELETE_VERSION_SUCCESSOR = "DELETE FROM version_successor WHERE id = %d;";

  /* Version History DAG-specific statements */
  public static final String INSERT_VERSION_HISTORY_DAG_EDGE = "INSERT INTO version_history_dag (item_id, version_successor_id) VALUES (%d, %d);";
  public static final String SELECT_VERSION_HISTORY_DAG = "SELECT * FROM version_history_dag WHERE item_id = %d;";
  public static final String DELETE_SUCCESSOR_FROM_DAG = "DELETE FROM version_history_dag WHERE version_successor_id = %d;";

  /* Item-specific statements */
  public static final String INSERT_ITEM = "INSERT INTO ITEM (id) VALUES (%d);";
  public static final String INSERT_ITEM_TAG_WITH_VALUE =
    "INSERT INTO item_tag (item_id, key, value, type) VALUES (%d, " + "\'%s\', \'%s\', \'%s\');";
  public static final String INSERT_ITEM_TAG_NO_VALUE = "INSERT INTO item_tag (item_id, key, value, type) VALUES (%d, \'%s\', null, null);";
  public static final String SELECT_ITEM_TAGS = "SELECT * FROM item_tag WHERE item_id = %d;";
  public static final String SELECT_ITEM_TAGS_BY_KEY = "SELECT * FROM item_tag WHERE key = \'%s\';";

  /* Edge-specific statements */
  public static final String INSERT_EDGE_WITH_NAME =
    "INSERT INTO edge (item_id, source_key, from_node_id, to_node_id, name) VALUES (%d, \'%s\', %d, %d, \'%s\');";
  public static final String INSERT_EDGE_WITHOUT_NAME =
    "INSERT INTO edge (item_id, source_key, from_node_id, to_node_id, name) VALUES (%d, \'%s\', %d, %d, null);";
  public static final String INSERT_EDGE_VERSION = "INSERT INTO edge_version (id, edge_id, from_node_version_start_id, from_node_version_end_id, "
                                                     + "to_node_version_start_id, to_node_version_end_id) VALUES (%d, %d, %d, %d, %d, %d);";
  public static final String UPDATE_EDGE_VERSION = "UPDATE edge_version SET from_node_version_end_id = %d, to_node_version_end_id = %d WHERE id = "
                                                     + "%d;";

  /* Graph-specific statements */
  public static final String INSERT_GRAPH_VERSION = "INSERT INTO graph_version (id, graph_id) VALUES (%d, %d);";
  public static final String INSERT_GRAPH_VERSION_EDGE = "INSERT INTO graph_version_edge (graph_version_id, edge_version_id) VALUES (%d, %d);";
  public static final String SELECT_GRAPH_VERSION_EDGES = "SELECT * FROM graph_version_edge WHERE graph_version_id = %d;";
  public static final String DELETE_ALL_GRAPH_VERSION_EDGES = "DELETE FROM %s WHERE %s_version_id = %d";

  /* Node-specific statements */
  public static final String INSERT_NODE_VERSION = "INSERT INTO node_version (id, node_id) VALUES (%d, %d);";
  public static final String SELECT_NODE_VERSION_ADJACENT_LINEAGE = "SELECT * FROM lineage_edge_version WHERE from_rich_version_id = %d;";

  /* Rich Version-specific statements */
  public static final String INSERT_RICH_VERSION_WITH_REFERENCE = "INSERT INTO rich_version (id, structure_version_id, reference) VALUES (%d, %d, "
                                                                    + "\'%s\');";
  public static final String INSERT_RICH_VERSION_WITHOUT_REFERENCE = "INSERT INTO rich_version (id, structure_version_id, reference) VALUES (%d, %d, "
                                                                       + "null);";
  public static final String INSERT_RICH_VERSION_TAG_WITH_VALUE = "INSERT INTO rich_version_tag (rich_version_id, key, value, type) VALUES (%d, "
                                                                    + "\'%s\', \'%s\', \'%s\');";
  public static final String INSERT_RICH_VERSION_TAG_NO_VALUE = "INSERT INTO rich_version_tag (rich_version_id, key, value, type) VALUES (%d, "
                                                                  + "\'%s\', null, null);";
  public static final String INSERT_RICH_VERSION_EXTERNAL_PARAMETER = "INSERT INTO rich_version_external_parameter (rich_version_id, key, value) "
                                                                        + "VALUES (%d, \'%s\', \'%s\');";
  public static final String SELECT_RICH_VERSION_EXTERNAL_PARAMETERS = "SELECT * FROM rich_version_external_parameter WHERE rich_version_id = %d;";
  public static final String SELECT_RICH_VERSION_TAGS = "SELECT * FROM rich_version_tag WHERE rich_version_id = %d;";
  public static final String SELECT_RICH_VERSION_TAGS_BY_KEY = "SELECT * FROM rich_version_tag WHERE key = \'%s\';";
  public static final String DELETE_RICH_VERSION_TAGS = "DELETE FROM rich_version_tag WHERE rich_version_id = %d";
  public static final String DELETE_RICH_EXTERNAL_PARAMETERS = "DELETE FROM rich_version_external_parameter WHERE rich_version_id = %d";

  /* Structure-specific statements */
  public static final String INSERT_STRUCTURE_VERSION = "INSERT INTO structure_version (id, structure_id) VALUES (%d, %d);";
  public static final String INSERT_STRUCTURE_VERSION_ATTRIBUTE = "INSERT INTO structure_version_attribute (structure_version_id, key, type) "
                                                                    + "VALUES (%d, \'%s\', \'%s\');";
  public static final String SELECT_STRUCTURE_VERSION_ATTRIBUTES = "SELECT * FROM structure_version_attribute WHERE structure_version_id = %d;";
  public static final String DELETE_STRUCTURE_VERSION_ATTRIBUTES = "DELETE FROM structure_version_attribute WHERE structure_version_id = %d";

  /* Lineage Edge-specific statements */
  public static final String INSERT_LINEAGE_EDGE_VERSION = "INSERT INTO lineage_edge_version (id, lineage_edge_id, from_rich_version_id, "
                                                             + "to_rich_version_id, principal_id) VALUES (%d, %d, %d, %d, %d);";

  /* Lineage Graph-specific statements */
  public static final String INSERT_LINEAGE_GRAPH_VERSION = "INSERT INTO lineage_graph_version (id, lineage_graph_id) VALUES (%d, %d);";
  public static final String INSERT_LINEAGE_GRAPH_VERSION_EDGE = "INSERT INTO lineage_graph_version_edge (lineage_graph_version_id, "
                                                                   + "lineage_edge_version_id) VALUES (%d, %d);";
  public static final String SELECT_LINEAGE_GRAPH_VERSION_EDGES = "SELECT * FROM lineage_graph_version_edge WHERE lineage_graph_version_id = %d;";
} public class PostgresVersionHistoryDagDao implements VersionHistoryDagDao {

  private PostgresVersionSuccessorDao postgresVersionSuccessorDao;
  private Database dbSource;
  private IdGenerator idGenerator;

  public PostgresVersionHistoryDagDao(Database dbSource, IdGenerator idGenerator) {
    this.dbSource = dbSource;
    this.idGenerator = idGenerator;

    this.postgresVersionSuccessorDao = new PostgresVersionSuccessorDao(this.dbSource, this.idGenerator);
  }

  @Override
  public VersionHistoryDag create(long itemId) throws GroundException {
    return new VersionHistoryDag(itemId, new ArrayList<>());
  }

  /**
   * Retrieve a DAG from the database.
   *
   * @param itemId the id of the item whose dag we are retrieving
   * @return the retrieved DAG
   * @throws GroundException an error retrieving the DAG
   */
  @Override
  public VersionHistoryDag retrieveFromDatabase(long itemId) throws GroundException {
    String sql = String.format(SqlConstants.SELECT_VERSION_HISTORY_DAG, itemId);

    List<VersionSuccessor> edges = new ArrayList<>();
    try (Connection con = dbSource.getConnection()) {

      Statement stmt = con.createStatement();
      final ResultSet resultSet = stmt.executeQuery(sql);

      List<Long> successors = new ArrayList<>();
      while (resultSet.next()) {
        successors.add(resultSet.getLong("version_successor_id"));
      }

      stmt.close();
      con.close();

      for (Long versionSuccessorId : successors) {
        VersionSuccessor versionSuccessor = postgresVersionSuccessorDao.retrieveFromDatabase(versionSuccessorId);
        edges.add(versionSuccessor);
      }
    } catch (Exception e) {
      throw new GroundException(e);
    }
    return new VersionHistoryDag(itemId, edges);
  }

  /**
   * Add an edge to the DAG.
   *
   * @param dag the DAG to update
   * @param parentId the parent's id
   * @param childId the child's id
   * @param itemId the id of the Item whose DAG we're updating
   * @throws GroundException an error adding the edge
   */
  @Override
  public PostgresStatements addEdge(VersionHistoryDag dag, long parentId, long childId, long itemId) throws GroundException {
    VersionSuccessor successor = this.postgresVersionSuccessorDao.instantiateVersionSuccessor(parentId, childId);
    dag.addEdge(parentId, childId, successor.getId());

    PostgresStatements statements = postgresVersionSuccessorDao.insert(successor);
    statements.append(String.format(SqlConstants.INSERT_VERSION_HISTORY_DAG_EDGE, itemId, successor.getId()));
    return statements;
  }


  /**
   * Truncate the DAG to only have a certain number of levels, removing everything before that.
   *
   * @param dag the DAG to truncate
   * @param numLevels the number of levels to keep
   */
  @Override
  public void truncate(VersionHistoryDag dag, int numLevels, Class<? extends Item> itemType) throws GroundException {

    int keptLevels = 1;
    List<Long> lastLevel = new ArrayList<>();
    List<Long> previousLevel = dag.getLeaves();

    while (keptLevels <= numLevels) {
      List<Long> currentLevel = new ArrayList<>();

      previousLevel.forEach(id -> currentLevel.addAll(dag.getParent(id)));

      lastLevel = previousLevel;
      previousLevel = currentLevel;

      keptLevels++;
    }

    List<Long> deleteQueue = new ArrayList<>(new HashSet<>(previousLevel));
    Set<Long> deleted = new HashSet<>();

    PostgresStatements statements = new PostgresStatements();

    // delete the version successors between the last kept level and the first deleted level
    for (long id : lastLevel) {
      this.postgresVersionSuccessorDao.deleteFromDestination(statements, id, dag.getItemId());
      this.addEdge(dag, 0, id, dag.getItemId());
    }

    while (deleteQueue.size() > 0) {
      long id = deleteQueue.get(0);

      if (id != 0) {
        this.postgresVersionSuccessorDao.deleteFromDestination(statements, id, dag.getItemId());
        GroundUtils.getVersionDaoFromItemType(itemType, this.dbSource, this.idGenerator).delete(id);

        deleted.add(id);
        List<Long> parents = dag.getParent(id);

        parents.forEach(parentId -> {
          if (!deleted.contains(parentId)) {
            deleteQueue.add(parentId);
          }
        });
      }

      deleteQueue.remove(0);
    }

    for (long id : lastLevel) {
      statements.merge(this.addEdge(dag, 0, id, dag.getItemId()));
    }

    PostgresUtils.executeSqlList(dbSource, statements);
  }
} public class PostgresStatements implements DbStatements<String> {

  List<String> statements;

  public PostgresStatements() {
    this.statements = new ArrayList<>();
  }

  public PostgresStatements(List<String> statements) {
    this.statements = statements;
  }

  @Override
  public void append(String statement) {
    this.statements.add(statement);
  }

  @Override
  public void merge(DbStatements other) {
    this.statements.addAll(other.getAllStatements());
  }

  @Override
  public List<String> getAllStatements() {
    return this.statements;
  }
} public final class PostgresUtils {

  private PostgresUtils() {
  }

  public static Executor getDbSourceHttpContext(final ActorSystem actorSystem) {
    return HttpExecution.fromThread((Executor) actorSystem.dispatchers().lookup("ground.db.context"));
  }

  public static String executeQueryToJson(Database dbSource, String sql) throws GroundException {
    Logger.debug("executeQueryToJson: {}", sql);

    try {
      Connection con = dbSource.getConnection();
      Statement stmt = con.createStatement();

      final ResultSet resultSet = stmt.executeQuery(sql);
      final long columnCount = resultSet.getMetaData().getColumnCount();
      final List<Map<String, Object>> objList = new ArrayList<>();

      while (resultSet.next()) {
        final Map<String, Object> rowData = new HashMap<>();

        for (int column = 1; column <= columnCount; column++) {
          String key = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, resultSet.getMetaData().getColumnLabel(column));
          rowData.put(key, resultSet.getObject(column));
        }

        objList.add(rowData);
      }

      stmt.close();
      con.close();
      return GroundUtils.listToJson(objList);
    } catch (SQLException e) {
      Logger.error("ERROR:  executeQueryToJson  SQL : {} Message: {} Trace: {}", sql, e.getMessage(), e.getStackTrace());
      throw new GroundException(e);
    }
  }

  public static void executeSqlList(final Database dbSource, final PostgresStatements statements) throws GroundException {
    try {
      Connection con = dbSource.getConnection();
      con.setAutoCommit(false);
      Statement stmt = con.createStatement();

      for (final String sql : statements.getAllStatements()) {
        Logger.debug("executeSqlList sql : {}", sql);

        try {
          stmt.execute(sql);
        } catch (final SQLException e) {
          con.rollback();
          Logger.error("error:  Message: {} Trace: {}", e.getMessage(), e.getStackTrace());

          throw new GroundException(e);
        }
      }

      stmt.close();
      con.commit();
      con.close();
    } catch (SQLException e) {
      Logger.error("error:  executeSqlList SQL : {} Message: {} Trace: {}", statements.getAllStatements(), e.getMessage(), e.getStackTrace());

      throw new GroundException(e);
    }
  }
}
import com.fasterxml.jackson.databind.JsonNode;
import edu.berkeley.ground.common.dao.core.EdgeVersionDao;
import edu.berkeley.ground.common.exception.GroundException;
import edu.berkeley.ground.common.exception.GroundException.ExceptionType;
import edu.berkeley.ground.common.model.core.Edge;
import edu.berkeley.ground.common.model.core.EdgeVersion;
import edu.berkeley.ground.common.model.core.RichVersion;
import edu.berkeley.ground.common.model.version.VersionHistoryDag;
import edu.berkeley.ground.common.util.IdGenerator;
import edu.berkeley.ground.postgres.dao.SqlConstants;
import edu.berkeley.ground.postgres.dao.version.PostgresVersionHistoryDagDao;
import edu.berkeley.ground.postgres.util.PostgresStatements;
import edu.berkeley.ground.postgres.util.PostgresUtils;
import java.util.List;
import play.db.Database;
import play.libs.Json;
package edu.berkeley.ground.postgres.dao.core;


public class PostgresEdgeVersionDao extends PostgresRichVersionDao<EdgeVersion> implements EdgeVersionDao {

  private PostgresEdgeDao postgresEdgeDao;

  public PostgresEdgeVersionDao(Database dbSource, IdGenerator idGenerator) {
    super(dbSource, idGenerator);
    this.postgresEdgeDao = new PostgresEdgeDao(dbSource, idGenerator);
  }

  @Override
  public EdgeVersion create(final EdgeVersion edgeVersion, List<Long> parentIds) throws GroundException {