// This software is released into the Public Domain. See copying.txt for details. package org.openstreetmap.osmosis.apidb.v0_6.impl; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.List; import org.openstreetmap.osmosis.apidb.common.DatabaseContext; import org.openstreetmap.osmosis.core.OsmosisRuntimeException; import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials; import org.openstreetmap.osmosis.core.database.ReleasableStatementContainer; import org.openstreetmap.osmosis.core.domain.v0_6.Entity; import org.openstreetmap.osmosis.core.domain.v0_6.Node; import org.openstreetmap.osmosis.core.domain.v0_6.Relation; import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember; import org.openstreetmap.osmosis.core.domain.v0_6.Tag; import org.openstreetmap.osmosis.core.domain.v0_6.Way; import org.openstreetmap.osmosis.core.domain.v0_6.WayNode; import org.openstreetmap.osmosis.core.lifecycle.Completable; import org.openstreetmap.osmosis.core.task.common.ChangeAction; import org.openstreetmap.osmosis.core.util.FixedPrecisionCoordinateConvertor; import org.openstreetmap.osmosis.core.util.TileCalculator; /** * Writes changes to a database. * * @author Brett Henderson */ public class ChangeWriter implements Completable { private static final String INSERT_SQL_NODE = "INSERT INTO nodes (node_id, version, timestamp, visible, changeset_id, latitude, longitude, tile)" + " VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; private static final String UPDATE_SQL_NODE = "UPDATE nodes SET timestamp = ?, visible = ?, changeset_id = ?, latitude = ?, longitude = ?, tile = ?" + " WHERE node_id = ? AND version = ?"; private static final String SELECT_SQL_NODE_COUNT = "SELECT Count(node_id) AS rowCount FROM nodes WHERE node_id = ? AND version = ?"; private static final String INSERT_SQL_NODE_CURRENT = "INSERT INTO current_nodes (id, version, timestamp, visible, changeset_id, latitude, longitude, tile)" + " VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; private static final String UPDATE_SQL_NODE_CURRENT = "UPDATE current_nodes SET version = ?, timestamp = ?, visible = ?, changeset_id = ?, latitude = ?," + " longitude = ?, tile = ? WHERE id = ?"; private static final String SELECT_SQL_NODE_CURRENT_COUNT = "SELECT Count(id) AS rowCount FROM current_nodes WHERE id = ?"; private static final String INSERT_SQL_NODE_TAG = "INSERT INTO node_tags (node_id, version, k, v) VALUES (?, ?, ?, ?)"; private static final String DELETE_SQL_NODE_TAG = "DELETE FROM node_tags WHERE node_id = ? AND version = ?"; private static final String INSERT_SQL_NODE_TAG_CURRENT = "INSERT INTO current_node_tags (node_id, k, v) VALUES (?, ?, ?)"; private static final String DELETE_SQL_NODE_TAG_CURRENT = "DELETE FROM current_node_tags WHERE node_id = ?"; private static final String INSERT_SQL_WAY = "INSERT INTO ways (way_id, version, timestamp, visible, changeset_id) VALUES (?, ?, ?, ?, ?)"; private static final String UPDATE_SQL_WAY = "UPDATE current_ways SET timestamp = ?, visible = ?, changeset_id = ? WHERE id = ? AND version = ?"; private static final String SELECT_SQL_WAY_COUNT = "SELECT Count(way_id) AS rowCount FROM ways WHERE way_id = ? AND version = ?"; private static final String INSERT_SQL_WAY_CURRENT = "INSERT INTO current_ways (id, version, timestamp, visible, changeset_id) VALUES (?, ?, ?, ?, ?)"; private static final String UPDATE_SQL_WAY_CURRENT = "UPDATE current_ways SET version = ?, timestamp = ?, visible = ?, changeset_id = ? WHERE id = ?"; private static final String SELECT_SQL_WAY_CURRENT_COUNT = "SELECT Count(id) AS rowCount FROM current_ways WHERE id = ?"; private static final String INSERT_SQL_WAY_TAG = "INSERT INTO way_tags (way_id, version, k, v) VALUES (?, ?, ?, ?)"; private static final String DELETE_SQL_WAY_TAG = "DELETE FROM way_tags WHERE way_id = ? AND version = ?"; private static final String INSERT_SQL_WAY_TAG_CURRENT = "INSERT INTO current_way_tags (way_id, k, v) VALUES (?, ?, ?)"; private static final String DELETE_SQL_WAY_TAG_CURRENT = "DELETE FROM current_way_tags WHERE way_id = ?"; private static final String INSERT_SQL_WAY_NODE = "INSERT INTO way_nodes (way_id, version, node_id, sequence_id) VALUES (?, ?, ?, ?)"; private static final String DELETE_SQL_WAY_NODE = "DELETE FROM way_nodes WHERE way_id = ? AND version = ?"; private static final String INSERT_SQL_WAY_NODE_CURRENT = "INSERT INTO current_way_nodes (way_id, node_id, sequence_id) VALUES (?, ?, ?)"; private static final String DELETE_SQL_WAY_NODE_CURRENT = "DELETE FROM current_way_nodes WHERE way_id = ?"; private static final String INSERT_SQL_RELATION = "INSERT INTO relations (relation_id, version, timestamp, visible, changeset_id) VALUES (?, ?, ?, ?, ?)"; private static final String UPDATE_SQL_RELATION = "UPDATE relations SET timestamp = ?, visible = ?, changeset_id = ? WHERE relation_id = ? AND version = ?"; private static final String SELECT_SQL_RELATION_COUNT = "SELECT Count(id) AS rowCount FROM current_relations WHERE id = ? AND version = ?"; private static final String INSERT_SQL_RELATION_CURRENT = "INSERT INTO current_relations (id, version, timestamp, visible, changeset_id) VALUES (?, ?, ?, ?, ?)"; private static final String UPDATE_SQL_RELATION_CURRENT = "UPDATE current_relations SET version = ?, timestamp = ?, visible = ?, changeset_id = ? WHERE id = ?"; private static final String SELECT_SQL_RELATION_CURRENT_COUNT = "SELECT Count(id) AS rowCount FROM current_relations WHERE id = ?"; private static final String INSERT_SQL_RELATION_TAG = "INSERT INTO relation_tags (relation_id, version, k, v) VALUES (?, ?, ?, ?)"; private static final String DELETE_SQL_RELATION_TAG = "DELETE FROM relation_tags WHERE relation_id = ? AND version = ?"; private static final String INSERT_SQL_RELATION_TAG_CURRENT = "INSERT INTO current_relation_tags (relation_id, k, v) VALUES (?, ?, ?)"; private static final String DELETE_SQL_RELATION_TAG_CURRENT = "DELETE FROM current_relation_tags WHERE relation_id = ?"; private static final String INSERT_SQL_RELATION_MEMBER_MYSQL = "INSERT INTO relation_members (relation_id, version, member_type, member_id, member_role, sequence_id)" + " VALUES (?, ?, ?, ?, ?, ?)"; private static final String INSERT_SQL_RELATION_MEMBER_PGSQL = "INSERT INTO relation_members (relation_id, version, member_type, member_id, member_role, sequence_id)" + " VALUES (?, ?, ?::nwr_enum, ?, ?, ?)"; private static final String DELETE_SQL_RELATION_MEMBER = "DELETE FROM relation_members WHERE relation_id = ? AND version = ?"; private static final String INSERT_SQL_RELATION_MEMBER_CURRENT_MYSQL = "INSERT INTO current_relation_members (relation_id, member_type, member_id, member_role, sequence_id)" + " VALUES (?, ?, ?, ?, ?)"; private static final String INSERT_SQL_RELATION_MEMBER_CURRENT_PGSQL = "INSERT INTO current_relation_members (relation_id, member_type, member_id, member_role, sequence_id)" + " VALUES (?, ?::nwr_enum, ?, ?, ?)"; private static final String DELETE_SQL_RELATION_MEMBER_CURRENT = "DELETE FROM current_relation_members WHERE relation_id = ?"; private final DatabaseContext dbCtx; private final UserManager userManager; private final ChangesetManager changesetManager; private final boolean populateCurrentTables; private final ReleasableStatementContainer statementContainer; private PreparedStatement insertNodeStatement; private PreparedStatement updateNodeStatement; private PreparedStatement selectNodeCountStatement; private PreparedStatement insertNodeCurrentStatement; private PreparedStatement updateNodeCurrentStatement; private PreparedStatement selectNodeCurrentCountStatement; private PreparedStatement insertNodeTagStatement; private PreparedStatement deleteNodeTagStatement; private PreparedStatement insertNodeTagCurrentStatement; private PreparedStatement deleteNodeTagCurrentStatement; private PreparedStatement insertWayStatement; private PreparedStatement updateWayStatement; private PreparedStatement selectWayCountStatement; private PreparedStatement insertWayCurrentStatement; private PreparedStatement updateWayCurrentStatement; private PreparedStatement selectWayCurrentCountStatement; private PreparedStatement insertWayTagStatement; private PreparedStatement deleteWayTagStatement; private PreparedStatement insertWayTagCurrentStatement; private PreparedStatement deleteWayTagCurrentStatement; private PreparedStatement insertWayNodeStatement; private PreparedStatement deleteWayNodeStatement; private PreparedStatement insertWayNodeCurrentStatement; private PreparedStatement deleteWayNodeCurrentStatement; private PreparedStatement insertRelationStatement; private PreparedStatement updateRelationStatement; private PreparedStatement selectRelationCountStatement; private PreparedStatement insertRelationCurrentStatement; private PreparedStatement updateRelationCurrentStatement; private PreparedStatement selectRelationCurrentCountStatement; private PreparedStatement insertRelationTagStatement; private PreparedStatement deleteRelationTagStatement; private PreparedStatement insertRelationTagCurrentStatement; private PreparedStatement deleteRelationTagCurrentStatement; private PreparedStatement insertRelationMemberStatement; private PreparedStatement deleteRelationMemberStatement; private PreparedStatement insertRelationMemberCurrentStatement; private PreparedStatement deleteRelationMemberCurrentStatement; private final MemberTypeRenderer memberTypeRenderer; private final TileCalculator tileCalculator; /** * Creates a new instance. * * @param loginCredentials Contains all information required to connect to the database. * @param populateCurrentTables If true, the current tables will be populated as well as history * tables. */ public ChangeWriter(DatabaseLoginCredentials loginCredentials, boolean populateCurrentTables) { dbCtx = new DatabaseContext(loginCredentials); statementContainer = new ReleasableStatementContainer(); userManager = new UserManager(dbCtx); changesetManager = new ChangesetManager(dbCtx); this.populateCurrentTables = populateCurrentTables; tileCalculator = new TileCalculator(); memberTypeRenderer = new MemberTypeRenderer(); } /** * Checks to see if the specified entity exists. * * @param statement The statement used to perform the check. * @param id The entity identifier. * @return True if the entity exists, false otherwise. * @throws SQLException if an error occurs accessing the database. */ private boolean checkIfEntityExists(PreparedStatement statement, long id) throws SQLException { statement.setLong(1, id); try (ResultSet resultSet = statement.executeQuery()) { resultSet.next(); return (resultSet.getInt("rowCount") != 0); } } /** * Checks to see if the specified entity history item exists. * * @param statement The statement used to perform the check. * @param id The entity identifier. * @param version The entity version. * @return True if the entity exists, false otherwise. * @throws SQLException if an error occurs accessing the database. */ private boolean checkIfEntityHistoryExists(PreparedStatement statement, long id, int version) throws SQLException { int prmIndex = 1; statement.setLong(prmIndex++, id); statement.setInt(prmIndex++, version); try (ResultSet resultSet = statement.executeQuery()) { resultSet.next(); return (resultSet.getInt("rowCount") != 0); } } private void assertEntityHasTimestamp(Entity entity) { if (entity.getTimestamp() == null) { throw new OsmosisRuntimeException( entity.getType().toString() + " " + entity.getId() + " does not have a timestamp set."); } } /** * Writes the specified node change to the database. * * @param node The node to be written. * @param action The change to be applied. */ public void write(Node node, ChangeAction action) { boolean visible; boolean exists; int prmIndex; assertEntityHasTimestamp(node); // Add or update the user in the database. userManager.addOrUpdateUser(node.getUser()); // Create the changeset in the database. changesetManager.addChangesetIfRequired(node.getChangesetId(), node.getUser()); // If this is a deletion, the entity is not visible. visible = !action.equals(ChangeAction.Delete); // Create the prepared statements for node creation if necessary. if (insertNodeStatement == null) { insertNodeStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_NODE)); updateNodeStatement = statementContainer.add(dbCtx.prepareStatement(UPDATE_SQL_NODE)); selectNodeCountStatement = statementContainer.add(dbCtx.prepareStatement(SELECT_SQL_NODE_COUNT)); insertNodeCurrentStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_NODE_CURRENT)); updateNodeCurrentStatement = statementContainer.add(dbCtx.prepareStatement(UPDATE_SQL_NODE_CURRENT)); selectNodeCurrentCountStatement = statementContainer.add(dbCtx .prepareStatement(SELECT_SQL_NODE_CURRENT_COUNT)); insertNodeTagStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_NODE_TAG)); deleteNodeTagStatement = statementContainer.add(dbCtx.prepareStatement(DELETE_SQL_NODE_TAG)); insertNodeTagCurrentStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_NODE_TAG_CURRENT)); deleteNodeTagCurrentStatement = statementContainer.add(dbCtx.prepareStatement(DELETE_SQL_NODE_TAG_CURRENT)); } // Remove the existing tags of the node history item. try { prmIndex = 1; deleteNodeTagStatement.setLong(prmIndex++, node.getId()); deleteNodeTagStatement.setInt(prmIndex++, node.getVersion()); deleteNodeTagStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to delete node history tags for node with id=" + node.getId() + ".", e); } // Update the node if it already exists in the history table, otherwise insert it. try { exists = checkIfEntityHistoryExists(selectNodeCountStatement, node.getId(), node.getVersion()); } catch (SQLException e) { throw new OsmosisRuntimeException( "Unable to check if current node with id=" + node.getId() + " exists.", e); } if (exists) { // Update the node in the history table. try { prmIndex = 1; updateNodeStatement.setTimestamp(prmIndex++, new Timestamp(node.getTimestamp().getTime())); updateNodeStatement.setBoolean(prmIndex++, visible); updateNodeStatement.setLong(prmIndex++, node.getChangesetId()); updateNodeStatement.setInt(prmIndex++, FixedPrecisionCoordinateConvertor.convertToFixed(node .getLatitude())); updateNodeStatement.setInt(prmIndex++, FixedPrecisionCoordinateConvertor.convertToFixed(node .getLongitude())); updateNodeStatement.setLong(prmIndex++, tileCalculator.calculateTile(node.getLatitude(), node .getLongitude())); updateNodeStatement.setLong(prmIndex++, node.getId()); updateNodeStatement.setInt(prmIndex++, node.getVersion()); updateNodeStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to update history node with id=" + node.getId() + ".", e); } } else { // Insert the new node into the history table. try { prmIndex = 1; insertNodeStatement.setLong(prmIndex++, node.getId()); insertNodeStatement.setInt(prmIndex++, node.getVersion()); insertNodeStatement.setTimestamp(prmIndex++, new Timestamp(node.getTimestamp().getTime())); insertNodeStatement.setBoolean(prmIndex++, visible); insertNodeStatement.setLong(prmIndex++, node.getChangesetId()); insertNodeStatement.setInt(prmIndex++, FixedPrecisionCoordinateConvertor.convertToFixed(node .getLatitude())); insertNodeStatement.setInt(prmIndex++, FixedPrecisionCoordinateConvertor.convertToFixed(node .getLongitude())); insertNodeStatement.setLong(prmIndex++, tileCalculator.calculateTile(node.getLatitude(), node .getLongitude())); insertNodeStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to insert history node with id=" + node.getId() + ".", e); } } // Insert the tags of the new node into the history table. for (Tag tag : node.getTags()) { try { prmIndex = 1; insertNodeTagStatement.setLong(prmIndex++, node.getId()); insertNodeTagStatement.setInt(prmIndex++, node.getVersion()); insertNodeTagStatement.setString(prmIndex++, tag.getKey()); insertNodeTagStatement.setString(prmIndex++, tag.getValue()); insertNodeTagStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to insert history node tag with id=" + node.getId() + " and key=(" + tag.getKey() + ").", e); } } if (populateCurrentTables) { // Delete the existing node tags from the current table. try { deleteNodeTagCurrentStatement.setLong(1, node.getId()); deleteNodeTagCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException( "Unable to delete current node tags with id=" + node.getId() + ".", e); } // Update the node if it already exists in the current table, otherwise insert it. try { exists = checkIfEntityExists(selectNodeCurrentCountStatement, node.getId()); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to check if current node with id=" + node.getId() + " exists.", e); } if (exists) { // Update the node in the current table. try { prmIndex = 1; updateNodeCurrentStatement.setInt(prmIndex++, node.getVersion()); updateNodeCurrentStatement.setTimestamp(prmIndex++, new Timestamp(node.getTimestamp().getTime())); updateNodeCurrentStatement.setBoolean(prmIndex++, visible); updateNodeCurrentStatement.setLong(prmIndex++, node.getChangesetId()); updateNodeCurrentStatement.setInt(prmIndex++, FixedPrecisionCoordinateConvertor.convertToFixed(node .getLatitude())); updateNodeCurrentStatement.setInt(prmIndex++, FixedPrecisionCoordinateConvertor.convertToFixed(node .getLongitude())); updateNodeCurrentStatement.setLong(prmIndex++, tileCalculator.calculateTile(node.getLatitude(), node.getLongitude())); updateNodeCurrentStatement.setLong(prmIndex++, node.getId()); updateNodeCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to update current node with id=" + node.getId() + ".", e); } } else { // Insert the new node into the current table. try { prmIndex = 1; insertNodeCurrentStatement.setLong(prmIndex++, node.getId()); insertNodeCurrentStatement.setInt(prmIndex++, node.getVersion()); insertNodeCurrentStatement.setTimestamp(prmIndex++, new Timestamp(node.getTimestamp().getTime())); insertNodeCurrentStatement.setBoolean(prmIndex++, visible); insertNodeCurrentStatement.setLong(prmIndex++, node.getChangesetId()); insertNodeCurrentStatement.setInt(prmIndex++, FixedPrecisionCoordinateConvertor.convertToFixed(node .getLatitude())); insertNodeCurrentStatement.setInt(prmIndex++, FixedPrecisionCoordinateConvertor.convertToFixed(node .getLongitude())); insertNodeCurrentStatement.setLong(prmIndex++, tileCalculator.calculateTile(node.getLatitude(), node.getLongitude())); insertNodeCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to insert current node with id=" + node.getId() + ".", e); } } // Insert the tags of the new node into the current table. for (Tag tag : node.getTags()) { try { prmIndex = 1; insertNodeTagCurrentStatement.setLong(prmIndex++, node.getId()); insertNodeTagCurrentStatement.setString(prmIndex++, tag.getKey()); insertNodeTagCurrentStatement.setString(prmIndex++, tag.getValue()); insertNodeTagCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to insert current node tag with id=" + node.getId() + " and key=(" + tag.getKey() + ").", e); } } } } /** * Writes the specified way change to the database. * * @param way The way to be written. * @param action The change to be applied. */ public void write(Way way, ChangeAction action) { boolean visible; boolean exists; int prmIndex; List<WayNode> nodeReferenceList; assertEntityHasTimestamp(way); // Add or update the user in the database. userManager.addOrUpdateUser(way.getUser()); // Create the changeset in the database. changesetManager.addChangesetIfRequired(way.getChangesetId(), way.getUser()); nodeReferenceList = way.getWayNodes(); // If this is a deletion, the entity is not visible. visible = !action.equals(ChangeAction.Delete); // Create the prepared statements for way creation if necessary. if (insertWayStatement == null) { insertWayStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_WAY)); updateWayStatement = statementContainer.add(dbCtx.prepareStatement(UPDATE_SQL_WAY)); selectWayCountStatement = statementContainer.add(dbCtx.prepareStatement(SELECT_SQL_WAY_COUNT)); insertWayCurrentStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_WAY_CURRENT)); updateWayCurrentStatement = statementContainer.add(dbCtx.prepareStatement(UPDATE_SQL_WAY_CURRENT)); selectWayCurrentCountStatement = statementContainer.add(dbCtx .prepareStatement(SELECT_SQL_WAY_CURRENT_COUNT)); insertWayTagStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_WAY_TAG)); deleteWayTagStatement = statementContainer.add(dbCtx.prepareStatement(DELETE_SQL_WAY_TAG)); insertWayTagCurrentStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_WAY_TAG_CURRENT)); deleteWayTagCurrentStatement = statementContainer.add(dbCtx.prepareStatement(DELETE_SQL_WAY_TAG_CURRENT)); insertWayNodeStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_WAY_NODE)); deleteWayNodeStatement = statementContainer.add(dbCtx.prepareStatement(DELETE_SQL_WAY_NODE)); insertWayNodeCurrentStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_WAY_NODE_CURRENT)); deleteWayNodeCurrentStatement = statementContainer.add(dbCtx.prepareStatement(DELETE_SQL_WAY_NODE_CURRENT)); } // Remove the existing tags of the way history item. try { prmIndex = 1; deleteWayTagStatement.setLong(prmIndex++, way.getId()); deleteWayTagStatement.setInt(prmIndex++, way.getVersion()); deleteWayTagStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to delete way history tags for way with id=" + way.getId() + ".", e); } // Remove the existing way nodes of the way history item. try { prmIndex = 1; deleteWayNodeStatement.setLong(prmIndex++, way.getId()); deleteWayNodeStatement.setInt(prmIndex++, way.getVersion()); deleteWayNodeStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException( "Unable to delete way history nodes for way with id=" + way.getId() + ".", e); } // Update the way if it already exists in the history table, otherwise insert it. try { exists = checkIfEntityHistoryExists(selectWayCountStatement, way.getId(), way.getVersion()); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to check if current way with id=" + way.getId() + " exists.", e); } if (exists) { // Update the way in the history table. try { prmIndex = 1; updateWayStatement.setTimestamp(prmIndex++, new Timestamp(way.getTimestamp().getTime())); updateWayStatement.setBoolean(prmIndex++, visible); updateWayStatement.setLong(prmIndex++, way.getChangesetId()); updateWayStatement.setLong(prmIndex++, way.getId()); updateWayStatement.setInt(prmIndex++, way.getVersion()); updateWayStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to update history way with id=" + way.getId() + ".", e); } } else { // Insert the new way into the history table. try { prmIndex = 1; insertWayStatement.setLong(prmIndex++, way.getId()); insertWayStatement.setInt(prmIndex++, way.getVersion()); insertWayStatement.setTimestamp(prmIndex++, new Timestamp(way.getTimestamp().getTime())); insertWayStatement.setBoolean(prmIndex++, visible); insertWayStatement.setLong(prmIndex++, way.getChangesetId()); insertWayStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to insert history way with id=" + way.getId() + ".", e); } } // Insert the tags of the new way into the history table. for (Tag tag : way.getTags()) { try { prmIndex = 1; insertWayTagStatement.setLong(prmIndex++, way.getId()); insertWayTagStatement.setInt(prmIndex++, way.getVersion()); insertWayTagStatement.setString(prmIndex++, tag.getKey()); insertWayTagStatement.setString(prmIndex++, tag.getValue()); insertWayTagStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to insert history way tag with id=" + way.getId() + " and key=(" + tag.getKey() + ").", e); } } // Insert the nodes of the new way into the history table. for (int i = 0; i < nodeReferenceList.size(); i++) { WayNode nodeReference; nodeReference = nodeReferenceList.get(i); try { prmIndex = 1; insertWayNodeStatement.setLong(prmIndex++, way.getId()); insertWayNodeStatement.setInt(prmIndex++, way.getVersion()); insertWayNodeStatement.setLong(prmIndex++, nodeReference.getNodeId()); insertWayNodeStatement.setLong(prmIndex++, i + 1); insertWayNodeStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to insert history way node with way id=" + way.getId() + " and node id=" + nodeReference.getNodeId() + ".", e); } } if (populateCurrentTables) { // Delete the existing way tags from the current table. try { deleteWayTagCurrentStatement.setLong(1, way.getId()); deleteWayTagCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to delete current way tags with id=" + way.getId() + ".", e); } // Delete the existing way nodes from the current table. try { deleteWayNodeCurrentStatement.setLong(1, way.getId()); deleteWayNodeCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to delete current way nodes with id=" + way.getId() + ".", e); } // Update the node if it already exists in the current table, otherwise insert it. try { exists = checkIfEntityExists(selectWayCurrentCountStatement, way.getId()); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to check if current way with id=" + way.getId() + " exists.", e); } if (exists) { // Update the way in the current table. try { prmIndex = 1; updateWayCurrentStatement.setInt(prmIndex++, way.getVersion()); updateWayCurrentStatement.setTimestamp(prmIndex++, new Timestamp(way.getTimestamp().getTime())); updateWayCurrentStatement.setBoolean(prmIndex++, visible); updateWayCurrentStatement.setLong(prmIndex++, way.getChangesetId()); updateWayCurrentStatement.setLong(prmIndex++, way.getId()); updateWayCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to update current way with id=" + way.getId() + ".", e); } } else { // Insert the new way into the current table. try { prmIndex = 1; insertWayCurrentStatement.setLong(prmIndex++, way.getId()); insertWayCurrentStatement.setInt(prmIndex++, way.getVersion()); insertWayCurrentStatement.setTimestamp(prmIndex++, new Timestamp(way.getTimestamp().getTime())); insertWayCurrentStatement.setBoolean(prmIndex++, visible); insertWayCurrentStatement.setLong(prmIndex++, way.getChangesetId()); insertWayCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to insert current way with id=" + way.getId() + ".", e); } } // Insert the tags of the new way into the current table. for (Tag tag : way.getTags()) { try { prmIndex = 1; insertWayTagCurrentStatement.setLong(prmIndex++, way.getId()); insertWayTagCurrentStatement.setString(prmIndex++, tag.getKey()); insertWayTagCurrentStatement.setString(prmIndex++, tag.getValue()); insertWayTagCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to insert current way tag with id=" + way.getId() + " and key=(" + tag.getKey() + ").", e); } } // Insert the nodes of the new way into the current table. for (int i = 0; i < nodeReferenceList.size(); i++) { WayNode nodeReference; nodeReference = nodeReferenceList.get(i); try { prmIndex = 1; insertWayNodeCurrentStatement.setLong(prmIndex++, way.getId()); insertWayNodeCurrentStatement.setLong(prmIndex++, nodeReference.getNodeId()); insertWayNodeCurrentStatement.setLong(prmIndex++, i); insertWayNodeCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to insert current way node with way id=" + way.getId() + " and node id=" + nodeReference.getNodeId() + ".", e); } } } } /** * Writes the specified relation change to the database. * * @param relation The relation to be written. * @param action The change to be applied. */ public void write(Relation relation, ChangeAction action) { boolean visible; boolean exists; int prmIndex; List<RelationMember> relationMemberList; assertEntityHasTimestamp(relation); // Add or update the user in the database. userManager.addOrUpdateUser(relation.getUser()); // Create the changeset in the database. changesetManager.addChangesetIfRequired(relation.getChangesetId(), relation.getUser()); relationMemberList = relation.getMembers(); // If this is a deletion, the entity is not visible. visible = !action.equals(ChangeAction.Delete); // Create the prepared statements for relation creation if necessary. if (insertRelationStatement == null) { insertRelationStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_RELATION)); updateRelationStatement = statementContainer.add(dbCtx.prepareStatement(UPDATE_SQL_RELATION)); selectRelationCountStatement = statementContainer.add(dbCtx.prepareStatement(SELECT_SQL_RELATION_COUNT)); insertRelationCurrentStatement = statementContainer .add(dbCtx.prepareStatement(INSERT_SQL_RELATION_CURRENT)); updateRelationCurrentStatement = statementContainer .add(dbCtx.prepareStatement(UPDATE_SQL_RELATION_CURRENT)); selectRelationCurrentCountStatement = statementContainer.add(dbCtx .prepareStatement(SELECT_SQL_RELATION_CURRENT_COUNT)); insertRelationTagStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_RELATION_TAG)); deleteRelationTagStatement = statementContainer.add(dbCtx.prepareStatement(DELETE_SQL_RELATION_TAG)); insertRelationTagCurrentStatement = statementContainer.add(dbCtx .prepareStatement(INSERT_SQL_RELATION_TAG_CURRENT)); deleteRelationTagCurrentStatement = statementContainer.add(dbCtx .prepareStatement(DELETE_SQL_RELATION_TAG_CURRENT)); switch (dbCtx.getDatabaseType()) { case POSTGRESQL: insertRelationMemberStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_RELATION_MEMBER_PGSQL)); break; case MYSQL: insertRelationMemberStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_RELATION_MEMBER_MYSQL)); break; default: throw new OsmosisRuntimeException("Unknown database type " + dbCtx.getDatabaseType() + "."); } deleteRelationMemberStatement = statementContainer.add(dbCtx.prepareStatement(DELETE_SQL_RELATION_MEMBER)); switch (dbCtx.getDatabaseType()) { case POSTGRESQL: insertRelationMemberCurrentStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_RELATION_MEMBER_CURRENT_PGSQL)); break; case MYSQL: insertRelationMemberCurrentStatement = statementContainer.add(dbCtx.prepareStatement(INSERT_SQL_RELATION_MEMBER_CURRENT_MYSQL)); break; default: throw new OsmosisRuntimeException("Unknown database type " + dbCtx.getDatabaseType() + "."); } deleteRelationMemberCurrentStatement = statementContainer.add(dbCtx .prepareStatement(DELETE_SQL_RELATION_MEMBER_CURRENT)); } // Remove the existing tags of the relation history item. try { prmIndex = 1; deleteRelationTagStatement.setLong(prmIndex++, relation.getId()); deleteRelationTagStatement.setInt(prmIndex++, relation.getVersion()); deleteRelationTagStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to delete relation history tags for relation with id=" + relation.getId() + ".", e); } // Remove the existing relation members of the relation history item. try { prmIndex = 1; deleteRelationMemberStatement.setLong(prmIndex++, relation.getId()); deleteRelationMemberStatement.setInt(prmIndex++, relation.getVersion()); deleteRelationMemberStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to delete relation history members for relation with id=" + relation.getId() + ".", e); } // Update the relation if it already exists in the history table, otherwise insert it. try { exists = checkIfEntityHistoryExists(selectRelationCountStatement, relation.getId(), relation.getVersion()); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to check if current relation with id=" + relation.getId() + " exists.", e); } if (exists) { // Update the relation in the history table. try { prmIndex = 1; updateRelationStatement.setTimestamp(prmIndex++, new Timestamp(relation.getTimestamp().getTime())); updateRelationStatement.setBoolean(prmIndex++, visible); updateRelationStatement.setLong(prmIndex++, relation.getChangesetId()); updateRelationStatement.setLong(prmIndex++, relation.getId()); updateRelationStatement.setInt(prmIndex++, relation.getVersion()); updateRelationStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException( "Unable to update history relation with id=" + relation.getId() + ".", e); } } else { // Insert the new relation into the history table. try { prmIndex = 1; insertRelationStatement.setLong(prmIndex++, relation.getId()); insertRelationStatement.setInt(prmIndex++, relation.getVersion()); insertRelationStatement.setTimestamp(prmIndex++, new Timestamp(relation.getTimestamp().getTime())); insertRelationStatement.setBoolean(prmIndex++, visible); insertRelationStatement.setLong(prmIndex++, relation.getChangesetId()); insertRelationStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException( "Unable to insert history relation with id=" + relation.getId() + ".", e); } } // Insert the tags of the new relation into the history table. for (Tag tag : relation.getTags()) { try { prmIndex = 1; insertRelationTagStatement.setLong(prmIndex++, relation.getId()); insertRelationTagStatement.setInt(prmIndex++, relation.getVersion()); insertRelationTagStatement.setString(prmIndex++, tag.getKey()); insertRelationTagStatement.setString(prmIndex++, tag.getValue()); insertRelationTagStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to insert history relation tag with id=" + relation.getId() + " and key=(" + tag.getKey() + ").", e); } } // Insert the members of the new relation into the history table. for (int i = 0; i < relationMemberList.size(); i++) { RelationMember relationMember; relationMember = relationMemberList.get(i); try { prmIndex = 1; insertRelationMemberStatement.setLong(prmIndex++, relation.getId()); insertRelationMemberStatement.setInt(prmIndex++, relation.getVersion()); insertRelationMemberStatement.setString(prmIndex++, memberTypeRenderer.render(relationMember .getMemberType())); insertRelationMemberStatement.setLong(prmIndex++, relationMember.getMemberId()); insertRelationMemberStatement.setString(prmIndex++, relationMember.getMemberRole()); insertRelationMemberStatement.setInt(prmIndex++, i + 1); insertRelationMemberStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to insert history relation member with relation id=" + relation.getId() + ", member type=" + relationMember.getMemberId() + " and member id=" + relationMember.getMemberId() + ".", e); } } if (populateCurrentTables) { // Delete the existing relation tags from the current table. try { deleteRelationTagCurrentStatement.setLong(1, relation.getId()); deleteRelationTagCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to delete current relation tags with id=" + relation.getId() + ".", e); } // Delete the existing relation members from the current table. try { deleteRelationMemberCurrentStatement.setLong(1, relation.getId()); deleteRelationMemberCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to delete current relation members with id=" + relation.getId() + ".", e); } // Update the relation if it already exists in the current table, otherwise insert it. try { exists = checkIfEntityExists(selectRelationCurrentCountStatement, relation.getId()); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to check if current relation with id=" + relation.getId() + " exists.", e); } if (exists) { // Update the relation in the current table. try { prmIndex = 1; updateRelationCurrentStatement.setInt(prmIndex++, relation.getVersion()); updateRelationCurrentStatement.setTimestamp(prmIndex++, new Timestamp(relation.getTimestamp() .getTime())); updateRelationCurrentStatement.setBoolean(prmIndex++, visible); updateRelationCurrentStatement.setLong(prmIndex++, relation.getChangesetId()); updateRelationCurrentStatement.setLong(prmIndex++, relation.getId()); updateRelationCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to update current relation with id=" + relation.getId() + ".", e); } } else { // Insert the new node into the current table. try { prmIndex = 1; insertRelationCurrentStatement.setLong(prmIndex++, relation.getId()); insertRelationCurrentStatement.setInt(prmIndex++, relation.getVersion()); insertRelationCurrentStatement.setTimestamp(prmIndex++, new Timestamp(relation.getTimestamp() .getTime())); insertRelationCurrentStatement.setBoolean(prmIndex++, visible); insertRelationCurrentStatement.setLong(prmIndex++, relation.getChangesetId()); insertRelationCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to insert current relation with id=" + relation.getId() + ".", e); } } // Insert the tags of the new relation into the current table. for (Tag tag : relation.getTags()) { try { prmIndex = 1; insertRelationTagCurrentStatement.setLong(prmIndex++, relation.getId()); insertRelationTagCurrentStatement.setString(prmIndex++, tag.getKey()); insertRelationTagCurrentStatement.setString(prmIndex++, tag.getValue()); insertRelationTagCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to insert current relation tag with id=" + relation.getId() + " and key=(" + tag.getKey() + ").", e); } } // Insert the members of the new relation into the current table. for (int i = 0; i < relationMemberList.size(); i++) { RelationMember relationMember; relationMember = relationMemberList.get(i); try { prmIndex = 1; insertRelationMemberCurrentStatement.setLong(prmIndex++, relation.getId()); insertRelationMemberCurrentStatement.setString(prmIndex++, memberTypeRenderer.render(relationMember .getMemberType())); insertRelationMemberCurrentStatement.setLong(prmIndex++, relationMember.getMemberId()); insertRelationMemberCurrentStatement.setString(prmIndex++, relationMember.getMemberRole()); insertRelationMemberCurrentStatement.setInt(prmIndex++, i + 1); insertRelationMemberCurrentStatement.execute(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to insert current relation member with relation id=" + relation.getId() + ", member type=" + relationMember.getMemberId() + " and member id=" + relationMember.getMemberId() + ".", e); } } } } /** * Flushes all changes to the database. */ public void complete() { dbCtx.commit(); } /** * Releases all database resources. */ public void close() { statementContainer.close(); userManager.close(); changesetManager.close(); dbCtx.close(); } }