mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
ALF-11030: Create object graph from real database schema
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@31625 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -68,8 +68,11 @@ public class DbObjectXMLTransformer
|
||||
String tagName = dbObject.getClass().getSimpleName().toLowerCase();
|
||||
final AttributesImpl attribs = new AttributesImpl();
|
||||
attribs.addAttribute("", "", "name", "CDATA", dbObject.getName());
|
||||
// Add class-specific attributes.
|
||||
addAttributes(dbObject, attribs);
|
||||
xmlOut.startElement("", "", tagName, attribs);
|
||||
|
||||
|
||||
// The element's contents can optionally be populated with class-specific content.
|
||||
transformDbObject(dbObject);
|
||||
|
||||
@@ -77,6 +80,21 @@ public class DbObjectXMLTransformer
|
||||
xmlOut.endElement("", "", tagName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add class-specific attributes.
|
||||
*
|
||||
* @param dbObject
|
||||
* @param attribs
|
||||
*/
|
||||
private void addAttributes(DbObject dbObject, AttributesImpl attribs)
|
||||
{
|
||||
if (dbObject instanceof Index)
|
||||
{
|
||||
Index index = (Index) dbObject;
|
||||
attribs.addAttribute("", "", "unique", "CDATA", Boolean.toString(index.isUnique()));
|
||||
}
|
||||
}
|
||||
|
||||
private void transformDbObject(DbObject dbObject) throws SAXException
|
||||
{
|
||||
if (dbObject instanceof Schema)
|
||||
|
@@ -142,7 +142,7 @@ public class DbObjectXMLTransformerTest
|
||||
BufferedReader reader = new BufferedReader(new StringReader(writer.toString()));
|
||||
dumpOutput();
|
||||
assertHasPreamble(reader);
|
||||
assertEquals("<index name=\"index_name\">", reader.readLine());
|
||||
assertEquals("<index name=\"index_name\" unique=\"false\">", reader.readLine());
|
||||
assertEquals(" <columnnames>", reader.readLine());
|
||||
assertEquals(" <columnname>first</columnname>", reader.readLine());
|
||||
assertEquals(" <columnname>second</columnname>", reader.readLine());
|
||||
|
383
source/java/org/alfresco/util/schemacomp/ExportDb.java
Normal file
383
source/java/org/alfresco/util/schemacomp/ExportDb.java
Normal file
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* 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.lang.reflect.Field;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.alfresco.util.PropertyCheck;
|
||||
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.Table;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.PostgreSQLDialect;
|
||||
import org.hibernate.dialect.TypeNames;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
|
||||
/**
|
||||
* Exports a database schema to an in-memory {@link Schema} object.
|
||||
*
|
||||
* @author Matt Ward
|
||||
*/
|
||||
public class ExportDb
|
||||
{
|
||||
/** Reverse map from database types to JDBC types (loaded from a Hibernate dialect). */
|
||||
private final Map<String, Integer> reverseTypeMap = new TreeMap<String, Integer>();
|
||||
|
||||
/** The JDBC DataSource. */
|
||||
private DataSource dataSource;
|
||||
|
||||
/** The object graph we're building */
|
||||
private Schema schema;
|
||||
|
||||
private Dialect dialect;
|
||||
|
||||
|
||||
|
||||
public ExportDb(ApplicationContext context) throws Exception
|
||||
{
|
||||
this((DataSource) context.getBean("dataSource"),
|
||||
(Dialect) context.getBean("dialect"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance of the tool within the context of an existing database connection
|
||||
*
|
||||
* @param connection the database connection to use for metadata queries
|
||||
* @param dialect the Hibernate dialect
|
||||
*/
|
||||
public ExportDb(final DataSource dataSource, final Dialect dialect) throws Exception
|
||||
{
|
||||
this.dataSource = dataSource;
|
||||
this.dialect = dialect;
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the fields ready to perform the database metadata reading
|
||||
* @param dialect the Hibernate dialect
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void init() throws Exception
|
||||
{
|
||||
final Field typeNamesField = Dialect.class.getDeclaredField("typeNames");
|
||||
typeNamesField.setAccessible(true);
|
||||
final TypeNames typeNames = (TypeNames) typeNamesField.get(dialect);
|
||||
final Field defaultsField = TypeNames.class.getDeclaredField("defaults");
|
||||
defaultsField.setAccessible(true);
|
||||
final Map<Integer, String> forwardMap2 = (Map<Integer, String>) defaultsField.get(typeNames);
|
||||
for (final Map.Entry<Integer, String> e : forwardMap2.entrySet())
|
||||
{
|
||||
this.reverseTypeMap.put(e.getValue(), e.getKey());
|
||||
}
|
||||
|
||||
final Field weightedField = TypeNames.class.getDeclaredField("weighted");
|
||||
weightedField.setAccessible(true);
|
||||
final Map<Integer, Map<Integer, String>> forwardMap1 = (Map<Integer, Map<Integer, String>>) weightedField
|
||||
.get(typeNames);
|
||||
for (final Map.Entry<Integer, Map<Integer, String>> e : forwardMap1.entrySet())
|
||||
{
|
||||
for (final String type : e.getValue().values())
|
||||
{
|
||||
this.reverseTypeMap.put(type, e.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void execute() throws Exception
|
||||
{
|
||||
PropertyCheck.mandatory(this, "dataSource", dataSource);
|
||||
// Get a Connection
|
||||
Connection connection = dataSource.getConnection();
|
||||
|
||||
try
|
||||
{
|
||||
connection.setAutoCommit(false);
|
||||
execute(connection);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { connection.close(); } catch (Throwable e) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void execute(Connection con) throws Exception
|
||||
{
|
||||
final DatabaseMetaData dbmd = con.getMetaData();
|
||||
|
||||
// Assume that if there are schemas, we want the one named after the connection user or the one called "dbo" (MS
|
||||
// SQL hack)
|
||||
String schemaName = null;
|
||||
final ResultSet schemas = dbmd.getSchemas();
|
||||
while (schemas.next())
|
||||
{
|
||||
final String thisSchema = schemas.getString("TABLE_SCHEM");
|
||||
if (thisSchema.equals(dbmd.getUserName()) || thisSchema.equalsIgnoreCase("dbo"))
|
||||
{
|
||||
schemaName = thisSchema;
|
||||
break;
|
||||
}
|
||||
}
|
||||
schemas.close();
|
||||
|
||||
schema = new Schema(schemaName);
|
||||
|
||||
final ResultSet tables = dbmd.getTables(null, schemaName, "%", new String[]
|
||||
{
|
||||
"TABLE", "VIEW"
|
||||
});
|
||||
|
||||
|
||||
while (tables.next())
|
||||
{
|
||||
final String tableName = tables.getString("TABLE_NAME");
|
||||
|
||||
// Oracle hack: ignore tables in the recycle bin
|
||||
if (tableName.startsWith("BIN$"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Table table = new Table(tableName);
|
||||
schema.add(table);
|
||||
|
||||
// Table columns
|
||||
final ResultSet columns = dbmd.getColumns(null, tables.getString("TABLE_SCHEM"), tableName, "%");
|
||||
while (columns.next())
|
||||
{
|
||||
String columnName = columns.getString("COLUMN_NAME");
|
||||
Column column = new Column(columnName);
|
||||
|
||||
String dbType = columns.getString("TYPE_NAME");
|
||||
int colSize = columns.getInt("COLUMN_SIZE");
|
||||
int scale = columns.getInt("DECIMAL_DIGITS");
|
||||
int jdbcType = columns.getInt("DATA_TYPE");
|
||||
String type = generateType(dbType, colSize, scale, jdbcType);
|
||||
column.setType(type);
|
||||
|
||||
String nullableString = columns.getString("IS_NULLABLE");
|
||||
column.setNullable(parseBoolean(nullableString));
|
||||
|
||||
table.getColumns().add(column);
|
||||
}
|
||||
columns.close();
|
||||
|
||||
|
||||
// Primary key
|
||||
final ResultSet primarykeycols = dbmd.getPrimaryKeys(null, tables.getString("TABLE_SCHEM"), tableName);
|
||||
|
||||
PrimaryKey pk = null;
|
||||
|
||||
while (primarykeycols.next())
|
||||
{
|
||||
if (pk == null)
|
||||
{
|
||||
String pkName = primarykeycols.getString("PK_NAME");
|
||||
pk = new PrimaryKey(pkName);
|
||||
}
|
||||
String columnName = primarykeycols.getString("COLUMN_NAME");
|
||||
pk.getColumnNames().add(columnName);
|
||||
}
|
||||
primarykeycols.close();
|
||||
|
||||
// If this table has a primary key, add it.
|
||||
if (pk != null)
|
||||
{
|
||||
table.setPrimaryKey(pk);
|
||||
}
|
||||
|
||||
|
||||
// Indexes
|
||||
final ResultSet indexes = dbmd.getIndexInfo(null, tables.getString("TABLE_SCHEM"), tableName, false, true);
|
||||
String lastIndexName = "";
|
||||
|
||||
Index index = null;
|
||||
|
||||
while (indexes.next())
|
||||
{
|
||||
final String indexName = indexes.getString("INDEX_NAME");
|
||||
if (indexName == null)
|
||||
{
|
||||
// Oracle seems to have some dummy index entries
|
||||
continue;
|
||||
}
|
||||
// Skip the index corresponding to the PK if it is mentioned
|
||||
else if (indexName.equals(table.getPrimaryKey().getName()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!indexName.equals(lastIndexName))
|
||||
{
|
||||
index = new Index(indexName);
|
||||
index.setUnique(!indexes.getBoolean("NON_UNIQUE"));
|
||||
table.getIndexes().add(index);
|
||||
lastIndexName = indexName;
|
||||
}
|
||||
if (index != null)
|
||||
{
|
||||
String columnName = indexes.getString("COLUMN_NAME");
|
||||
index.getColumnNames().add(columnName);
|
||||
}
|
||||
}
|
||||
indexes.close();
|
||||
|
||||
|
||||
|
||||
final ResultSet foreignkeys = dbmd.getImportedKeys(null, tables.getString("TABLE_SCHEM"), tableName);
|
||||
String lastKeyName = "";
|
||||
|
||||
ForeignKey fk = null;
|
||||
|
||||
while (foreignkeys.next())
|
||||
{
|
||||
final String keyName = foreignkeys.getString("FK_NAME");
|
||||
if (!keyName.equals(lastKeyName))
|
||||
{
|
||||
fk = new ForeignKey(keyName);
|
||||
table.getForeignKeys().add(fk);
|
||||
lastKeyName = keyName;
|
||||
}
|
||||
if (fk != null)
|
||||
{
|
||||
fk.setLocalColumn(foreignkeys.getString("FKCOLUMN_NAME"));
|
||||
fk.setTargetTable(foreignkeys.getString("PKTABLE_NAME"));
|
||||
fk.setTargetColumn(foreignkeys.getString("PKCOLUMN_NAME"));
|
||||
}
|
||||
}
|
||||
foreignkeys.close();
|
||||
}
|
||||
tables.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a boolean string as used in the database, to a boolean value.
|
||||
*
|
||||
* @param nullableString
|
||||
* @return true if "YES", false if "NO"
|
||||
*/
|
||||
private boolean parseBoolean(String nullableString)
|
||||
{
|
||||
// TODO: what about (from the javadoc):
|
||||
// empty string --- if the nullability for the parameter is unknown
|
||||
if (nullableString.equals("NO"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (nullableString.equals("YES"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unsupported term \"" + nullableString +
|
||||
"\", perhaps this database doesn't use YES/NO for booleans?");
|
||||
|
||||
}
|
||||
|
||||
protected String generateType(final String dbType, int size, final int digits, int sqlType)
|
||||
throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
String dbName = dbType.toLowerCase() + "(" + size + ")";
|
||||
if (this.reverseTypeMap.containsKey(dbName))
|
||||
{
|
||||
// the map may contain an exact match, e.g. "char(1)"
|
||||
return dbName;
|
||||
}
|
||||
|
||||
dbName = dbType.toLowerCase() + "(" + size + ", " + digits + ")";
|
||||
if (this.reverseTypeMap.containsKey(dbName))
|
||||
{
|
||||
// the map may contain an exact match, e.g. "numeric(3, 1)"
|
||||
return dbName;
|
||||
}
|
||||
else
|
||||
{
|
||||
String precisionScaleType = dbType + "(" + size + ", " + digits + ")";
|
||||
String sizeType = dbType + "(" + size + ")";
|
||||
|
||||
for (String key : reverseTypeMap.keySet())
|
||||
{
|
||||
// Populate the placeholders, examples:
|
||||
// varchar($l) => varchar(20)
|
||||
// numeric($p, $s) => numeric(5, 2)
|
||||
String popKey = key.replaceAll("\\$p", String.valueOf(size));
|
||||
popKey = popKey.replaceAll("\\$s", String.valueOf(digits));
|
||||
popKey = popKey.replaceAll("\\$l", String.valueOf(size));
|
||||
|
||||
// If the populated key matches a precision/scale type or a size type
|
||||
// then the populated key gives us the string we're after.
|
||||
if (popKey.equals(precisionScaleType) || popKey.equals(sizeType))
|
||||
{
|
||||
return popKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dbType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the schema
|
||||
*/
|
||||
public Schema getSchema()
|
||||
{
|
||||
return this.schema;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException
|
||||
{
|
||||
ExportDb exportDb = null;
|
||||
try
|
||||
{
|
||||
exportDb = new ExportDb(null, new PostgreSQLDialect());
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
throw new RuntimeException(error);
|
||||
}
|
||||
|
||||
if (exportDb != null)
|
||||
{
|
||||
String varchar = exportDb.generateType("varchar", 20, 0, 12);
|
||||
System.out.println("varchar: " + varchar);
|
||||
|
||||
String int4 = exportDb.generateType("int4", 20, 0, 4);
|
||||
System.out.println("int4: " + int4);
|
||||
}
|
||||
}
|
||||
}
|
251
source/java/org/alfresco/util/schemacomp/ExportDbTest.java
Normal file
251
source/java/org/alfresco/util/schemacomp/ExportDbTest.java
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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.Iterator;
|
||||
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
import org.alfresco.util.schemacomp.model.Column;
|
||||
import org.alfresco.util.schemacomp.model.DbObject;
|
||||
import org.alfresco.util.schemacomp.model.ForeignKey;
|
||||
import org.alfresco.util.schemacomp.model.Index;
|
||||
import org.alfresco.util.schemacomp.model.Schema;
|
||||
import org.alfresco.util.schemacomp.model.Sequence;
|
||||
import org.alfresco.util.schemacomp.model.Table;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
* Tests for the ExportDb class.
|
||||
*
|
||||
* @author Matt Ward
|
||||
*/
|
||||
public class ExportDbTest
|
||||
{
|
||||
private ApplicationContext ctx;
|
||||
private ExportDb exporter;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
ctx = ApplicationContextHelper.getApplicationContext();
|
||||
exporter = new ExportDb(ctx);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void exportDb() throws Exception
|
||||
{
|
||||
exporter.execute();
|
||||
|
||||
Schema schema = exporter.getSchema();
|
||||
System.out.println(schema);
|
||||
|
||||
Table appliedPatchTable = null;
|
||||
Table qNameTable = null;
|
||||
Sequence authoritySeq = null;
|
||||
|
||||
for (DbObject dbo : schema)
|
||||
{
|
||||
if (dbo.getName().equals("alf_applied_patch"))
|
||||
{
|
||||
appliedPatchTable = (Table) dbo;
|
||||
}
|
||||
if (dbo.getName().equals("alf_qname"))
|
||||
{
|
||||
qNameTable = (Table) dbo;
|
||||
}
|
||||
if (dbo.getName().equals("alf_authority_seq"))
|
||||
{
|
||||
authoritySeq = (Sequence) dbo;
|
||||
}
|
||||
}
|
||||
|
||||
checkAppliedPatchTable(appliedPatchTable);
|
||||
checkQNameTable(qNameTable);
|
||||
// TODO: what to do about sequences? They can't easily be retrieved with JDBC's DatabaseMetaData
|
||||
//checkAuthoritySequence(authoritySeq);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param qNameTable
|
||||
*/
|
||||
private void checkQNameTable(Table qNameTable)
|
||||
{
|
||||
/*
|
||||
CREATE TABLE alf_qname
|
||||
(
|
||||
id INT8 NOT NULL,
|
||||
version INT8 NOT NULL,
|
||||
ns_id INT8 NOT NULL,
|
||||
local_name VARCHAR(200) NOT NULL,
|
||||
CONSTRAINT fk_alf_qname_ns FOREIGN KEY (ns_id) REFERENCES alf_namespace (id),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE UNIQUE INDEX ns_id ON alf_qname (ns_id, local_name);
|
||||
CREATE INDEX fk_alf_qname_ns ON alf_qname (ns_id);
|
||||
*/
|
||||
|
||||
assertNotNull("Couldn't find table alf_qname", qNameTable);
|
||||
|
||||
Iterator<Column> colIt = qNameTable.getColumns().iterator();
|
||||
Column col = colIt.next();
|
||||
assertEquals("id", col.getName());
|
||||
assertEquals("int8", col.getType());
|
||||
assertEquals(false, col.isNullable());
|
||||
|
||||
col = colIt.next();
|
||||
assertEquals("version", col.getName());
|
||||
assertEquals("int8", col.getType());
|
||||
assertEquals(false, col.isNullable());
|
||||
|
||||
col = colIt.next();
|
||||
assertEquals("ns_id", col.getName());
|
||||
assertEquals("int8", col.getType());
|
||||
assertEquals(false, col.isNullable());
|
||||
|
||||
col = colIt.next();
|
||||
assertEquals("local_name", col.getName());
|
||||
assertEquals("varchar(200)", col.getType());
|
||||
assertEquals(false, col.isNullable());
|
||||
|
||||
assertEquals(2, qNameTable.getIndexes().size());
|
||||
Iterator<Index> indexIt = qNameTable.getIndexes().iterator();
|
||||
|
||||
Index index = indexIt.next();
|
||||
assertEquals("ns_id", index.getName());
|
||||
assertEquals(true, index.isUnique());
|
||||
assertEquals(2, index.getColumnNames().size());
|
||||
assertEquals("ns_id", index.getColumnNames().get(0));
|
||||
assertEquals("local_name", index.getColumnNames().get(1));
|
||||
|
||||
index = indexIt.next();
|
||||
assertEquals("fk_alf_qname_ns", index.getName());
|
||||
assertEquals(1, index.getColumnNames().size());
|
||||
assertEquals("ns_id", index.getColumnNames().get(0));
|
||||
|
||||
assertEquals("id", qNameTable.getPrimaryKey().getColumnNames().get(0));
|
||||
|
||||
assertEquals(1, qNameTable.getForeignKeys().size());
|
||||
ForeignKey fk = qNameTable.getForeignKeys().get(0);
|
||||
assertEquals("fk_alf_qname_ns", fk.getName());
|
||||
assertEquals("ns_id", fk.getLocalColumn());
|
||||
assertEquals("alf_namespace", fk.getTargetTable());
|
||||
assertEquals("id", fk.getTargetColumn());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param appliedPatch
|
||||
*/
|
||||
private void checkAppliedPatchTable(Table appliedPatch)
|
||||
{
|
||||
/*
|
||||
CREATE TABLE alf_applied_patch
|
||||
(
|
||||
id VARCHAR(64) NOT NULL,
|
||||
description VARCHAR(1024),
|
||||
fixes_from_schema INT4,
|
||||
fixes_to_schema INT4,
|
||||
applied_to_schema INT4,
|
||||
target_schema INT4,
|
||||
applied_on_date TIMESTAMP,
|
||||
applied_to_server VARCHAR(64),
|
||||
was_executed BOOL,
|
||||
succeeded BOOL,
|
||||
report VARCHAR(1024),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
*/
|
||||
assertNotNull("Couldn't find alf_applied_patch", appliedPatch);
|
||||
|
||||
assertEquals("alf_applied_patch", appliedPatch.getName());
|
||||
Iterator<Column> colIt = appliedPatch.getColumns().iterator();
|
||||
Column col = colIt.next();
|
||||
assertEquals("id", col.getName());
|
||||
assertEquals("varchar(64)", col.getType());
|
||||
assertEquals(false, col.isNullable());
|
||||
|
||||
col = colIt.next();
|
||||
assertEquals("description", col.getName());
|
||||
assertEquals("varchar(1024)", col.getType());
|
||||
assertEquals(true, col.isNullable());
|
||||
|
||||
col = colIt.next();
|
||||
assertEquals("fixes_from_schema", col.getName());
|
||||
assertEquals("int4", col.getType());
|
||||
assertEquals(true, col.isNullable());
|
||||
|
||||
col = colIt.next();
|
||||
assertEquals("fixes_to_schema", col.getName());
|
||||
assertEquals("int4", col.getType());
|
||||
assertEquals(true, col.isNullable());
|
||||
|
||||
col = colIt.next();
|
||||
assertEquals("applied_to_schema", col.getName());
|
||||
assertEquals("int4", col.getType());
|
||||
assertEquals(true, col.isNullable());
|
||||
|
||||
col = colIt.next();
|
||||
assertEquals("target_schema", col.getName());
|
||||
assertEquals("int4", col.getType());
|
||||
assertEquals(true, col.isNullable());
|
||||
|
||||
col = colIt.next();
|
||||
assertEquals("applied_on_date", col.getName());
|
||||
assertEquals("timestamp", col.getType());
|
||||
assertEquals(true, col.isNullable());
|
||||
|
||||
col = colIt.next();
|
||||
assertEquals("applied_to_server", col.getName());
|
||||
assertEquals("varchar(64)", col.getType());
|
||||
assertEquals(true, col.isNullable());
|
||||
|
||||
col = colIt.next();
|
||||
assertEquals("was_executed", col.getName());
|
||||
assertEquals("bool", col.getType());
|
||||
assertEquals(true, col.isNullable());
|
||||
|
||||
col = colIt.next();
|
||||
assertEquals("succeeded", col.getName());
|
||||
assertEquals("bool", col.getType());
|
||||
assertEquals(true, col.isNullable());
|
||||
|
||||
col = colIt.next();
|
||||
assertEquals("report", col.getName());
|
||||
assertEquals("varchar(1024)", col.getType());
|
||||
assertEquals(true, col.isNullable());
|
||||
|
||||
assertEquals("id", appliedPatch.getPrimaryKey().getColumnNames().get(0));
|
||||
}
|
||||
|
||||
|
||||
public void checkAuthoritySequence(Sequence seq)
|
||||
{
|
||||
/*
|
||||
CREATE SEQUENCE alf_authority_seq START WITH 1 INCREMENT BY 1;
|
||||
*/
|
||||
assertNotNull("Couldn't find sequence alf_authority_seq", seq);
|
||||
assertEquals("alf_authority_seq", seq.getName());
|
||||
}
|
||||
}
|
@@ -32,6 +32,7 @@ import org.junit.runners.Suite;
|
||||
DbObjectXMLTransformerTest.class,
|
||||
DbPropertyTest.class,
|
||||
DefaultComparisonUtilsTest.class,
|
||||
ExportDbTest.class,
|
||||
SchemaComparatorTest.class,
|
||||
ValidatingVisitorTest.class,
|
||||
SchemaToXMLTest.class,
|
||||
|
@@ -108,12 +108,24 @@ public class SchemaCompTestingUtils
|
||||
{
|
||||
String[] parts = indexDefs[i].split(" ");
|
||||
String name = parts[0];
|
||||
String[] columns = (String[]) ArrayUtils.subarray(parts, 1, parts.length);
|
||||
|
||||
boolean unique = false;
|
||||
int columnsStart = 1;
|
||||
|
||||
if (parts[1].equals("[unique]"))
|
||||
{
|
||||
unique = true;
|
||||
columnsStart++;
|
||||
}
|
||||
|
||||
String[] columns = (String[]) ArrayUtils.subarray(parts, columnsStart, parts.length);
|
||||
indexes[i] = new Index(null, name, Arrays.asList(columns));
|
||||
indexes[i].setUnique(unique);
|
||||
}
|
||||
return Arrays.asList(indexes);
|
||||
}
|
||||
|
||||
|
||||
public static Sequence sequence(String name)
|
||||
{
|
||||
return new Sequence(name);
|
||||
|
@@ -28,6 +28,7 @@ import static org.alfresco.util.schemacomp.SchemaCompTestingUtils.indexes;
|
||||
import static org.alfresco.util.schemacomp.SchemaCompTestingUtils.pk;
|
||||
import static org.alfresco.util.schemacomp.SchemaCompTestingUtils.table;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -71,14 +72,14 @@ public class SchemaComparatorTest
|
||||
indexes("idx_node id nodeRef")));
|
||||
left.add(table("table_in_left"));
|
||||
left.add(new Table(left, "tbl_has_diff_pk", columns("id NUMBER(10)", "nodeRef VARCHAR2(200)"),
|
||||
pk("pk_is_diff", "id"), fkeys(), indexes()));
|
||||
pk("pk_is_diff", "id"), fkeys(), indexes("idx_one id nodeRef", "idx_two id")));
|
||||
|
||||
// Right hand side's database objects.
|
||||
right.add(new Table(right, "tbl_no_diff", columns("id NUMBER(10)", "nodeRef VARCHAR2(200)", "name VARCHAR2(150)"),
|
||||
pk("pk_tbl_no_diff", "id"), fkeys(fk("fk_tbl_no_diff", "nodeRef", "node", "nodeRef")),
|
||||
indexes("idx_node id nodeRef")));
|
||||
right.add(new Table(right, "tbl_has_diff_pk", columns("id NUMBER(10)", "nodeRef VARCHAR2(200)"),
|
||||
pk("pk_is_diff", "nodeRef"), fkeys(), indexes()));
|
||||
pk("pk_is_diff", "nodeRef"), fkeys(), indexes("idx_one id nodeRef", "idx_two [unique] id")));
|
||||
right.add(table("table_in_right"));
|
||||
|
||||
|
||||
@@ -91,7 +92,6 @@ public class SchemaComparatorTest
|
||||
|
||||
|
||||
Results differences = comparator.getDifferences();
|
||||
assertEquals(5, differences.size());
|
||||
|
||||
Iterator<Difference> it = differences.iterator();
|
||||
|
||||
@@ -135,6 +135,23 @@ public class SchemaComparatorTest
|
||||
assertEquals("columnNames[0]", diff.getRight().getPropertyName());
|
||||
assertEquals("nodeRef", diff.getRight().getPropertyValue());
|
||||
|
||||
// idx_two is unique in the righ_schema but not in the left
|
||||
diff = it.next();
|
||||
assertEquals("left_schema.tbl_has_diff_pk.idx_two.unique", diff.getLeft().getPath());
|
||||
assertEquals("right_schema.tbl_has_diff_pk.idx_two.unique", diff.getRight().getPath());
|
||||
assertEquals("unique", diff.getLeft().getPropertyName());
|
||||
assertEquals(false, diff.getLeft().getPropertyValue());
|
||||
assertEquals("unique", diff.getRight().getPropertyName());
|
||||
assertEquals(true, diff.getRight().getPropertyValue());
|
||||
|
||||
// Table table_in_right does not exist in the left schema
|
||||
diff = it.next();
|
||||
assertEquals(Where.ONLY_IN_RIGHT, diff.getWhere());
|
||||
assertEquals("right_schema.table_in_right", diff.getRight().getPath());
|
||||
assertEquals(null, diff.getLeft());
|
||||
assertEquals(null, diff.getRight().getPropertyName());
|
||||
assertEquals(null, diff.getRight().getPropertyValue());
|
||||
|
||||
assertFalse("There should be no more differences", it.hasNext());
|
||||
}
|
||||
}
|
||||
|
@@ -43,4 +43,5 @@ public abstract class XML
|
||||
public static final String EL_TARGET_TABLE = "targettable";
|
||||
|
||||
public static final String ATTR_NAME = "name";
|
||||
public static final String ATTR_UNIQUE = "unique";
|
||||
}
|
||||
|
@@ -162,7 +162,10 @@ public class XMLToSchema extends DefaultHandler
|
||||
}
|
||||
else if (qName.equals(XML.EL_INDEX))
|
||||
{
|
||||
dboStack.push(new Index(atts.getValue(XML.ATTR_NAME)));
|
||||
Index index = new Index(atts.getValue(XML.ATTR_NAME));
|
||||
boolean unique = Boolean.parseBoolean(atts.getValue(XML.ATTR_UNIQUE));
|
||||
index.setUnique(unique);
|
||||
dboStack.push(index);
|
||||
}
|
||||
else if (qName.equals(XML.EL_SEQUENCE))
|
||||
{
|
||||
|
@@ -19,6 +19,8 @@
|
||||
package org.alfresco.util.schemacomp;
|
||||
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
@@ -31,7 +33,6 @@ import org.alfresco.util.schemacomp.model.Sequence;
|
||||
import org.alfresco.util.schemacomp.model.Table;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Tests for the XMLToSchema class.
|
||||
@@ -89,6 +90,7 @@ public class XMLToSchemaTest
|
||||
|
||||
assertEquals(1, table.getIndexes().size());
|
||||
assertEquals("idx_node_by_id", table.getIndexes().get(0).getName());
|
||||
assertEquals(true, table.getIndexes().get(0).isUnique());
|
||||
assertEquals(2, table.getIndexes().get(0).getColumnNames().size());
|
||||
assertEquals("id", table.getIndexes().get(0).getColumnNames().get(0));
|
||||
assertEquals("nodeRef", table.getIndexes().get(0).getColumnNames().get(1));
|
||||
|
@@ -34,7 +34,7 @@ import org.alfresco.util.schemacomp.Result.Strength;
|
||||
public class Index extends AbstractDbObject
|
||||
{
|
||||
private final List<String> columnNames = new ArrayList<String>();
|
||||
|
||||
private boolean unique;
|
||||
|
||||
public Index(String name)
|
||||
{
|
||||
@@ -69,12 +69,33 @@ public class Index extends AbstractDbObject
|
||||
this.columnNames.addAll(columnNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this index have the unique attribute?
|
||||
*
|
||||
* @return unique
|
||||
*/
|
||||
public boolean isUnique()
|
||||
{
|
||||
return this.unique;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #isUnique()
|
||||
* @param unique
|
||||
*/
|
||||
public void setUnique(boolean unique)
|
||||
{
|
||||
this.unique = unique;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + ((this.columnNames == null) ? 0 : this.columnNames.hashCode());
|
||||
result = prime * result + (this.unique ? 1231 : 1237);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -90,6 +111,7 @@ public class Index extends AbstractDbObject
|
||||
if (other.columnNames != null) return false;
|
||||
}
|
||||
else if (!this.columnNames.equals(other.columnNames)) return false;
|
||||
if (this.unique != other.unique) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -126,6 +148,10 @@ public class Index extends AbstractDbObject
|
||||
new DbProperty(rightIndex, "columnNames"),
|
||||
ctx,
|
||||
strength);
|
||||
comparisonUtils.compareSimple(
|
||||
new DbProperty(this, "unique"),
|
||||
new DbProperty(rightIndex, "unique"),
|
||||
ctx);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -64,6 +64,10 @@ public class IndexTest extends DbObjectTestBase<Index>
|
||||
new DbProperty(thatIndex, "columnNames"),
|
||||
ctx,
|
||||
Strength.ERROR);
|
||||
inOrder.verify(comparisonUtils).compareSimple(
|
||||
new DbProperty(thisIndex, "unique"),
|
||||
new DbProperty(thatIndex, "unique"),
|
||||
ctx);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -32,7 +32,7 @@
|
||||
</foreignkey>
|
||||
</foreignkeys>
|
||||
<indexes>
|
||||
<index name="idx_node_by_id">
|
||||
<index name="idx_node_by_id" unique="true">
|
||||
<columnnames>
|
||||
<columnname>id</columnname>
|
||||
<columnname>nodeRef</columnname>
|
||||
|
Reference in New Issue
Block a user