/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.util.databasechange;
import java.sql.BatchUpdateException;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.openmrs.util.DatabaseUpdater;
import org.openmrs.util.DatabaseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import liquibase.change.custom.CustomTaskChange;
import liquibase.database.Database;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.CustomChangeException;
import liquibase.exception.DatabaseException;
import liquibase.exception.SetupException;
import liquibase.exception.ValidationErrors;
import liquibase.resource.ResourceAccessor;
/**
* Liquibase custom changeset used to identify and resolve duplicate EncounterRole names. If a
* duplicate EncounterRole name is identified, it will be edited to include a suffix term which
* makes it unique, and identifies it as a value to be manually changed during later review
*/
public class DuplicateEncounterRoleNameChangeSet implements CustomTaskChange {
private static final Logger log = LoggerFactory.getLogger(DuplicateEncounterRoleNameChangeSet.class);
@Override
public String getConfirmationMessage() {
return "Completed updating duplicate EncounterRole names";
}
@Override
public void setFileOpener(ResourceAccessor arg0) {
}
@Override
public void setUp() throws SetupException {
// No setup actions
}
@Override
public ValidationErrors validate(Database arg0) {
return null;
}
/**
* Method to perform validation and resolution of duplicate EncounterRole names
*/
@Override
public void execute(Database database) throws CustomChangeException {
JdbcConnection connection = (JdbcConnection) database.getConnection();
Map<String, HashSet<Integer>> duplicates = new HashMap<String, HashSet<Integer>>();
Statement stmt = null;
PreparedStatement pStmt = null;
ResultSet rs = null;
Boolean initialAutoCommit = null;
try {
initialAutoCommit = connection.getAutoCommit();
// set auto commit mode to false for UPDATE action
connection.setAutoCommit(false);
stmt = connection.createStatement();
rs = stmt
.executeQuery("SELECT * FROM encounter_role INNER JOIN (SELECT name FROM encounter_role GROUP BY name HAVING count(name) > 1) dup ON encounter_role.name = dup.name");
Integer id = null;
String name = null;
while (rs.next()) {
id = rs.getInt("encounter_role_id");
name = rs.getString("name");
if (duplicates.get(name) == null) {
HashSet<Integer> results = new HashSet<Integer>();
results.add(id);
duplicates.put(name, results);
} else {
HashSet<Integer> results = duplicates.get(name);
results.add(id);
}
}
Iterator it2 = duplicates.entrySet().iterator();
while (it2.hasNext()) {
Map.Entry pairs = (Map.Entry) it2.next();
HashSet values = (HashSet) pairs.getValue();
List<Integer> ids = new ArrayList<Integer>(values);
int duplicateNameId = 1;
for (int i = 1; i < ids.size(); i++) {
String newName = pairs.getKey() + "_" + duplicateNameId;
List<List<Object>> duplicateResult = null;
boolean duplicateName = false;
Connection con = DatabaseUpdater.getConnection();
do {
String sqlValidatorString = "select * from encounter_role where name = '" + newName + "'";
duplicateResult = DatabaseUtil.executeSQL(con, sqlValidatorString, true);
if (!duplicateResult.isEmpty()) {
duplicateNameId += 1;
newName = pairs.getKey() + "_" + duplicateNameId;
duplicateName = true;
} else {
duplicateName = false;
}
} while (duplicateName);
pStmt = connection
.prepareStatement("update encounter_role set name = ?, changed_by = ?, date_changed = ? where encounter_role_id = ?");
if (!duplicateResult.isEmpty()) {
pStmt.setString(1, newName);
}
pStmt.setString(1, newName);
pStmt.setInt(2, DatabaseUpdater.getAuthenticatedUserId());
Calendar cal = Calendar.getInstance();
Date date = new Date(cal.getTimeInMillis());
pStmt.setDate(3, date);
pStmt.setInt(4, ids.get(i));
duplicateNameId += 1;
pStmt.executeUpdate();
}
}
}
catch (BatchUpdateException e) {
log.warn("Error generated while processsing batch insert", e);
try {
log.debug("Rolling back batch", e);
connection.rollback();
}
catch (Exception rbe) {
log.warn("Error generated while rolling back batch insert", e);
}
// marks the changeset as a failed one
throw new CustomChangeException("Failed to update one or more duplicate EncounterRole names", e);
}
catch (Exception e) {
throw new CustomChangeException(e);
}
finally {
// set auto commit to its initial state
try {
connection.commit();
if (initialAutoCommit != null) {
connection.setAutoCommit(initialAutoCommit);
}
// connection.close();
}
catch (DatabaseException e) {
log.warn("Failed to set auto commit to ids initial state", e);
}
if (rs != null) {
try {
rs.close();
}
catch (SQLException e) {
log.warn("Failed to close the resultset object");
}
}
if (stmt != null) {
try {
stmt.close();
}
catch (SQLException e) {
log.warn("Failed to close the select statement used to identify duplicate EncounterRole object names");
}
}
if (pStmt != null) {
try {
pStmt.close();
}
catch (SQLException e) {
log.warn("Failed to close the prepared statement used to update duplicate EncounterRole object names");
}
}
}
}
}