Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
tbrugz.sqldiff.SchemaDiffer Maven / Gradle / Ivy
package tbrugz.sqldiff;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import tbrugz.sqldiff.model.ChangeType;
import tbrugz.sqldiff.model.ColumnDiff;
import tbrugz.sqldiff.model.DBIdentifiableDiff;
import tbrugz.sqldiff.model.Diff;
import tbrugz.sqldiff.model.GrantDiff;
import tbrugz.sqldiff.model.SchemaDiff;
import tbrugz.sqldiff.model.TableDiff;
import tbrugz.sqldiff.util.DiffUtil;
import tbrugz.sqldump.dbmodel.DBIdentifiable;
import tbrugz.sqldump.dbmodel.DBObjectType;
import tbrugz.sqldump.dbmodel.FK;
import tbrugz.sqldump.dbmodel.SchemaModel;
import tbrugz.sqldump.dbmodel.Table;
import tbrugz.sqldump.dbmodel.TableType;
public class SchemaDiffer {
static final Log log = LogFactory.getLog(SchemaDiffer.class);
static boolean mayReplaceDbId = true;
Set doDiffTypes = null;
static final TableType[] diffableTableTypesArray = {
TableType.BASE_TABLE, TableType.EXTERNAL_TABLE, TableType.FOREIGN_TABLE,
TableType.SYSTEM_TABLE, TableType.TABLE
//, TableType.TYPE ?
};
static final List diffableTableTypes;
static {
diffableTableTypes = Arrays.asList(diffableTableTypesArray);
}
void diffTables(SchemaModel modelOrig, SchemaModel modelNew, SchemaDiff diff) {
//tables
Set newTablesThatExistsInOrigModel = new HashSet();
Map nonDiffableTableTypesPresent = new TreeMap();
if(modelOrig.getTables().size()>0) {
for(Table tOrig: modelOrig.getTables()) {
if( !diffableTableTypes.contains(tOrig.getType()) ) {
Integer count = nonDiffableTableTypesPresent.get(tOrig.getType());
if(count==null) { count = 1; } else { count++; }
nonDiffableTableTypesPresent.put(tOrig.getType(), count);
continue;
}
Table tNew = DBIdentifiable.getDBIdentifiableBySchemaAndName(modelNew.getTables(), tOrig.getSchemaName(), tOrig.getName());
if(tNew==null) {
//if new table doesn't exist, drop old
TableDiff td = new TableDiff(ChangeType.DROP, tOrig);
//td.table = tOrig;
//td.diffType = ChangeType.DROP;
diff.getTableDiffs().add(td);
}
else {
//new and old tables exists
newTablesThatExistsInOrigModel.add(tNew);
//rename XXX: what about rename? external info needed?
List diffs = TableDiff.tableDiffs(tOrig, tNew);
//FKs
diffs(DBObjectType.FK, diff.getDbIdDiffs(),
getFKsFromTable(modelOrig.getForeignKeys(), tOrig.getName()),
getFKsFromTable(modelNew.getForeignKeys(), tNew.getName()),
tOrig.getFinalQualifiedName(),
tNew.getFinalQualifiedName());
for(Diff dt: diffs) {
boolean added = false;
if(dt instanceof TableDiff) {
added = diff.getTableDiffs().add((TableDiff)dt);
}
else if(dt instanceof ColumnDiff) {
added = diff.getColumnDiffs().add((ColumnDiff)dt);
}
else if(dt instanceof GrantDiff) {
added = diff.getGrantDiffs().add((GrantDiff)dt);
}
else if(dt instanceof DBIdentifiableDiff) {
added = diff.getDbIdDiffs().add((DBIdentifiableDiff)dt);
}
else {
added = true;
log.warn("unknown diff: "+dt);
}
if(!added) {
log.warn("diff already present in set: "+dt);
}
}
//diff.tableDiffs.addAll(diffs);
//column changes
}
}
}
/*Set newTables = new HashSet();
newTables.addAll(modelNew.getTables());
newTables.removeAll(newTablesThatExistsInOrigModel);
diff.getTables().addAll(newTables);*/
/*for(Table tNew: newTables) {
diff.getTables().add(tNew);
}*/
Set addTables = new TreeSet();
//add tables
addTables.addAll(modelNew.getTables());
addTables.removeAll(newTablesThatExistsInOrigModel);
for(Table t: addTables) {
if( !diffableTableTypes.contains(t.getType()) ) {
Integer count = nonDiffableTableTypesPresent.get(t.getType());
if(count==null) { count = 1; } else { count++; }
nonDiffableTableTypesPresent.put(t.getType(), count);
continue;
}
TableDiff td = new TableDiff(ChangeType.ADD, t);
diff.getTableDiffs().add(td);
}
if(nonDiffableTableTypesPresent.size()>0) {
log.warn("non-diffable tables by type: "+nonDiffableTableTypesPresent);
}
}
static final DBObjectType[] diffableTypes = {
DBObjectType.TABLE, DBObjectType.VIEW, DBObjectType.MATERIALIZED_VIEW, DBObjectType.TRIGGER, DBObjectType.EXECUTABLE,
DBObjectType.SYNONYM, DBObjectType.INDEX, DBObjectType.SEQUENCE,
DBObjectType.FUNCTION, DBObjectType.PROCEDURE, DBObjectType.PACKAGE, DBObjectType.PACKAGE_BODY, DBObjectType.TYPE
};
static final Set diffableTypesSet = new HashSet();
static {
diffableTypesSet.addAll(Arrays.asList(diffableTypes));
}
public SchemaDiff diffSchemas(SchemaModel modelOrig, SchemaModel modelNew) {
if(modelOrig == null || modelNew == null) {
log.warn("source, target, or both models are null");
return null;
}
SchemaDiff diff = new SchemaDiff();
if(doDiffTypes!=null) {
log.info("diffing types: "+doDiffTypes);
Set setDiff = new HashSet();
setDiff.addAll(doDiffTypes);
setDiff.removeAll(diffableTypesSet);
if(setDiff.size()>0) {
log.warn("undiffable types: "+setDiff);
}
}
//Tables
if(doDiffTypes==null || doDiffTypes.contains(DBObjectType.TABLE)) {
//TODO: diff or not COLUMN, GRANT & CONSTRAINT types
diffTables(modelOrig, modelNew, diff);
}
//Views
if(doDiffTypes==null || doDiffTypes.contains(DBObjectType.VIEW)) {
diffs(DBObjectType.VIEW, diff.getDbIdDiffs(), modelOrig.getViews(), modelNew.getViews());
}
//Materialized Views
if(doDiffTypes==null || doDiffTypes.contains(DBObjectType.MATERIALIZED_VIEW)) {
diffs(DBObjectType.MATERIALIZED_VIEW, diff.getDbIdDiffs(), modelOrig.getViews(), modelNew.getViews(), null, null, true);
}
//Triggers
if(doDiffTypes==null || doDiffTypes.contains(DBObjectType.TRIGGER)) {
diffs(DBObjectType.TRIGGER, diff.getDbIdDiffs(), modelOrig.getTriggers(), modelNew.getTriggers());
}
//FIXedME: package and package body: findByName must also use object type! (and schemaName!)
//Executables
if(doDiffTypes==null || doDiffTypes.contains(DBObjectType.EXECUTABLE)) {
diffs(DBObjectType.EXECUTABLE, diff.getDbIdDiffs(), modelOrig.getExecutables(), modelNew.getExecutables());
}
else {
//log.debug("countsByType-orig::\n"+ModelUtils.getExecutableCountsByType(modelOrig.getExecutables()));
//log.debug("countsByType-new::\n"+ModelUtils.getExecutableCountsByType(modelNew.getExecutables()));
DBObjectType[] types = { DBObjectType.FUNCTION, DBObjectType.PROCEDURE, DBObjectType.PACKAGE, DBObjectType.PACKAGE_BODY, DBObjectType.TYPE };
//DBObjectType[] types = DBObjectType.getExecutableTypes();
//int count = 0;
for(DBObjectType et: types) {
//log.debug("diffSchemas: execs["+count+"]: #dbid="+diff.getDbIdDiffs().size()+" #orig="+modelOrig.getExecutables().size()+" #new="+modelNew.getExecutables().size());
if(doDiffTypes.contains(et)) {
diffs(et, diff.getDbIdDiffs(), modelOrig.getExecutables(), modelNew.getExecutables(), null, null, true);
}
//count++;
}
//log.debug("diffSchemas: execs-end["+count+"]: #dbid="+diff.getDbIdDiffs().size()+" #orig="+modelOrig.getExecutables().size()+" #new="+modelNew.getExecutables().size());
}
//Synonyms
//FIXedME: doesn't detect schemaName changes
if(doDiffTypes==null || doDiffTypes.contains(DBObjectType.SYNONYM)) {
diffs(DBObjectType.SYNONYM, diff.getDbIdDiffs(), modelOrig.getSynonyms(), modelNew.getSynonyms());
}
//Indexes
if(doDiffTypes==null || doDiffTypes.contains(DBObjectType.INDEX)) {
diffs(DBObjectType.INDEX, diff.getDbIdDiffs(), modelOrig.getIndexes(), modelNew.getIndexes());
}
//Sequences
if(doDiffTypes==null || doDiffTypes.contains(DBObjectType.SEQUENCE)) {
diffs(DBObjectType.SEQUENCE, diff.getDbIdDiffs(), modelOrig.getSequences(), modelNew.getSequences());
}
diff.setSqlDialect(modelNew.getSqlDialect());
//XXX: query tableDiffs and columnDiffs: set schema.type: ADD, ALTER, DROP
SchemaDiff.logInfo(diff);
return diff;
}
public void diffs(DBObjectType objType, Collection diffs, Collection listOrig, Collection listNew) {
diffs(objType, diffs, listOrig, listNew, null, null);
}
public void diffs(DBObjectType objType, Collection diffs, Collection listOrig, Collection listNew, String origOwnerTableName, String newOwnerTableName) {
diffs(objType, diffs, listOrig, listNew, origOwnerTableName, newOwnerTableName, false);
}
public void diffs(DBObjectType objType, Collection diffs, Collection listOrig, Collection listNew, String origOwnerTableName, String newOwnerTableName, boolean filterByType) {
Set newDBObjectsThatExistsInOrigModel = new HashSet();
int countAdd = 0, countReplace = 0, countDrop = 0, countSource = 0, countTarget = 0, countSourceInit=0, countTargetInit=0;
/*List lo = new ArrayList();
lo.addAll(listOrig);
List ln = new ArrayList();
ln.addAll(listNew);*/
//log.info("diffs: "+objType+" / "+origOwnerTableName);
for(DBIdentifiable cOrig: listOrig) {
//log.info("dbid: "+cOrig);
//DBIdentifiable cOrig = (DBIdentifiable) lo.get(i);
countSourceInit++;
if(filterByType && DBIdentifiable.getType(cOrig)!=objType) { continue; }
countSource++;
DBIdentifiable cNew = DiffUtil.getDBIdentifiableByTypeSchemaAndName(listNew, DBIdentifiable.getType(cOrig), cOrig.getSchemaName(), cOrig.getName());
if(cNew!=null) {
newDBObjectsThatExistsInOrigModel.add(cNew);
if(!cOrig.equals4Diff(cNew)) {
if(!cOrig.isDumpable()) {
log.debug("original/new object not dumpable: "+cOrig);
continue;
}
if(mayReplaceDbId
&& ( (origOwnerTableName==null && newOwnerTableName==null)
|| (origOwnerTableName!=null && origOwnerTableName.equals(newOwnerTableName)) ) ) {
log.debug("replace "+objType+": orig: "+cOrig+" new: "+cNew);
diffs.add(new DBIdentifiableDiff(ChangeType.REPLACE, cOrig, cNew, origOwnerTableName));
}
else {
log.debug("drop/add "+objType+": orig: "+cOrig+" new: "+cNew);
diffs.add(new DBIdentifiableDiff(ChangeType.DROP, cOrig, cNew, origOwnerTableName));
diffs.add(new DBIdentifiableDiff(ChangeType.ADD, cOrig, cNew, newOwnerTableName));
}
countReplace++;
}
}
else {
if(!cOrig.isDumpable()) {
log.debug("original object not dumpable: "+cOrig);
continue;
}
log.debug("drop "+objType+": orig: "+cOrig);
diffs.add(new DBIdentifiableDiff(ChangeType.DROP, cOrig, null, origOwnerTableName));
countDrop++;
}
}
for(DBIdentifiable cNew: listNew) {
//DBIdentifiable cNew = (DBIdentifiable) ln.get(i);
countTargetInit++;
if(filterByType && DBIdentifiable.getType(cNew)!=objType) { continue; }
countTarget++;
if(newDBObjectsThatExistsInOrigModel.contains(cNew)) { continue; }
if(!cNew.isDumpable()) {
log.debug("new object not dumpable: "+cNew);
continue;
}
log.debug("add "+objType+": new: "+cNew);
diffs.add(new DBIdentifiableDiff(ChangeType.ADD, null, cNew, newOwnerTableName));
countAdd++;
}
log.debug("diffs["+objType+"]: #add="+countAdd+" #replace="+countReplace+" #drop="+countDrop+
" #source="+countSource+"/"+countSourceInit+"/"+listOrig.size()+
" #target="+countTarget+"/"+countTargetInit+"/"+listNew.size()
);
}
static Set getFKsFromTable(Set fks, String table) {
Set retfks = new HashSet();
for(FK fk: fks) {
if(fk.getFkTable().equals(table)) { retfks.add(fk); }
}
return retfks;
}
public void setTypesForDiff(String types) {
if(types==null) { return; }
doDiffTypes = new TreeSet();
String[] typesArr = types.split(",");
for(String s: typesArr) {
if(s==null) { continue; }
s = s.trim();
try {
DBObjectType t = DBObjectType.valueOf(s);
doDiffTypes.add(t);
}
catch(IllegalArgumentException e) {
log.warn("unknown object type: "+s);
}
}
}
}