mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
ALF-10772: schema comparator
Compares two abstract database schemas. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@31312 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -19,15 +19,112 @@
|
|||||||
package org.alfresco.util.schemacomp;
|
package org.alfresco.util.schemacomp;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import org.alfresco.util.schemacomp.Result.Where;
|
||||||
|
import org.alfresco.util.schemacomp.model.DbObject;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keeps a record of what differences, if any, exist between the two
|
* Collects differences so that tools can report on or respond to differences between database schemas.
|
||||||
* schemas being compared.
|
|
||||||
*
|
*
|
||||||
* @author Matt Ward
|
* @author Matt Ward
|
||||||
*/
|
*/
|
||||||
public class Differences
|
public class Differences implements Iterable<Result>
|
||||||
{
|
{
|
||||||
private List<Result> results = new ArrayList<Result>();
|
private final List<Result> items = new ArrayList<Result>();
|
||||||
|
private final Path path = new Path();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Path
|
||||||
|
*/
|
||||||
|
public void pushPath(String component)
|
||||||
|
{
|
||||||
|
this.path.push(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Path
|
||||||
|
*/
|
||||||
|
public String popPath()
|
||||||
|
{
|
||||||
|
return this.path.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record a difference between two objects, or specify that an object only appears in either the
|
||||||
|
* 'left' or 'right' schemas.
|
||||||
|
*
|
||||||
|
* @param where The type of difference, see {@link Where}
|
||||||
|
* @param left Left value, or null if the item appears in the right, but not left schema.
|
||||||
|
* @param right Right value, or null if the item appears in the left, but not right schema.
|
||||||
|
*/
|
||||||
|
public void add(Where where, Object left, Object right)
|
||||||
|
{
|
||||||
|
Result result = new Result(where, left, right, path.getCurrentPath());
|
||||||
|
items.add(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain an iterator for the top-level items held in this schema - since this is a hierarchical model,
|
||||||
|
* deeper items are obtained by navigating through the top-level items.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator<Result> iterator()
|
||||||
|
{
|
||||||
|
return items.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return How many top-level items are in the schema.
|
||||||
|
*/
|
||||||
|
public int size()
|
||||||
|
{
|
||||||
|
return items.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies where in a database schema a difference occurs - this is largely the same as the fully qualified
|
||||||
|
* name of a database object, e.g. <code>public.alf_node</code> except that if an object doesn't have a name
|
||||||
|
* then a suitable label will be provided for that path element.
|
||||||
|
* <p>
|
||||||
|
* Elements can be <code>push</code>ed onto the path and <code>pop</code>ped from the path, making it easier for a
|
||||||
|
* {@link DbObject} to process that object's data as part of a heirachy - the parent object should push an
|
||||||
|
* appropriate label onto the path for itself, before invoking any child objects'
|
||||||
|
* {@link DbObject#diff(DbObject, Differences)} method.
|
||||||
|
*
|
||||||
|
* @author Matt Ward
|
||||||
|
*/
|
||||||
|
private static class Path
|
||||||
|
{
|
||||||
|
private Stack<String> components = new Stack<String>();
|
||||||
|
private String current;
|
||||||
|
|
||||||
|
public void push(String component)
|
||||||
|
{
|
||||||
|
components.push(component);
|
||||||
|
makeCurrentPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String pop()
|
||||||
|
{
|
||||||
|
String component = components.pop();
|
||||||
|
makeCurrentPath();
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrentPath()
|
||||||
|
{
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makeCurrentPath()
|
||||||
|
{
|
||||||
|
current = StringUtils.join(components, ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,39 +18,73 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.util.schemacomp;
|
package org.alfresco.util.schemacomp;
|
||||||
|
|
||||||
import org.alfresco.util.schemacomp.model.DbObject;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Result of a comparison between two database objects.
|
* Result of a comparison between two database objects.
|
||||||
*
|
*
|
||||||
* @author Matt Ward
|
* @author Matt Ward
|
||||||
*/
|
*/
|
||||||
public class Result
|
public final class Result
|
||||||
{
|
{
|
||||||
public enum Type { ONLY_IN_LEFT, ONLY_IN_RIGHT, IN_BOTH_NO_DIFFERENCE, IN_BOTH_BUT_DIFFERENCE };
|
/** Specifies the type of differences */
|
||||||
private Type type;
|
public enum Where { ONLY_IN_LEFT, ONLY_IN_RIGHT, IN_BOTH_NO_DIFFERENCE, IN_BOTH_BUT_DIFFERENCE };
|
||||||
|
private final Where where;
|
||||||
// Field list, where differences lie - recorded for single level only, otherwise, an error deep down would end
|
private final Object left;
|
||||||
// up having the whole schema as being different, but that isn't useful. During reporting, climb back up the tree
|
private final Object right;
|
||||||
// to produce a path, e.g. "my_schema.my_table.my_column.nullable has differences"
|
private final String path;
|
||||||
|
|
||||||
|
|
||||||
// Could hold the two items that (may - see type above) differ?
|
public Result(Where where, Object left, Object right, String path)
|
||||||
// These objects are already structured, no field names required.
|
|
||||||
DbObject left;
|
|
||||||
DbObject right;
|
|
||||||
|
|
||||||
// or, could...
|
|
||||||
// Have differences
|
|
||||||
|
|
||||||
|
|
||||||
public static class DiffField
|
|
||||||
{
|
{
|
||||||
String leftFieldName;
|
this.where = where;
|
||||||
String leftVal;
|
this.left = left;
|
||||||
|
this.right = right;
|
||||||
String rightFieldName;
|
this.path = path;
|
||||||
String rightVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the where
|
||||||
|
*/
|
||||||
|
public Where getWhere()
|
||||||
|
{
|
||||||
|
return this.where;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the left
|
||||||
|
*/
|
||||||
|
public Object getLeft()
|
||||||
|
{
|
||||||
|
return this.left;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the right
|
||||||
|
*/
|
||||||
|
public Object getRight()
|
||||||
|
{
|
||||||
|
return this.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the path
|
||||||
|
*/
|
||||||
|
public String getPath()
|
||||||
|
{
|
||||||
|
return this.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "Result [where=" + this.where + ", left=" + this.left + ", right=" + this.right
|
||||||
|
+ ", path=" + this.path + "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,17 +18,19 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.util.schemacomp;
|
package org.alfresco.util.schemacomp;
|
||||||
|
|
||||||
import org.alfresco.util.schemacomp.model.DbObject;
|
|
||||||
import org.alfresco.util.schemacomp.model.Schema;
|
import org.alfresco.util.schemacomp.model.Schema;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: comment me!
|
* Compares two abstract database schemas, a 'left' schema and a 'right' schema. The left schema is the primary
|
||||||
|
* schema and all objects in the right schema are judged relative to it.
|
||||||
|
*
|
||||||
* @author Matt Ward
|
* @author Matt Ward
|
||||||
*/
|
*/
|
||||||
public class SchemaComparator
|
public class SchemaComparator
|
||||||
{
|
{
|
||||||
private Schema leftSchema;
|
private final Schema leftSchema;
|
||||||
private Schema rightSchema;
|
private final Schema rightSchema;
|
||||||
|
private final Differences differences = new Differences();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a comparator to compare schemas left and right.
|
* Construct a comparator to compare schemas left and right.
|
||||||
@@ -42,34 +44,19 @@ public class SchemaComparator
|
|||||||
this.rightSchema = right;
|
this.rightSchema = right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void compare()
|
public void compare()
|
||||||
{
|
{
|
||||||
for (DbObject leftObj : leftSchema)
|
// Check the left schema against the right schema and record any differences.
|
||||||
{
|
leftSchema.diff(rightSchema, differences);
|
||||||
DbObject rightObj = rightSchema.get(leftObj.getIdentifier());
|
}
|
||||||
if (rightObj != null)
|
|
||||||
{
|
|
||||||
// There is an equivalent object in the right hand schema as in the left.
|
/**
|
||||||
System.out.println("Both schemas have object: " + leftObj.getIdentifier() +
|
* @return the differences
|
||||||
"(" + leftObj.getClass().getSimpleName() + ")");
|
*/
|
||||||
}
|
public Differences getDifferences()
|
||||||
else
|
{
|
||||||
{
|
return this.differences;
|
||||||
// No equivalent object in the right hand schema.
|
|
||||||
System.out.println("No matching object in right schema: " + leftObj.getIdentifier() +
|
|
||||||
"(" + leftObj.getClass().getSimpleName() + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identify objects in the right schema but not the left
|
|
||||||
for (DbObject rightObj : rightSchema)
|
|
||||||
{
|
|
||||||
if (!leftSchema.contains(rightObj.getIdentifier()))
|
|
||||||
{
|
|
||||||
// No equivalent object in the left hand schema.
|
|
||||||
System.out.println("No matching object in left schema: " + rightObj.getIdentifier() +
|
|
||||||
"(" + rightObj.getClass().getSimpleName() + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,13 +19,27 @@
|
|||||||
package org.alfresco.util.schemacomp;
|
package org.alfresco.util.schemacomp;
|
||||||
|
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.alfresco.util.schemacomp.Result.Where;
|
||||||
|
import org.alfresco.util.schemacomp.model.Column;
|
||||||
|
import org.alfresco.util.schemacomp.model.ForeignKey;
|
||||||
|
import org.alfresco.util.schemacomp.model.Index;
|
||||||
|
import org.alfresco.util.schemacomp.model.PrimaryKey;
|
||||||
import org.alfresco.util.schemacomp.model.Schema;
|
import org.alfresco.util.schemacomp.model.Schema;
|
||||||
import org.alfresco.util.schemacomp.model.Table;
|
import org.alfresco.util.schemacomp.model.Table;
|
||||||
|
import org.apache.commons.lang.ArrayUtils;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: comment me!
|
* Tests for the SchmeaComparator class.
|
||||||
|
*
|
||||||
* @author Matt Ward
|
* @author Matt Ward
|
||||||
*/
|
*/
|
||||||
public class SchemaComparatorTest
|
public class SchemaComparatorTest
|
||||||
@@ -37,8 +51,8 @@ public class SchemaComparatorTest
|
|||||||
@Before
|
@Before
|
||||||
public void setup()
|
public void setup()
|
||||||
{
|
{
|
||||||
left = new Schema();
|
left = new Schema("left_schema");
|
||||||
right = new Schema();
|
right = new Schema("right_schema");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -47,19 +61,183 @@ public class SchemaComparatorTest
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Test
|
@Test
|
||||||
// public void canDetermineSameTables()
|
public void canPerformDiff()
|
||||||
// {
|
{
|
||||||
// left.put(new Table("alf_node"));
|
// Left hand side's database objects.
|
||||||
// left.put(new Table("table_in_left"));
|
left.add(new Table("tbl_no_diff", columns("id NUMBER(10)", "nodeRef VARCHAR2(200)", "name VARCHAR2(150)"),
|
||||||
// left.put(new Table("in_both_but_different"));
|
pk("pk_tbl_no_diff", "id"), fkeys(fk("fk_tbl_no_diff", "nodeRef", "node", "nodeRef")),
|
||||||
// right.put(new Table("alf_node"));
|
indexes("idx_node id nodeRef")));
|
||||||
// // Note this table is in different position in the RHS list.
|
left.add(table("table_in_left"));
|
||||||
// Table rightTable = new Table("in_both_but_different");
|
left.add(new Table("tbl_has_diff_pk", columns("id NUMBER(10)", "nodeRef VARCHAR2(200)"),
|
||||||
// right.put(rightTable);
|
pk("pk_is_diff", "id"), fkeys(), indexes()));
|
||||||
// right.put(new Table("table_in_right"));
|
|
||||||
//
|
// Right hand side's database objects.
|
||||||
// comparator = new SchemaComparator(left, right);
|
right.add(new Table("tbl_no_diff", columns("id NUMBER(10)", "nodeRef VARCHAR2(200)", "name VARCHAR2(150)"),
|
||||||
// comparator.compare();
|
pk("pk_tbl_no_diff", "id"), fkeys(fk("fk_tbl_no_diff", "nodeRef", "node", "nodeRef")),
|
||||||
// }
|
indexes("idx_node id nodeRef")));
|
||||||
|
right.add(new Table("tbl_has_diff_pk", columns("id NUMBER(10)", "nodeRef VARCHAR2(200)"),
|
||||||
|
pk("pk_is_diff", "nodeRef"), fkeys(), indexes()));
|
||||||
|
right.add(table("table_in_right"));
|
||||||
|
|
||||||
|
|
||||||
|
comparator = new SchemaComparator(left, right);
|
||||||
|
comparator.compare();
|
||||||
|
|
||||||
|
dumpDiffs(comparator.getDifferences(), true);
|
||||||
|
|
||||||
|
Iterator<Result> it = comparator.getDifferences().iterator();
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff", "tbl_no_diff", it.next());
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.id", "id", it.next());
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.id", "NUMBER(10)", it.next());
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.id", Boolean.FALSE, it.next()); // nullable
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.nodeRef", "nodeRef", it.next());
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.nodeRef", "VARCHAR2(200)", it.next());
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.nodeRef", Boolean.FALSE, it.next()); // nullable
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.name", "name", it.next());
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.name", "VARCHAR2(150)", it.next());
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.name", Boolean.FALSE, it.next()); // nullable
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.pk_tbl_no_diff", "pk_tbl_no_diff", it.next()); // name field
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.pk_tbl_no_diff", "id", it.next()); // first (& only) column of list
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.fk_tbl_no_diff", "fk_tbl_no_diff", it.next()); // name field
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.fk_tbl_no_diff", "nodeRef", it.next()); // localColumn
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.fk_tbl_no_diff", "node", it.next()); // targetTable
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.fk_tbl_no_diff", "nodeRef", it.next()); // targetColumn
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.idx_node", "idx_node", it.next()); // index name
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.idx_node", "id", it.next()); // first indexed column
|
||||||
|
assertNoDifference("left_schema.tbl_no_diff.idx_node", "nodeRef", it.next()); // second indexed column
|
||||||
|
// TODO: why are diffs for table not flattened out as for index?
|
||||||
|
assertOnlyInOne("left_schema", Where.ONLY_IN_LEFT, table("table_in_left"), it.next());
|
||||||
|
|
||||||
|
assertNoDifference("left_schema.tbl_has_diff_pk", "tbl_has_diff_pk", it.next());
|
||||||
|
assertNoDifference("left_schema.tbl_has_diff_pk.id", "id", it.next());
|
||||||
|
assertNoDifference("left_schema.tbl_has_diff_pk.id", "NUMBER(10)", it.next());
|
||||||
|
assertNoDifference("left_schema.tbl_has_diff_pk.id", Boolean.FALSE, it.next()); // nullable
|
||||||
|
assertNoDifference("left_schema.tbl_has_diff_pk.nodeRef", "nodeRef", it.next());
|
||||||
|
assertNoDifference("left_schema.tbl_has_diff_pk.nodeRef", "VARCHAR2(200)", it.next());
|
||||||
|
assertNoDifference("left_schema.tbl_has_diff_pk.nodeRef", Boolean.FALSE, it.next()); // nullable
|
||||||
|
assertNoDifference("left_schema.tbl_has_diff_pk.pk_is_diff", "pk_is_diff", it.next()); // name field
|
||||||
|
|
||||||
|
// TODO: surely this should be a diff rather than a ONLY_IN_LEFT plus ONLY_IN_RIGHT?
|
||||||
|
// assertHasDifference("left_schema.tbl_has_diff_pk.pk_is_diff", "id", "nodeRef", it.next()); // first (& only) column of list
|
||||||
|
assertOnlyInOne("left_schema.tbl_has_diff_pk.pk_is_diff", Where.ONLY_IN_LEFT, "id", it.next()); // first (& only) column of list
|
||||||
|
|
||||||
|
// This belong to the pk_is_diff above.
|
||||||
|
assertOnlyInOne("left_schema.tbl_has_diff_pk.pk_is_diff", Where.ONLY_IN_RIGHT, "nodeRef", it.next()); // first (& only) column of list
|
||||||
|
|
||||||
|
// Items that are ONLY_IN_RIGHT always come at the end
|
||||||
|
assertOnlyInOne("left_schema", Where.ONLY_IN_RIGHT, table("table_in_right"), it.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that the result shows the value to have different values in the left and right items.
|
||||||
|
*/
|
||||||
|
private void assertHasDifference(String path, Object leftValue, Object rightValue, Result result)
|
||||||
|
{
|
||||||
|
assertEquals(Where.IN_BOTH_BUT_DIFFERENCE, result.getWhere());
|
||||||
|
assertEquals(path, result.getPath());
|
||||||
|
assertEquals(leftValue, result.getLeft());
|
||||||
|
assertEquals(rightValue, result.getRight());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that the result shows the value to be present only in either the left or right items.
|
||||||
|
*/
|
||||||
|
private void assertOnlyInOne(String path, Where which, Object value, Result result)
|
||||||
|
{
|
||||||
|
assertEquals(which, result.getWhere());
|
||||||
|
assertEquals(path, result.getPath());
|
||||||
|
|
||||||
|
if (which == Where.ONLY_IN_LEFT)
|
||||||
|
{
|
||||||
|
assertEquals(value, result.getLeft());
|
||||||
|
assertNull(result.getRight());
|
||||||
|
}
|
||||||
|
else if (which == Where.ONLY_IN_RIGHT)
|
||||||
|
{
|
||||||
|
assertNull(result.getLeft());
|
||||||
|
assertEquals(value, result.getRight());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("The 'which' argument should be ONLY_IN_LEFT or ONLY_IN_RIGHT.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that the result shows no differences between the left and right items.
|
||||||
|
*/
|
||||||
|
private void assertNoDifference(String path, Object value, Result result)
|
||||||
|
{
|
||||||
|
assertEquals(Where.IN_BOTH_NO_DIFFERENCE, result.getWhere());
|
||||||
|
assertEquals(path, result.getPath());
|
||||||
|
assertEquals(value, result.getLeft());
|
||||||
|
assertEquals(value, result.getRight());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param differences
|
||||||
|
*/
|
||||||
|
private void dumpDiffs(Differences differences, boolean showNonDifferences)
|
||||||
|
{
|
||||||
|
System.out.println("Differences (" + differences.size() + ")");
|
||||||
|
for (Result d : differences)
|
||||||
|
{
|
||||||
|
if (d.getWhere() != Where.IN_BOTH_NO_DIFFERENCE || showNonDifferences)
|
||||||
|
{
|
||||||
|
System.out.println(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Table table(String name)
|
||||||
|
{
|
||||||
|
return new Table(name, columns("id NUMBER(10)"), pk("pk_" + name, "id"), fkeys(), indexes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<Column> columns(String... colDefs)
|
||||||
|
{
|
||||||
|
assertTrue("Tables must have columns", colDefs.length > 0);
|
||||||
|
Column[] columns = new Column[colDefs.length];
|
||||||
|
|
||||||
|
for (int i = 0; i < colDefs.length; i++)
|
||||||
|
{
|
||||||
|
String[] parts = colDefs[i].split(" ");
|
||||||
|
columns[i] = new Column(parts[0], parts[1], false);
|
||||||
|
}
|
||||||
|
return Arrays.asList(columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PrimaryKey pk(String name, String... columnNames)
|
||||||
|
{
|
||||||
|
assertTrue("No columns specified", columnNames.length > 0);
|
||||||
|
PrimaryKey pk = new PrimaryKey();
|
||||||
|
pk.setName(name);
|
||||||
|
pk.setColumnNames(Arrays.asList(columnNames));
|
||||||
|
return pk;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ForeignKey> fkeys(ForeignKey... fkeys)
|
||||||
|
{
|
||||||
|
return Arrays.asList(fkeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ForeignKey fk(String fkName, String localColumn, String targetTable, String targetColumn)
|
||||||
|
{
|
||||||
|
return new ForeignKey(fkName, localColumn, targetTable, targetColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<Index> indexes(String... indexDefs)
|
||||||
|
{
|
||||||
|
Index[] indexes = new Index[indexDefs.length];
|
||||||
|
for (int i = 0; i < indexDefs.length; i++)
|
||||||
|
{
|
||||||
|
String[] parts = indexDefs[i].split(" ");
|
||||||
|
String name = parts[0];
|
||||||
|
String[] columns = (String[]) ArrayUtils.subarray(parts, 1, parts.length);
|
||||||
|
indexes[i] = new Index(name, Arrays.asList(columns));
|
||||||
|
}
|
||||||
|
return Arrays.asList(indexes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
132
source/java/org/alfresco/util/schemacomp/SchemaUtils.java
Normal file
132
source/java/org/alfresco/util/schemacomp/SchemaUtils.java
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.util.schemacomp;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.alfresco.util.schemacomp.Result.Where;
|
||||||
|
import org.alfresco.util.schemacomp.model.DbObject;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.commons.collections.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: comment me!
|
||||||
|
*
|
||||||
|
* @author Matt Ward
|
||||||
|
*/
|
||||||
|
public abstract class SchemaUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param objToFind
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static DbObject findSameObjectAs(Collection<? extends DbObject> objects,
|
||||||
|
final DbObject objToFind)
|
||||||
|
{
|
||||||
|
return (DbObject) CollectionUtils.find(objects, new Predicate()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean evaluate(Object o)
|
||||||
|
{
|
||||||
|
DbObject object = (DbObject) o;
|
||||||
|
return object.sameAs(objToFind);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compareSimpleCollections(Collection<? extends Object> leftCollection,
|
||||||
|
Collection<? extends Object> rightCollection, Differences differences)
|
||||||
|
{
|
||||||
|
for (Object leftObj : leftCollection)
|
||||||
|
{
|
||||||
|
if (rightCollection.contains(leftObj))
|
||||||
|
{
|
||||||
|
// The same valued object in the right hand collection as in the left.
|
||||||
|
differences.add(Where.IN_BOTH_NO_DIFFERENCE, leftObj, leftObj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No equivalent object in the right hand collection.
|
||||||
|
differences.add(Where.ONLY_IN_LEFT, leftObj, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identify objects in the right collection but not the left
|
||||||
|
for (Object rightObj : rightCollection)
|
||||||
|
{
|
||||||
|
if (!leftCollection.contains(rightObj))
|
||||||
|
{
|
||||||
|
// No equivalent object in the left hand collection.
|
||||||
|
differences.add(Where.ONLY_IN_RIGHT, null, rightObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compareCollections(Collection<? extends DbObject> leftCollection,
|
||||||
|
Collection<? extends DbObject> rightCollection, Differences differences)
|
||||||
|
{
|
||||||
|
for (DbObject leftObj : leftCollection)
|
||||||
|
{
|
||||||
|
DbObject rightObj = SchemaUtils.findSameObjectAs(rightCollection, leftObj);
|
||||||
|
|
||||||
|
if (rightObj != null)
|
||||||
|
{
|
||||||
|
// There is an equivalent object in the right hand collection as
|
||||||
|
// in the left.
|
||||||
|
leftObj.diff(rightObj, differences);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No equivalent object in the right hand collection.
|
||||||
|
differences.add(Where.ONLY_IN_LEFT, leftObj, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identify objects in the right collection but not the left
|
||||||
|
for (DbObject rightObj : rightCollection)
|
||||||
|
{
|
||||||
|
if (!leftCollection.contains(rightObj))
|
||||||
|
{
|
||||||
|
// No equivalent object in the left hand collection.
|
||||||
|
differences.add(Where.ONLY_IN_RIGHT, null, rightObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compareSimple(Object left, Object right, Differences differences)
|
||||||
|
{
|
||||||
|
if (left == null && right == null)
|
||||||
|
{
|
||||||
|
differences.add(Where.IN_BOTH_NO_DIFFERENCE, null, null);
|
||||||
|
}
|
||||||
|
else if (left != null && left.equals(right))
|
||||||
|
{
|
||||||
|
differences.add(Where.IN_BOTH_NO_DIFFERENCE, left, right);
|
||||||
|
}
|
||||||
|
else if (left == null && right != null)
|
||||||
|
{
|
||||||
|
differences.add(Where.ONLY_IN_RIGHT, null, right);
|
||||||
|
}
|
||||||
|
else if (left != null && right == null)
|
||||||
|
{
|
||||||
|
differences.add(Where.ONLY_IN_LEFT, left, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -18,8 +18,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.util.schemacomp.model;
|
package org.alfresco.util.schemacomp.model;
|
||||||
|
|
||||||
|
import org.alfresco.util.schemacomp.Differences;
|
||||||
|
import org.alfresco.util.schemacomp.SchemaUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: comment me!
|
* Useful base class for many, if not all the {@link DbObject} implementations.
|
||||||
|
*
|
||||||
* @author Matt Ward
|
* @author Matt Ward
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractDbObject implements DbObject
|
public abstract class AbstractDbObject implements DbObject
|
||||||
@@ -60,9 +65,18 @@ public abstract class AbstractDbObject implements DbObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getIdentifier()
|
public boolean sameAs(DbObject other)
|
||||||
{
|
{
|
||||||
return getName();
|
if (getName() != null && other != null && other.getName() != null)
|
||||||
|
{
|
||||||
|
return getName().equals(other.getName());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Only other way we can know if they are the same is if they are
|
||||||
|
// the exact same object reference.
|
||||||
|
return this == other;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -88,4 +102,60 @@ public abstract class AbstractDbObject implements DbObject
|
|||||||
else if (!this.name.equals(other.name)) return false;
|
else if (!this.name.equals(other.name)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
sb.append(getClass().getSimpleName());
|
||||||
|
sb.append("[name=");
|
||||||
|
|
||||||
|
if (getName() != null)
|
||||||
|
{
|
||||||
|
sb.append(getName());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.append("null");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("]");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides an implementation of {@link DbObject#diff(DbObject, Differences)}. The template
|
||||||
|
* method {@link #doDiff(DbObject, Differences)} provides the subclass specific diffing logic,
|
||||||
|
* whilst this method handles the workflow required in most cases: set the path's prefix that will be
|
||||||
|
* used to explain where differences occur; compare the name fields of the two objects; delegate to the
|
||||||
|
* subclass specific diffing (if any); remove the last path addition ready for the next object to perform
|
||||||
|
* its diff correctly.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void diff(DbObject right, Differences differences)
|
||||||
|
{
|
||||||
|
if (name != null && StringUtils.hasText(name))
|
||||||
|
{
|
||||||
|
differences.pushPath(name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
differences.pushPath("<" + getClass().getSimpleName() + ">");
|
||||||
|
}
|
||||||
|
SchemaUtils.compareSimple(name, right.getName(), differences);
|
||||||
|
doDiff(right, differences);
|
||||||
|
differences.popPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override this method to provide subclass specific diffing logic.
|
||||||
|
*
|
||||||
|
* @param right
|
||||||
|
* @param differences
|
||||||
|
*/
|
||||||
|
protected void doDiff(DbObject right, Differences differences)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,8 +18,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.util.schemacomp.model;
|
package org.alfresco.util.schemacomp.model;
|
||||||
|
|
||||||
|
import org.alfresco.util.schemacomp.Differences;
|
||||||
|
import org.alfresco.util.schemacomp.SchemaUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: comment me!
|
* Represents a column in a database table.
|
||||||
|
*
|
||||||
* @author Matt Ward
|
* @author Matt Ward
|
||||||
*/
|
*/
|
||||||
public class Column extends AbstractDbObject
|
public class Column extends AbstractDbObject
|
||||||
@@ -27,6 +31,21 @@ public class Column extends AbstractDbObject
|
|||||||
private String type;
|
private String type;
|
||||||
private boolean nullable;
|
private boolean nullable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a Column.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @param type
|
||||||
|
* @param nullable
|
||||||
|
*/
|
||||||
|
public Column(String name, String type, boolean nullable)
|
||||||
|
{
|
||||||
|
super(name);
|
||||||
|
this.type = type;
|
||||||
|
this.nullable = nullable;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the type
|
* @return the type
|
||||||
*/
|
*/
|
||||||
@@ -84,4 +103,12 @@ public class Column extends AbstractDbObject
|
|||||||
else if (!this.type.equals(other.type)) return false;
|
else if (!this.type.equals(other.type)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDiff(DbObject right, Differences differences)
|
||||||
|
{
|
||||||
|
Column rightColumn = (Column) right;
|
||||||
|
SchemaUtils.compareSimple(type, rightColumn.type, differences);
|
||||||
|
SchemaUtils.compareSimple(nullable, rightColumn.nullable, differences);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,12 +18,39 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.util.schemacomp.model;
|
package org.alfresco.util.schemacomp.model;
|
||||||
|
|
||||||
|
import org.alfresco.util.schemacomp.Differences;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All database objects to be modelled for schema comparisons must implement this interface.
|
* All database objects to be modelled for schema comparisons must implement this interface, examples
|
||||||
|
* of which include: schemas, tables, indexes, primary keys, foreign keys, sequences and columns.
|
||||||
*
|
*
|
||||||
* @author Matt Ward
|
* @author Matt Ward
|
||||||
*/
|
*/
|
||||||
public interface DbObject
|
public interface DbObject
|
||||||
{
|
{
|
||||||
Object getIdentifier();
|
/**
|
||||||
|
* Are the two <code>DbObject</code>s logically the same? For example two Index objects may have
|
||||||
|
* different names, but are the same index as they both index the same columns for the same table.
|
||||||
|
*
|
||||||
|
* @param other
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean sameAs(DbObject other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All items can be asked for their name, but it may be null.
|
||||||
|
*
|
||||||
|
* @return Name if available, null otherwise.
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a report of differences between this object ('left') and another object ('right').
|
||||||
|
* Differences between the left and right objects under inspection are captured in the {@link Differences}
|
||||||
|
* object passed in to this method.
|
||||||
|
*
|
||||||
|
* @param right The object to compare against.
|
||||||
|
* @param differences A collector of differences.
|
||||||
|
*/
|
||||||
|
void diff(DbObject right, Differences differences);
|
||||||
}
|
}
|
||||||
|
@@ -18,9 +18,14 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.util.schemacomp.model;
|
package org.alfresco.util.schemacomp.model;
|
||||||
|
|
||||||
|
import org.alfresco.util.schemacomp.Differences;
|
||||||
|
import org.alfresco.util.schemacomp.SchemaUtils;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: comment me!
|
* Represents a foreign key on a database table (<code>localColumn</code>) that references
|
||||||
|
* <code>targetTable.targetColumn</code>
|
||||||
|
*
|
||||||
* @author Matt Ward
|
* @author Matt Ward
|
||||||
*/
|
*/
|
||||||
public class ForeignKey extends AbstractDbObject
|
public class ForeignKey extends AbstractDbObject
|
||||||
@@ -30,6 +35,22 @@ public class ForeignKey extends AbstractDbObject
|
|||||||
private String targetColumn;
|
private String targetColumn;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param fkName
|
||||||
|
* @param localColumn
|
||||||
|
* @param targetTable
|
||||||
|
* @param targetColumn
|
||||||
|
*/
|
||||||
|
public ForeignKey(String fkName, String localColumn, String targetTable, String targetColumn)
|
||||||
|
{
|
||||||
|
super(fkName);
|
||||||
|
this.localColumn = localColumn;
|
||||||
|
this.targetTable = targetTable;
|
||||||
|
this.targetColumn = targetColumn;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the localColumn
|
* @return the localColumn
|
||||||
*/
|
*/
|
||||||
@@ -37,6 +58,7 @@ public class ForeignKey extends AbstractDbObject
|
|||||||
{
|
{
|
||||||
return this.localColumn;
|
return this.localColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param localColumn the localColumn to set
|
* @param localColumn the localColumn to set
|
||||||
*/
|
*/
|
||||||
@@ -44,6 +66,7 @@ public class ForeignKey extends AbstractDbObject
|
|||||||
{
|
{
|
||||||
this.localColumn = localColumn;
|
this.localColumn = localColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the targetTable
|
* @return the targetTable
|
||||||
*/
|
*/
|
||||||
@@ -51,6 +74,7 @@ public class ForeignKey extends AbstractDbObject
|
|||||||
{
|
{
|
||||||
return this.targetTable;
|
return this.targetTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param targetTable the targetTable to set
|
* @param targetTable the targetTable to set
|
||||||
*/
|
*/
|
||||||
@@ -58,6 +82,7 @@ public class ForeignKey extends AbstractDbObject
|
|||||||
{
|
{
|
||||||
this.targetTable = targetTable;
|
this.targetTable = targetTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the targetColumn
|
* @return the targetColumn
|
||||||
*/
|
*/
|
||||||
@@ -65,6 +90,7 @@ public class ForeignKey extends AbstractDbObject
|
|||||||
{
|
{
|
||||||
return this.targetColumn;
|
return this.targetColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param targetColumn the targetColumn to set
|
* @param targetColumn the targetColumn to set
|
||||||
*/
|
*/
|
||||||
@@ -107,5 +133,15 @@ public class ForeignKey extends AbstractDbObject
|
|||||||
}
|
}
|
||||||
else if (!this.targetTable.equals(other.targetTable)) return false;
|
else if (!this.targetTable.equals(other.targetTable)) return false;
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDiff(DbObject right, Differences differences)
|
||||||
|
{
|
||||||
|
ForeignKey rightFK = (ForeignKey) right;
|
||||||
|
SchemaUtils.compareSimple(localColumn, rightFK.localColumn, differences);
|
||||||
|
SchemaUtils.compareSimple(targetTable, rightFK.targetTable, differences);
|
||||||
|
SchemaUtils.compareSimple(targetColumn, rightFK.targetColumn, differences);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,8 +20,12 @@ package org.alfresco.util.schemacomp.model;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.alfresco.util.schemacomp.Differences;
|
||||||
|
import org.alfresco.util.schemacomp.SchemaUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: comment me!
|
* Represents an index on a table.
|
||||||
|
*
|
||||||
* @author Matt Ward
|
* @author Matt Ward
|
||||||
*/
|
*/
|
||||||
public class Index extends AbstractDbObject
|
public class Index extends AbstractDbObject
|
||||||
@@ -29,6 +33,15 @@ public class Index extends AbstractDbObject
|
|||||||
private List<String> columnNames;
|
private List<String> columnNames;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param columnNames
|
||||||
|
*/
|
||||||
|
public Index(String name, List<String> columnNames)
|
||||||
|
{
|
||||||
|
super(name);
|
||||||
|
this.columnNames = columnNames;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the columnNames
|
* @return the columnNames
|
||||||
*/
|
*/
|
||||||
@@ -45,18 +58,6 @@ public class Index extends AbstractDbObject
|
|||||||
this.columnNames = columnNames;
|
this.columnNames = columnNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: column names should be fully qualified, OR an Index should have a table name field
|
|
||||||
* and the identifier should include it.
|
|
||||||
*
|
|
||||||
* @return the Index identifier
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Object getIdentifier()
|
|
||||||
{
|
|
||||||
return getColumnNames();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode()
|
public int hashCode()
|
||||||
{
|
{
|
||||||
@@ -80,4 +81,34 @@ public class Index extends AbstractDbObject
|
|||||||
else if (!this.columnNames.equals(other.columnNames)) return false;
|
else if (!this.columnNames.equals(other.columnNames)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean sameAs(DbObject o)
|
||||||
|
{
|
||||||
|
if (o != null && o instanceof Index)
|
||||||
|
{
|
||||||
|
Index other = (Index) o;
|
||||||
|
|
||||||
|
if (getName() != null)
|
||||||
|
{
|
||||||
|
if (getName().equals(other.getName()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return columnNames.equals(other.getColumnNames());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDiff(DbObject right, Differences differences)
|
||||||
|
{
|
||||||
|
Index rightIndex = (Index) right;
|
||||||
|
SchemaUtils.compareSimpleCollections(columnNames, rightIndex.columnNames, differences);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,8 +20,12 @@ package org.alfresco.util.schemacomp.model;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.alfresco.util.schemacomp.Differences;
|
||||||
|
import org.alfresco.util.schemacomp.SchemaUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: comment me!
|
* Primary key on a table.
|
||||||
|
*
|
||||||
* @author Matt Ward
|
* @author Matt Ward
|
||||||
*/
|
*/
|
||||||
public class PrimaryKey extends AbstractDbObject
|
public class PrimaryKey extends AbstractDbObject
|
||||||
@@ -68,4 +72,11 @@ public class PrimaryKey extends AbstractDbObject
|
|||||||
else if (!this.columnNames.equals(other.columnNames)) return false;
|
else if (!this.columnNames.equals(other.columnNames)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDiff(DbObject right, Differences differences)
|
||||||
|
{
|
||||||
|
PrimaryKey rightPK = (PrimaryKey) right;
|
||||||
|
SchemaUtils.compareSimpleCollections(columnNames, rightPK.columnNames, differences);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,50 +18,50 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.util.schemacomp.model;
|
package org.alfresco.util.schemacomp.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
import org.alfresco.util.schemacomp.Differences;
|
||||||
|
import org.alfresco.util.schemacomp.SchemaUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instances of this class will represent a database schema.
|
* Instances of this class represent a database schema.
|
||||||
*
|
*
|
||||||
* @author Matt Ward
|
* @author Matt Ward
|
||||||
*/
|
*/
|
||||||
public class
|
public class Schema extends AbstractDbObject implements Iterable<DbObject>
|
||||||
Schema extends AbstractDbObject implements Iterable<DbObject>
|
|
||||||
{
|
{
|
||||||
private final Map<Object, DbObject> objects = new LinkedHashMap<Object, DbObject>();
|
private final List<DbObject> objects = new ArrayList<DbObject>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param key
|
* Construct a schema with the given name.
|
||||||
* @return
|
*
|
||||||
|
* @param name
|
||||||
*/
|
*/
|
||||||
public DbObject get(Object key)
|
public Schema(String name)
|
||||||
{
|
{
|
||||||
return this.objects.get(key);
|
super(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void add(DbObject dbObject)
|
||||||
* @param table
|
|
||||||
*/
|
|
||||||
public void put(DbObject dbObject)
|
|
||||||
{
|
{
|
||||||
objects.put(dbObject.getIdentifier(), dbObject);
|
objects.add(dbObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<DbObject> iterator()
|
public Iterator<DbObject> iterator()
|
||||||
{
|
{
|
||||||
return objects.values().iterator();
|
return objects.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param identifier
|
* @param identifier
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean contains(Object identifier)
|
public boolean contains(DbObject object)
|
||||||
{
|
{
|
||||||
return objects.containsKey(identifier);
|
return objects.contains(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -87,4 +87,12 @@ Schema extends AbstractDbObject implements Iterable<DbObject>
|
|||||||
else if (!this.objects.equals(other.objects)) return false;
|
else if (!this.objects.equals(other.objects)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDiff(DbObject right, Differences differences)
|
||||||
|
{
|
||||||
|
Schema rightSchema = (Schema) right;
|
||||||
|
SchemaUtils.compareCollections(objects, rightSchema.objects, differences);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,10 +18,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.util.schemacomp.model;
|
package org.alfresco.util.schemacomp.model;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: comment me!
|
* Represents a database sequence.
|
||||||
|
*
|
||||||
* @author Matt Ward
|
* @author Matt Ward
|
||||||
*/
|
*/
|
||||||
public class Sequence extends AbstractDbObject
|
public class Sequence extends AbstractDbObject
|
||||||
{
|
{
|
||||||
|
// No subclass specific data/behaviour at present.
|
||||||
}
|
}
|
||||||
|
@@ -22,8 +22,11 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.alfresco.util.schemacomp.Differences;
|
||||||
|
import org.alfresco.util.schemacomp.SchemaUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instances of this class will represent a database table.
|
* Instances of this class represent a database table.
|
||||||
*
|
*
|
||||||
* @author Matt Ward
|
* @author Matt Ward
|
||||||
*/
|
*/
|
||||||
@@ -31,23 +34,26 @@ public class Table extends AbstractDbObject
|
|||||||
{
|
{
|
||||||
private List<Column> columns = new ArrayList<Column>();
|
private List<Column> columns = new ArrayList<Column>();
|
||||||
private PrimaryKey primaryKey;
|
private PrimaryKey primaryKey;
|
||||||
private ForeignKey foreignKey;
|
private List<ForeignKey> foreignKeys = new ArrayList<ForeignKey>();
|
||||||
private List<Index> indexes = new ArrayList<Index>();
|
private List<Index> indexes = new ArrayList<Index>();
|
||||||
|
|
||||||
|
|
||||||
public Table(String name, Collection<Column> columns, PrimaryKey primaryKey,
|
public Table(String name, Collection<Column> columns, PrimaryKey primaryKey,
|
||||||
ForeignKey foreignKey, Collection<Index> indexes)
|
Collection<ForeignKey> foreignKeys, Collection<Index> indexes)
|
||||||
{
|
{
|
||||||
super(name);
|
super(name);
|
||||||
if (columns != null)
|
if (columns != null)
|
||||||
{
|
{
|
||||||
columns.addAll(columns);
|
this.columns.addAll(columns);
|
||||||
}
|
}
|
||||||
this.primaryKey = primaryKey;
|
this.primaryKey = primaryKey;
|
||||||
this.foreignKey = foreignKey;
|
if (foreignKeys != null)
|
||||||
|
{
|
||||||
|
this.foreignKeys.addAll(foreignKeys);
|
||||||
|
}
|
||||||
if (indexes != null)
|
if (indexes != null)
|
||||||
{
|
{
|
||||||
indexes.addAll(indexes);
|
this.indexes.addAll(indexes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,20 +95,20 @@ public class Table extends AbstractDbObject
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the foreignKey
|
* @return the foreignKeys
|
||||||
*/
|
*/
|
||||||
public ForeignKey getForeignKey()
|
public List<ForeignKey> getForeignKeys()
|
||||||
{
|
{
|
||||||
return this.foreignKey;
|
return this.foreignKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param foreignKey the foreignKey to set
|
* @param foreignKeys the foreignKeys to set
|
||||||
*/
|
*/
|
||||||
public void setForeignKey(ForeignKey foreignKey)
|
public void setForeignKeys(List<ForeignKey> foreignKeys)
|
||||||
{
|
{
|
||||||
this.foreignKey = foreignKey;
|
this.foreignKeys = foreignKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -130,7 +136,7 @@ public class Table extends AbstractDbObject
|
|||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
int result = super.hashCode();
|
int result = super.hashCode();
|
||||||
result = prime * result + ((this.columns == null) ? 0 : this.columns.hashCode());
|
result = prime * result + ((this.columns == null) ? 0 : this.columns.hashCode());
|
||||||
result = prime * result + ((this.foreignKey == null) ? 0 : this.foreignKey.hashCode());
|
result = prime * result + ((this.foreignKeys == null) ? 0 : this.foreignKeys.hashCode());
|
||||||
result = prime * result + ((this.indexes == null) ? 0 : this.indexes.hashCode());
|
result = prime * result + ((this.indexes == null) ? 0 : this.indexes.hashCode());
|
||||||
result = prime * result + ((this.primaryKey == null) ? 0 : this.primaryKey.hashCode());
|
result = prime * result + ((this.primaryKey == null) ? 0 : this.primaryKey.hashCode());
|
||||||
return result;
|
return result;
|
||||||
@@ -149,21 +155,34 @@ public class Table extends AbstractDbObject
|
|||||||
if (other.columns != null) return false;
|
if (other.columns != null) return false;
|
||||||
}
|
}
|
||||||
else if (!this.columns.equals(other.columns)) return false;
|
else if (!this.columns.equals(other.columns)) return false;
|
||||||
if (this.foreignKey == null)
|
if (this.foreignKeys == null)
|
||||||
{
|
{
|
||||||
if (other.foreignKey != null) return false;
|
if (other.foreignKeys != null) return false;
|
||||||
}
|
}
|
||||||
else if (!this.foreignKey.equals(other.foreignKey)) return false;
|
else if (!this.foreignKeys.equals(other.foreignKeys)) return false;
|
||||||
if (this.indexes == null)
|
if (this.indexes == null)
|
||||||
{
|
{
|
||||||
if (other.indexes != null) return false;
|
if (other.indexes != null) return false;
|
||||||
}
|
}
|
||||||
else if (!this.indexes.equals(other.indexes)) return false;
|
else if (!this.indexes.equals(other.indexes)) return false;
|
||||||
if (this.primaryKey == null)
|
// TODO: this is difficult, equals probably should include this, but diffs shouldn't -
|
||||||
{
|
// decide what to do about this.
|
||||||
if (other.primaryKey != null) return false;
|
// if (this.primaryKey == null)
|
||||||
}
|
// {
|
||||||
else if (!this.primaryKey.equals(other.primaryKey)) return false;
|
// if (other.primaryKey != null) return false;
|
||||||
|
// }
|
||||||
|
// else if (!this.primaryKey.equals(other.primaryKey)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDiff(DbObject other, Differences differences)
|
||||||
|
{
|
||||||
|
Table rightTable = (Table) other;
|
||||||
|
SchemaUtils.compareCollections(columns, rightTable.columns, differences);
|
||||||
|
primaryKey.diff(rightTable.primaryKey, differences);
|
||||||
|
SchemaUtils.compareCollections(foreignKeys, rightTable.foreignKeys, differences);
|
||||||
|
SchemaUtils.compareCollections(indexes, rightTable.indexes, differences);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user