// 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.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import org.openstreetmap.osmosis.apidb.common.DatabaseContext;
import org.openstreetmap.osmosis.core.OsmosisConstants;
import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
import org.openstreetmap.osmosis.core.database.ReleasableStatementContainer;
import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
import org.openstreetmap.osmosis.core.lifecycle.Closeable;
import org.openstreetmap.osmosis.core.lifecycle.ReleasableContainer;
import org.openstreetmap.osmosis.core.util.FixedPrecisionCoordinateConvertor;
/**
* Creates and maintains changesets for a database conversation. This will create a separate
* changeset for each user id making changes.
*
* @author Brett Henderson
*/
public class ChangesetManager implements Closeable {
private static final int MAX_CHANGESET_ID_CACHE_SIZE = 32768;
private static final String SQL_INSERT_CHANGESET = "INSERT INTO changesets"
+ " (id, user_id, created_at, min_lat, max_lat, min_lon, max_lon, closed_at, num_changes)" + " VALUES"
+ " (?, ?, NOW(), " + FixedPrecisionCoordinateConvertor.convertToFixed(-90) + ", "
+ FixedPrecisionCoordinateConvertor.convertToFixed(90) + ", "
+ FixedPrecisionCoordinateConvertor.convertToFixed(-180) + ", "
+ FixedPrecisionCoordinateConvertor.convertToFixed(180) + ", NOW(), 0)";
private static final String SQL_INSERT_CHANGESET_TAG = "INSERT INTO changeset_tags (changeset_id, k, v)"
+ " VALUES (?, 'created_by', 'Osmosis " + OsmosisConstants.VERSION + "'), (?, 'replication', 'true')";
private static final String SQL_SELECT_CHANGESET_COUNT =
"SELECT Count(*) AS changesetCount FROM changesets WHERE id = ?";
private final DatabaseContext dbCtx;
private final ReleasableContainer releasableContainer;
private final ReleasableStatementContainer statementContainer;
private PreparedStatement insertStatement;
private PreparedStatement insertTagStatement;
private PreparedStatement selectCountStatement;
private Set<Long> knownChangesetIds;
/**
* Creates a new instance.
*
* @param dbCtx The database context to use for all database access.
*/
public ChangesetManager(DatabaseContext dbCtx) {
this.dbCtx = dbCtx;
releasableContainer = new ReleasableContainer();
statementContainer = new ReleasableStatementContainer();
releasableContainer.add(statementContainer);
knownChangesetIds = new LinkedHashSet<Long>(32768);
}
private int readChangesetCount(ResultSet countSet) {
try (ResultSet resultSet = countSet) {
resultSet.next();
return resultSet.getInt("changesetCount");
} catch (SQLException e) {
throw new OsmosisRuntimeException("Unable to read the changeset count.", e);
}
}
private boolean doesChangesetExist(long changesetId) {
if (knownChangesetIds.contains(changesetId)) {
return true;
}
if (selectCountStatement == null) {
selectCountStatement = statementContainer.add(dbCtx
.prepareStatementForStreaming(SQL_SELECT_CHANGESET_COUNT));
}
try {
int prmIndex;
boolean changesetExists;
// Check if the changeset exists.
prmIndex = 1;
selectCountStatement.setLong(prmIndex++, changesetId);
changesetExists = readChangesetCount(selectCountStatement.executeQuery()) > 0;
return changesetExists;
} catch (SQLException e) {
throw new OsmosisRuntimeException("Unable to check if a changeset " + changesetId + " exists.", e);
}
}
private void addChangeset(long changesetId, long userId) {
if (insertStatement == null) {
insertStatement = statementContainer.add(dbCtx.prepareStatement(SQL_INSERT_CHANGESET));
insertTagStatement = statementContainer.add(dbCtx.prepareStatement(SQL_INSERT_CHANGESET_TAG));
}
try {
int prmIndex;
// Insert the new changeset record.
prmIndex = 1;
insertStatement.setLong(prmIndex++, changesetId);
insertStatement.setLong(prmIndex++, userId);
insertStatement.executeUpdate();
// Insert the changeset tags.
prmIndex = 1;
insertTagStatement.setLong(prmIndex++, changesetId);
insertTagStatement.setLong(prmIndex++, changesetId);
insertTagStatement.executeUpdate();
// Add the changeset to the cache, and trim the cache if required.
knownChangesetIds.add(changesetId);
if (knownChangesetIds.size() > MAX_CHANGESET_ID_CACHE_SIZE) {
Iterator<Long> i = knownChangesetIds.iterator();
i.next();
i.remove();
}
} catch (SQLException e) {
throw new OsmosisRuntimeException("Unable to insert a new changeset for user with id " + userId + ".", e);
}
}
/**
* Checks to see if the changeset already exists and adds it if it doesn't.
*
* @param changesetId
* The changeset identifier.
* @param user
* The user who created the changeset.
*/
public void addChangesetIfRequired(long changesetId, OsmUser user) {
if (!doesChangesetExist(changesetId)) {
addChangeset(changesetId, user.getId());
}
}
/**
* {@inheritDoc}
*/
@Override
public void close() {
releasableContainer.close();
}
}