/*
* Copyright (c) 2008, SQL Power Group Inc.
*
* This file is part of Power*Architect.
*
* Power*Architect is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Power*Architect 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ca.sqlpower.sqlobject;
import java.beans.PropertyChangeEvent;
import java.lang.reflect.InvocationTargetException;
import java.sql.DatabaseMetaData;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EventObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.commons.beanutils.BeanUtils;
import ca.sqlpower.object.AbstractPoolingSPListener;
import ca.sqlpower.object.SPChildEvent;
import ca.sqlpower.object.SPObject;
import ca.sqlpower.object.undo.CompoundEvent;
import ca.sqlpower.sql.JDBCDataSource;
import ca.sqlpower.sqlobject.SQLIndex.AscendDescend;
import ca.sqlpower.sqlobject.SQLIndex.Column;
import ca.sqlpower.sqlobject.SQLTable.TransferStyles;
import ca.sqlpower.sqlobject.TestSQLTable.EventLogger.SQLObjectSnapshot;
import ca.sqlpower.testutil.MockJDBCDriver;
import ca.sqlpower.util.SQLPowerUtils;
public class TestSQLTable extends BaseSQLObjectTestCase {
private SQLTable table;
public TestSQLTable(String name) throws Exception {
super(name);
}
@Override
protected void setUp() throws Exception {
super.setUp();
sqlx("CREATE TABLE REGRESSION_TEST1 (t1_c1 numeric(10), t1_c2 numeric(5))");
sqlx("CREATE TABLE REGRESSION_TEST2 (t2_c1 char(10))");
sqlx("CREATE VIEW REGRESSION_TEST1_VIEW AS SELECT * FROM REGRESSION_TEST1");
sqlx("CREATE TABLE SQL_TABLE_POPULATE_TEST (\n" +
" cow numeric(10) NOT NULL, \n" +
" CONSTRAINT test4pk PRIMARY KEY (cow))");
sqlx("CREATE TABLE SQL_TABLE_1_POPULATE_TEST (\n" +
" cow numeric(10) NOT NULL, \n" +
" CONSTRAINT test5pk PRIMARY KEY(cow))");
sqlx("ALTER TABLE SQL_TABLE_1_POPULATE_TEST " +
"ADD CONSTRAINT TEST_FK FOREIGN KEY (cow) " +
"REFERENCES SQL_TABLE_POPULATE_TEST (cow)");
table = new SQLTable(null, true);
table.setParent(new StubSQLObject());
table.addColumn(new SQLColumn(table, "one", Types.INTEGER, 10, 0));
table.addColumn(new SQLColumn(table, "two", Types.INTEGER, 10, 0));
table.addColumn(new SQLColumn(table, "three", Types.INTEGER, 10, 0));
table.addColumn(new SQLColumn(table, "four", Types.INTEGER, 10, 0));
table.addColumn(new SQLColumn(table, "five", Types.INTEGER, 10, 0));
table.addColumn(new SQLColumn(table, "six", Types.INTEGER, 10, 0));
table.getPrimaryKeyIndex().addIndexColumn(table.getColumn(0));
table.getPrimaryKeyIndex().addIndexColumn(table.getColumn(1));
table.getPrimaryKeyIndex().addIndexColumn(table.getColumn(2));
table.getColumn(0).setNullable(DatabaseMetaData.columnNullable);
table.getColumn(0).setAutoIncrement(true);
db.addTable(table);
}
@Override
protected SQLObject getSQLObjectUnderTest() {
return table;
}
@Override
protected Class<? extends SPObject> getChildClassType() {
return SQLColumn.class;
}
public void testConstructor() {
// FIXME need to test both constructors!
}
public void testGetDerivedInstance() throws Exception {
SQLTable derivedTable;
SQLTable table1;
// Check to make sure it can be added to a playpen like database
SQLDatabase pp = new SQLDatabase();
pp.setPlayPenDatabase(true);
pp.setParent(new StubSQLObject());
assertNotNull(table1 = db.getTableByName("REGRESSION_TEST1"));
derivedTable = table1.createInheritingInstance(pp);
TreeMap<String, Object> derivedPropertyMap = new TreeMap<String, Object>(BeanUtils.describe(derivedTable));
TreeMap<String, Object> table1PropertyMap = new TreeMap<String, Object>(BeanUtils.describe(table1));
table1PropertyMap.remove("parent");
table1PropertyMap.remove("SQLParent");
table1PropertyMap.remove("schemaName");
table1PropertyMap.remove("schema");
table1PropertyMap.remove("parentDatabase");
table1PropertyMap.remove("shortDisplayName");
table1PropertyMap.remove("UUID");
table1PropertyMap.remove("workspaceContainer");
table1PropertyMap.remove("runnableDispatcher");
table1PropertyMap.remove("SPListeners");
for (Map.Entry<String, Object> property : table1PropertyMap.entrySet()) {
assertEquals("Property \"" + property.getKey() + "\" has changed;", property.getValue(), derivedPropertyMap.get(property.getKey()));
}
}
public void testRenamePhysicalNameOfTableRenamesPK() throws SQLObjectException{
assertNotNull("Table has null logical name",table.getName());
String newName = "newTableName";
String newPhysicalName = "new_Table_Name";
table.setName(newName);
assertNotNull("Table has null physical name", table.getPhysicalName());
assertEquals("The physical name does not match the logical", newName, table.getPhysicalName().trim());
table.setPhysicalName(newPhysicalName);
assertEquals(newPhysicalName+"_pk",table.getPrimaryKeyIndex().getName());
}
public void testRenamePhysicalNameOfTableDoesntRenamePKIfPKRenamed() throws SQLObjectException {
assertNotNull("Table has null name",table.getName());
String newTableName = "newTableName";
String newPKName = "NewPKName";
String newPhysicalName = "new_Table_Name";
table.getPrimaryKeyIndex().setName(newPKName);
table.setName(newTableName);
table.setPhysicalName(newPhysicalName);
assertEquals(newPKName, table.getPrimaryKeyIndex().getName());
}
public void testRenamePhysicalNameOfTableRenamesSequences() throws Exception {
table.setPhysicalName("old name");
table.getColumn(0).setPhysicalName("cow");
table.getColumn(0).setAutoIncrementSequenceName(table.getPhysicalName() + "_cow");
table.setPhysicalName("new name");
assertTrue(table.getColumn(0).isAutoIncrementSequenceNameSet());
assertEquals(table.getPhysicalName() + "_cow_seq", table.getColumn(0).getAutoIncrementSequenceName());
}
public void testRenameTableDoesNotRenameUnnamedSequences() throws Exception {
table.setName("old name");
table.getColumn(0).setAutoIncrementSequenceName("moo_" + table.getName() + "_cow");
table.setName("new name");
assertTrue(table.getColumn(0).isAutoIncrementSequenceNameSet());
for (int i = 1; i < table.getColumns().size(); i++) {
assertFalse(table.getColumn(i).isAutoIncrementSequenceNameSet());
}
}
public void testInherit() throws SQLObjectException {
SQLTable table1;
SQLTable table2;
table1 = db.getTableByName("REGRESSION_TEST1");
table2 = db.getTableByName("REGRESSION_TEST2");
// the tables need to load properly
assertEquals(2, table1.getColumns().size());
assertEquals(1, table2.getColumns().size());
table2.inherit(table1, TransferStyles.REVERSE_ENGINEER, true);
assertEquals("The wrong 1st column was inherited",
table1.getColumn(0).toString(), table2.getColumn(1).toString());
assertEquals("The wrong 2nd column was inherited",
table1.getColumn(1).toString(), table2.getColumn(2).toString());
assertEquals("The wrong number of columns were inherited",
table2.getColumns().size(), 3);
try {
table2.inherit(table2, TransferStyles.REVERSE_ENGINEER, true);
} catch (SQLObjectException ae) {
if ("Cannot inherit from self".equals(ae.getMessage())) {
System.out.println("Expected Behaviour is to not be able to inherit from self");
} else {
throw ae;
}
}
}
public void testGetColumnByName() throws SQLObjectException {
SQLTable table1;
SQLColumn col1;
SQLColumn col2;
table1 = db.getTableByName("REGRESSION_TEST1");
col2 = table1.getColumnByName("t1_c2");
assertNotNull(col2);
assertEquals("The wrong colomn us returned", col2, table1.getColumn(1));
col1 = table1.getColumnByName("t1_c1");
assertNotNull(col1);
assertEquals("The wrong colomn us returned", col1, table1.getColumn(0));
assertNull(col1 = table1.getColumnByName("This_is_a_non_existant_column"));
assertNull("Invalid column name", col1 = table1.getColumnByName("$# #$%#%"));
}
public void testAddColumn() throws SQLObjectException {
SQLTable table1 = db.getTableByName("REGRESSION_TEST1");
assertEquals(2, table1.getColumns().size());
SQLColumn newColumn = new SQLColumn(table1, "my new column", Types.INTEGER, 10, 0);
table1.addColumn(newColumn, 2);
SQLColumn addedCol = table1.getColumn(2);
assertSame("Column at index 2 isn't same object as we added", newColumn, addedCol);
}
public void testAddColumnReference() throws SQLObjectException {
SQLTable table = db.getTableByName("REGRESSION_TEST1");
SQLColumn col = table.getColumn(0);
assertEquals("Existing column had refcount != 1", 1, col.getReferenceCount());
table.addColumn(col);
assertEquals("refcount didn't increase", 2, col.getReferenceCount());
}
public void testRemoveColumnByZeroRefs() throws SQLObjectException {
SQLTable table = db.getTableByName("REGRESSION_TEST1");
SQLColumn col = table.getColumn(0);
table.addColumn(col);
table.addColumn(col);
col.removeReference();
assertTrue(table.getColumns().contains(col));
col.removeReference();
assertTrue(table.getColumns().contains(col));
col.removeReference();
assertFalse(table.getColumns().contains(col));
}
/** this tests for a real bug.. the column was showing up one index above the end of the pk */
public void testAddColumnAtEndOfPK() throws SQLObjectException {
SQLTable t = new SQLTable(null, true);
t.setName("Test Table");
SQLColumn pk1 = new SQLColumn(t, "PKColumn1", Types.INTEGER, 10, 0);
SQLColumn pk2 = new SQLColumn(t, "PKColumn2", Types.INTEGER, 10, 0);
SQLColumn pk3 = new SQLColumn(t, "PKColumn3", Types.INTEGER, 10, 0);
SQLColumn at1 = new SQLColumn(t, "AT1", Types.INTEGER, 10, 0);
SQLColumn at2 = new SQLColumn(t, "AT2", Types.INTEGER, 10, 0);
SQLColumn at3 = new SQLColumn(t, "AT3", Types.INTEGER, 10, 0);
t.addColumn(pk1,0);
t.addColumn(pk2,1);
t.addColumn(pk3,2);
t.addColumn(at1,3);
t.addColumn(at2,4);
t.addColumn(at3,5);
t.getPrimaryKeyIndex().addIndexColumn(pk1);
t.getPrimaryKeyIndex().addIndexColumn(pk2);
t.getPrimaryKeyIndex().addIndexColumn(pk3);
assertEquals(3, t.getPkSize());
SQLColumn newcol = new SQLColumn(t, "newcol", Types.INTEGER, 10, 0);
t.addColumn(newcol, 3);
assertEquals("New column should be at requested position", 3, t.getColumnIndex(newcol));
t.getPrimaryKeyIndex().addIndexColumn(newcol);
assertEquals("New column should still be at requested position", 3, t.getColumnIndex(newcol));
}
public void testMoveToPKClearsNullability() throws SQLObjectException{
SQLTable t = db.getTableByName("REGRESSION_TEST1");
SQLColumn c = t.getColumnByName("t1_c1");
assertFalse("Column shouldn't be in PK to begin", c.isPrimaryKey());
c.setNullable(DatabaseMetaData.columnNullable);
// Now c is not in the PK and is nullable. Let's add it to PK
t.changeColumnIndex(0,0,true);
assertTrue(c.isPrimaryKey());
assertEquals(DatabaseMetaData.columnNoNulls, c.getNullable());
}
public void testRemoveColumnOutBounds() throws SQLObjectException {
SQLTable table1;
table1 = db.getTableByName("REGRESSION_TEST1");
Exception exc = null;
try {
table1.removeColumn(16);
} catch (IndexOutOfBoundsException e) {
System.out.println("Method throws proper error");
exc = e;
}
assertNotNull("Should have thrown an exception", exc);
}
public void testRemoveColumn() throws SQLObjectException {
SQLTable table1;
SQLColumn col1;
SQLColumn col2;
table1 = db.getTableByName("REGRESSION_TEST1");
col1 = table1.getColumn(0);
col2 = table1.getColumn(1);
assertEquals("We removed a column when we shouldn't have",
table1.getColumns().size(), 2);
table1.removeColumn(col1);
assertEquals("Either 0 or 2+ columns were removed",
table1.getColumns().size(), 1);
assertEquals("The wrong column was removed", col2, table1.getColumn(0));
table1.removeColumn(0);
assertEquals("Last Column failed to be removed",
table1.getColumns().size(), 0);
Exception exc = null;
try {
table1.removeColumn(0);
} catch (IndexOutOfBoundsException e) {
System.out.println("Method throws proper error");
exc = e;
}
assertNotNull("Should have thrown an exception", exc);
}
public void testGetPrimaryKey() throws SQLObjectException {
SQLIndex i1 = new SQLIndex("name",true,null, "BTREE",null);
SQLTable t1 = new SQLTable(null,true, i1);
SQLColumn c1 = new SQLColumn(t1,"col1",1,0,0);
t1.addChild(c1);
i1.addIndexColumn(c1, AscendDescend.UNSPECIFIED);
SQLIndex i2 = new SQLIndex("name 2",true,null, "BTREE",null);
i2.addChild(new Column("Index column string",AscendDescend.UNSPECIFIED));
t1.addChild(i2);
assertEquals(i1, t1.getPrimaryKeyIndex());
assertEquals(1, t1.getPrimaryKeyIndex().getChildrenWithoutPopulating().size());
}
public void testFireDbChildrenInserted() throws Exception {
SQLTable table1 = new SQLTable();
table1.setPopulated(true);
TestingSQLObjectListener testListener = new TestingSQLObjectListener();
table1.addSPListener(testListener);
SQLColumn col = new SQLColumn();
table1.addChild(col);
assertEquals("Children inserted event not fired!", 1, testListener.getInsertedCount());
}
public void testFireDbChildrenRemoved() throws Exception {
SQLTable table1 = new SQLTable();
table1.setPopulated(true);
SQLColumn col = new SQLColumn();
table1.addChild(col);
TestingSQLObjectListener testListener = new TestingSQLObjectListener();
table1.addSPListener(testListener);
table1.removeChild(col);
assertEquals("Children removed event not fired!", 1, testListener.getRemovedCount());
}
public void testDeleteLockedColumn() throws SQLObjectException {
SQLTable parentTable = new SQLTable(null, "parent", null, "TABLE", true);
parentTable.addColumn(new SQLColumn(parentTable, "pkcol_1", Types.INTEGER, 10, 0));
parentTable.addColumn(new SQLColumn(parentTable, "pkcol_2", Types.INTEGER, 10, 0));
parentTable.addColumn(new SQLColumn(parentTable, "attribute_1", Types.INTEGER, 10, 0));
SQLTable childTable1 = new SQLTable(null, "child_1", null, "TABLE", true);
childTable1.addColumn(new SQLColumn(childTable1, "child_pkcol_1", Types.INTEGER, 10, 0));
childTable1.addColumn(new SQLColumn(childTable1, "child_pkcol_2", Types.INTEGER, 10, 0));
childTable1.addColumn(new SQLColumn(childTable1, "child_attribute", Types.INTEGER, 10, 0));
SQLRelationship rel1 = new SQLRelationship();
rel1.attachRelationship(parentTable,childTable1,false);
rel1.addMapping(parentTable.getColumn(0), childTable1.getColumn(0));
rel1.addMapping(parentTable.getColumn(1), childTable1.getColumn(1));
try {
SQLColumn inheritedCol = childTable1.getColumnByName("child_pkcol_1");
childTable1.removeChild(inheritedCol);
fail("Remove should have thrown LockedColumnException");
} catch (LockedColumnException ex) {
// good
} catch (Exception ex) {
fail("Didn't get the exception we were expecting: " + ex);
}
}
public void testRemovePKColumn() throws SQLObjectException{
assertEquals("There should be 6 columns to start",6,
table.getColumns().size());
table.removeColumn((table.getColumnIndex(table.getColumnByName("two"))));
assertEquals("A column should have been removed",
5, table.getColumns().size());
assertEquals(2, table.getPkSize());
assertEquals(0, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("five")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("six")));
}
public void testRemoveFKColumn() throws Exception {
assertEquals("There should be 6 columns to start",6,
table.getColumns().size());
table.removeColumn(table.getColumnIndex(table.getColumnByName("five")));
assertEquals("A column should have been removed",
5, table.getColumns().size());
assertEquals(3, table.getPkSize());
assertEquals(0, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("two")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("six")));
}
public void testAddColAtFirstIdx() throws SQLObjectException{
table.addColumn(new SQLColumn(table, "zero", Types.INTEGER, 10, 0), 0);
assertEquals(7, table.getColumns().size());
assertEquals(4, table.getPkSize());
assertTrue(table.getColumnByName("zero").isPrimaryKey());
assertEquals(0, table.getColumnIndex(table.getColumnByName("zero")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("two")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(5, table.getColumnIndex(table.getColumnByName("five")));
assertEquals(6, table.getColumnIndex(table.getColumnByName("six")));
}
public void testAddColAbovePK() throws SQLObjectException{
table.addColumn(new SQLColumn(table, "indextwo", Types.INTEGER, 10, 0), 2);
assertEquals(7, table.getColumns().size());
assertEquals(4, table.getPkSize());
assertTrue(table.getColumnByName("indextwo").isPrimaryKey());
assertEquals(0, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("two")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("indextwo")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(5, table.getColumnIndex(table.getColumnByName("five")));
assertEquals(6, table.getColumnIndex(table.getColumnByName("six")));
}
public void testAddColBelowPK() throws SQLObjectException{
table.addColumn(new SQLColumn(table, "indexfour", Types.INTEGER, 10, 0), 4);
assertEquals(7, table.getColumns().size());
assertEquals(3, table.getPkSize());
assertFalse(table.getColumnByName("indexfour").isPrimaryKey());
assertEquals(0, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("two")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("indexfour")));
assertEquals(5, table.getColumnIndex(table.getColumnByName("five")));
assertEquals(6, table.getColumnIndex(table.getColumnByName("six")));
}
public void testChangeFirstColumnIdx() throws SQLObjectException{
table.changeColumnIndex(0, 1, true);
assertEquals(3, table.getPkSize());
assertEquals(0, table.getColumnIndex(table.getColumnByName("two")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("five")));
assertEquals(5, table.getColumnIndex(table.getColumnByName("six")));
}
public void testChangeSecondColumnIdx() throws SQLObjectException{
table.changeColumnIndex(1, 0, true);
assertEquals(3, table.getPkSize());
assertEquals(0, table.getColumnIndex(table.getColumnByName("two")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("five")));
assertEquals(5, table.getColumnIndex(table.getColumnByName("six")));
}
public void testChangeSecondColumnIdxToFifth() throws SQLObjectException{
table.changeColumnIndex(1, 4, true);
assertEquals(2, table.getPkSize());
assertEquals(0, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("five")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("two")));
assertEquals(5, table.getColumnIndex(table.getColumnByName("six")));
}
public void testChangeSFifthColumnIdxToSecond() throws Exception {
EventLogger l = new EventLogger();
SQLObjectSnapshot original = l.makeSQLObjectSnapshot(table);
SQLPowerUtils.listenToHierarchy(table, l);
table.changeColumnIndex(4, 1, true);
SQLPowerUtils.unlistenToHierarchy(table, l);
assertEquals(4, table.getPkSize());
assertEquals(0, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("five")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("two")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(5, table.getColumnIndex(table.getColumnByName("six")));
System.out.println("Event log:\n"+l);
SQLObjectSnapshot afterChange = l.makeSQLObjectSnapshot(table);
System.out.println("Original: "+original);
System.out.println("After: "+afterChange);
l.rollBack(afterChange);
assertEquals(original.toString(), afterChange.toString());
}
public void testChangeSFifthColumnIdxToTop() throws Exception {
EventLogger l = new EventLogger();
SQLObjectSnapshot original = l.makeSQLObjectSnapshot(table);
SQLPowerUtils.listenToHierarchy(table, l);
table.changeColumnIndex(4, 0, true);
SQLPowerUtils.unlistenToHierarchy(table, l);
assertEquals(4, table.getPkSize());
assertEquals(0, table.getColumnIndex(table.getColumnByName("five")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("two")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(5, table.getColumnIndex(table.getColumnByName("six")));
System.out.println("Event log:\n"+l);
SQLObjectSnapshot afterChange = l.makeSQLObjectSnapshot(table);
System.out.println("Original: "+original);
System.out.println("After: "+afterChange);
l.rollBack(afterChange);
assertEquals(original.toString(), afterChange.toString());
}
public void testChangeSFifthColumnIdxToThird() throws SQLObjectException{
table.changeColumnIndex(4, 2, true);
assertEquals(4, table.getPkSize());
assertEquals(0, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("two")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("five")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(5, table.getColumnIndex(table.getColumnByName("six")));
}
public void testChangeSForthColumnIdxToThird() throws SQLObjectException{
table.changeColumnIndex(3, 2, true);
assertEquals(4, table.getPkSize());
assertEquals(0, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("two")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("five")));
assertEquals(5, table.getColumnIndex(table.getColumnByName("six")));
}
public void testChangeSThirdColumnIdxToForth() throws SQLObjectException{
table.changeColumnIndex(2, 3, true);
assertEquals(2, table.getPkSize());
assertEquals(0, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("two")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("five")));
assertEquals(5, table.getColumnIndex(table.getColumnByName("six")));
}
public void testChangeFirstColumnKey() throws SQLObjectException{
SQLColumn col1 = table.getColumnByName("one");
assertNotNull(col1);
table.moveAfterPK(col1);
assertEquals(2, table.getPkSize());
//We just want to make sure it's no longer the first or second
//column where the PK column lies
assertTrue(table.getColumnIndex(col1) > 1);
}
public void testChangeThirdColumnKey() throws SQLObjectException{
SQLColumn col3 = table.getColumnByName("three");
assertNotNull(col3);
table.moveAfterPK(col3);
assertEquals(2, table.getPkSize());
assertEquals(0, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("two")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("five")));
assertEquals(5, table.getColumnIndex(table.getColumnByName("six")));
}
public void testChangeFourthColumnKey() throws SQLObjectException{
SQLColumn col4 = table.getColumnByName("four");
assertNotNull(col4);
table.changeColumnIndex(table.getColumnIndex(col4), 1, true);
assertEquals(4, table.getPkSize());
assertEquals(0, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("two")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("five")));
assertEquals(5, table.getColumnIndex(table.getColumnByName("six")));
}
public void testChangeFifthColumnKey() throws Exception {
EventLogger l = new EventLogger();
SQLColumn col5 = table.getColumnByName("five");
assertNotNull(col5);
SQLObjectSnapshot original = l.makeSQLObjectSnapshot(table);
SQLPowerUtils.listenToHierarchy(table, l);
table.changeColumnIndex(table.getColumnIndex(col5), 1, true);
SQLPowerUtils.unlistenToHierarchy(table, l);
System.out.println("Event log:\n"+l);
SQLObjectSnapshot afterChange = l.makeSQLObjectSnapshot(table);
assertEquals(4, table.getPkSize());
assertEquals(0, table.getColumnIndex(table.getColumnByName("one")));
assertEquals(1, table.getColumnIndex(table.getColumnByName("five")));
assertEquals(2, table.getColumnIndex(table.getColumnByName("two")));
assertEquals(3, table.getColumnIndex(table.getColumnByName("three")));
assertEquals(4, table.getColumnIndex(table.getColumnByName("four")));
assertEquals(5, table.getColumnIndex(table.getColumnByName("six")));
System.out.println("Original: "+original);
System.out.println("After: "+afterChange);
l.rollBack(afterChange);
assertEquals(original.toString(), afterChange.toString());
// also roll forward original and compare to afterChange
}
public void testPopulateColumnsCaseSensitive() throws Exception {
JDBCDataSource ds = new JDBCDataSource(getPLIni());
ds.setDisplayName("tableWithMixedColumnCase");
ds.getParentType().setJdbcDriver(MockJDBCDriver.class.getName());
ds.setUser("fake");
ds.setPass("fake");
ds.setUrl("jdbc:mock:" +
"tables=tab1" +
"&columns.tab1=this_is_my_column,THIS_IS_MY_COLUMN");
SQLDatabase db = new SQLDatabase(ds);
SQLTable t = db.getTableByName("tab1");
// this shouldn't throw a DuplicateColumnException
assertEquals(2, t.getColumns().size());
}
public void testPopulateViewIndices() throws Exception {
JDBCDataSource ds = getJDBCDataSource();
SQLDatabase db = new SQLDatabase(ds);
SQLTable t = db.getTableByName("REGRESSION_TEST1_VIEW");
// Should not throw an ArchitectException
t.populateColumns();
t.populateIndices();
}
/**
* Utility class that can log events for a tree of SQLObjects, and make snapshots of the
* state of that tree on demand. The snapshots can be rolled back using the event log
* (this is similar functionality to the undo manager, but does not depend on the undo manager
* in any way), and compared to older snapshots. Old snapshots can also be rolled forward
* (like an undo manager redo operation) to test that a stream of events is fully redoable.
*/
public static class EventLogger extends AbstractPoolingSPListener {
/**
* The list of events captured from the SQLObject tree. Events are stored in the order they
* are received, oldest first.
*/
private List<LogItem> log = new ArrayList<LogItem>();
/**
* Enumerates the event types for items that are stored in the log.
*/
private enum LogItemType { INSERT, REMOVE, CHANGE, STRUCTURE_CHANGE }
/**
* An item in the log. It has a type (based on which listener method was invoked) and the
* event that the listener received.
*/
private static class LogItem {
private LogItemType type;
private EventObject event;
public LogItem(LogItemType type, EventObject event) {
this.type = type;
this.event = event;
}
public LogItemType getType() {
return type;
}
public EventObject getEvent() {
return event;
}
@Override
public String toString() {
return type+": "+event;
}
}
/**
* Adds an item to the end of the event log.
*
* @param type The event type (based on which listener method was called)
* @param e The event. Must not be null.
*/
private void addToLog(LogItemType type, EventObject e) {
if (e == null) throw new NullPointerException("Can't add null events, dude");
log.add(new LogItem(type, e));
}
/**
* Listener method. Adds the received event to the log.
*/
@Override
public void childAddedImpl(SPChildEvent e) {
addToLog(LogItemType.INSERT, e);
}
/**
* Listener method. Adds the received event to the log.
*/
@Override
public void childRemovedImpl(SPChildEvent e) {
addToLog(LogItemType.REMOVE, e);
}
/**
* Listener method. Adds the received event to the log.
*/
@Override
public void propertyChangeImpl(PropertyChangeEvent e) {
addToLog(LogItemType.CHANGE, e);
// FIXME have to unlisten to old objects and listen to new ones
}
public void transactionStarted(CompoundEvent e) {
// whatever
}
public void transactionEnded(CompoundEvent e) {
// whatever
}
/**
* Holds a snapshot of all property values in a SQLObject instance at one particular point
* in time. Also allows rolling property change-type events forward and backward. This is
* different from the UndoManager in that it does not operate on actual SQLObjects, just
* snapshots of their properties.
*/
public static class SQLObjectSnapshot {
/**
* A collection of properties to ignore when creating a snaphsot of
* a SQLObject. (These ignored properties interfere with the comparison
* process which checks if the roll forward/roll back operation reproduced
* the identical object state).
*/
final static Map<Class, Set<String>> ignoreProperties;
static {
ignoreProperties = new HashMap<Class, Set<String>>();
Set<String> set = new HashSet<String>();
set.add("columns"); // tracked by the snapshot's "children" list
set.add("children"); // tracked by the snapshot's "children" list, is the same as the columns
set.add("childrenWithoutPopulating"); // the same as "children" except it does not populate the child list.
set.add("columnsWithoutPopulating"); // the same as "children" except it does not populate the child list.
set.add("columnsFolder"); // now equivalent to "columns".
set.add("pkSize"); // depends on number of columns with non-null PKSeq
set.add("SQLObjectListeners"); // interferes with EventLogger, which listens to all objects
set.add("SPListeners"); // interferes with EventLogger, which listens to all objects
ignoreProperties.put(SQLTable.class, set);
// set = new HashSet<String>();
// set.add("children"); // tracked by the snapshot's "children" list
// set.add("childCount"); // tracked by the snapshot's "children" list
// set.add("SQLObjectListeners"); // interferes with EventLogger, which listens to all objects
// ignoreProperties.put(SQLTable.Folder.class, set);
set = new HashSet<String>();
set.add("definitelyNullable"); // secondary property depends on nullable
set.add("primaryKey"); // secondary property depends on position in parent is isInPk
set.add("SQLObjectListeners"); // interferes with EventLogger, which listens to all objects
set.add("SPListeners"); // interferes with EventLogger, which listens to all objects
set.add("foreignKey"); // secondary property depends on position in parent
set.add("indexed"); // secondary property depends on position in parent
set.add("uniqueIndexed"); // secondary property depends on position in parent
set.add("autoIncrement"); // depends on user defined SQL type
set.add("constraintType"); // depends on user defined SQL type
set.add("defaultValue"); // depends on user defined SQL type
set.add("enumeration"); // depends on user defined SQL type
set.add("nullable"); // depends on user defined SQL type
set.add("precisionType"); // depends on user defined SQL type
set.add("scaleType"); // depends on user defined SQL type
set.add("sourceDataTypeName"); // depends on user defined SQL type
set.add("type"); // depends on user defined SQL type
ignoreProperties.put(SQLColumn.class, set);
set = new HashSet<String>();
set.add("children"); // tracked by the snapshot's "children" list
set.add("childrenWithoutPopulating"); // tracked by the snapshot's "children" list
set.add("childCount"); // tracked by the snapshot's "children" list
set.add("SQLObjectListeners"); // interferes with EventLogger, which listens to all objects
set.add("SPListeners"); // interferes with EventLogger, which listens to all objects
ignoreProperties.put(SQLIndex.class, set);
set = new HashSet<String>();
set.add("SQLObjectListeners"); // interferes with EventLogger, which listens to all objects
set.add("SPListeners"); // interferes with EventLogger, which listens to all objects
ignoreProperties.put(SQLIndex.Column.class, set);
set = new HashSet<String>();
set.add("nullability"); // retrieved from underlying domain or type which is not tested here.
ignoreProperties.put(UserDefinedSQLType.class, set);
}
/**
* The snapshot (BeanUtils.describe format) of the snapshotted object's properties.
* we use the sortedMap there because we need the key sorted, so the toString() of
* the properties will come out in a consistant order.
*/
private SortedMap<String,Object> properties;
/**
* Snapshots of the snapshotted object's children at the time of the snapshot.
*/
private List<SQLObjectSnapshot> children;
/**
* The snapshotted object's identity hash code (from System.identityHashCode()).
*/
private int snapshottedObjectIdentity;
/**
* The class of the snapshotted object.
*/
private Class snapshottedObjectClass;
/**
* When rolling back changes, values may be changed to one object and
* then that object may be removed and added at a different index in its
* parent. When this occurs we cannot just make a new snapshot of the
* current object as we changed values in the snapshot we want to keep.
* This list stores the removed snapshots so we can reuse them as we
* roll back. This would not happen in a normal event system as they use
* the actual objects.
*/
private final List<SQLObjectSnapshot> removedSnapshots = new ArrayList<SQLObjectSnapshot>();
public SQLObjectSnapshot(SQLObject object)
throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, SQLObjectException {
snapshottedObjectClass = object.getClass();
snapshottedObjectIdentity = System.identityHashCode(object);
children = new ArrayList<SQLObjectSnapshot>();
properties = new TreeMap<String, Object>(BeanUtils.describe(object));
if (ignoreProperties.containsKey(object.getClass())) {
for (String propName : ignoreProperties.get(object.getClass())) {
properties.remove(propName);
}
}
for (SQLObject c : (List<SQLObject>) object.getChildren()) {
children.add(new SQLObjectSnapshot(c));
}
}
public void clearRollbackSnapshots() {
removedSnapshots.clear();
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
getPropertiesToString(sb,0);
return sb.toString();
}
private void getPropertiesToString(StringBuffer buffer, int indent) {
buffer.append(snapshottedObjectClass.getName());
buffer.append("@").append(snapshottedObjectIdentity);
buffer.append(" \"").append(properties.get("name")).append("\" ");
// buffer.append(properties.toString());
for (String key : properties.keySet()) {
buffer.append(key).append("=");
if (snapshottedObjectClass.getName().equals("ca.sqlpower.sqlobject.SQLColumn") &&
key.equals("SQLObjectListeners")) {
buffer.append("xxx");
} else {
buffer.append(properties.get(key));
}
buffer.append(" ");
}
if (children.size() > 0) {
buffer.append("\n");
appendSpaces(buffer, indent);
buffer.append(children.size());
buffer.append(" children:");
for (SQLObjectSnapshot c : children) {
buffer.append("\n");
appendSpaces(buffer, indent + 1);
c.getPropertiesToString(buffer, indent + 1);
}
}
}
/**
* Appends the given number of spaces to the end of the given string buffer.
*/
private void appendSpaces(StringBuffer sb, int spaces) {
for (int i = 0; i < spaces; i++) {
sb.append(" ");
}
}
public void insertChild(int i, SQLObject object, boolean rollForward) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, SQLObjectException {
SQLObjectSnapshot snapshot = null;
for (SQLObjectSnapshot removedSnapshot : removedSnapshots) {
if (removedSnapshot.getSnapshottedObjectIdentity() ==
System.identityHashCode(object)) {
snapshot = removedSnapshot;
removedSnapshots.remove(removedSnapshot);
break;
}
}
if (snapshot == null) {
snapshot = new SQLObjectSnapshot(object);
}
children.add(i,snapshot);
}
public void removeChild(int i, SQLObject object, boolean rollForward) {
SQLObjectSnapshot removed = children.remove(i);
checkSnapshottedObject(object, removed, "removing child "+i);
if (!rollForward) {
removedSnapshots.add(removed);
}
}
private void checkSnapshottedObject(SQLObject object, SQLObjectSnapshot snapshot, String message) {
StringBuffer wrongThing = new StringBuffer();
if (!snapshot.getSnapshottedClass().equals(object.getClass())) {
wrongThing.append(" class");
}
// Skip the identity check for SQLIndex.Column because normalizePrimaryKey
// recreates the objects every time it is called (which changes their identities)
if (!snapshot.getSnapshottedClass().equals(SQLIndex.Column.class)) {
if (snapshot.getSnapshottedObjectIdentity() != System.identityHashCode(object)) {
wrongThing.append(" identity");
}
}
if (wrongThing.length() > 0) {
throw new IllegalStateException(
"Snapshot "+wrongThing+" mismatch. Expected: " +
snapshot.getSnapshottedClass().getName() + "@" + snapshot.getSnapshottedObjectIdentity() +
"; actual: " + object.getClass().getName() + "@" + System.identityHashCode(object) +
" while " + message);
}
}
private int getSnapshottedObjectIdentity() {
return snapshottedObjectIdentity;
}
private Class getSnapshottedClass() {
return snapshottedObjectClass;
}
public void applyChange(PropertyChangeEvent e) {
if (!properties.containsKey(e.getPropertyName())) {
throw new IllegalStateException("the snapshotted object does not contain this property: " +
e.getPropertyName());
}
checkSnapshottedObject((SQLObject) e.getSource(), this, "applying a property modification");
properties.put(e.getPropertyName(),e.getNewValue());
}
public void revertChange(PropertyChangeEvent e) {
if (!properties.containsKey(e.getPropertyName())) {
throw new IllegalStateException("this snapshotted object does not contain property: " +
e.getPropertyName());
}
checkSnapshottedObject((SQLObject) e.getSource(), this, "reversing a property midification");
properties.put(e.getPropertyName(), e.getOldValue());
}
/**
* Applies the given SQLObjectEvent to this snapshot, or the appropriate
* descendant snapshot object. If the appropriate snapshot is not a descendant
* of this snapshot, no changes will be applied, and the return value of this
* method will be false. Otherwise, the change will be applied and this method
* will return true.
*
* @param type The event type
* @param e The event itself
* @param rollForward Controls whether this event is applied as a "roll-forward" event
* (like a redo operation would do), or a roll-back (like an undo).
* @return True if the snapshot tree rooted at this snapshot contains a snapshot
* of the SQLObject e.getSource(); false if it does not.
*/
public boolean applyEvent(LogItemType type, EventObject e, boolean rollForward) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, SQLObjectException {
if (System.identityHashCode(e.getSource()) != getSnapshottedObjectIdentity()) {
for (SQLObjectSnapshot snap : children) {
if (snap.applyEvent(type, e, rollForward)) return true;
}
return false;
} else {
if (type == LogItemType.STRUCTURE_CHANGE) {
throw new UnsupportedOperationException("Structure changes are not repeatable");
}
if (rollForward) {
System.out.println("Rolling forward a "+type+": "+e);
if (type == LogItemType.INSERT) {
SPChildEvent event = (SPChildEvent) e;
insertChild(event.getIndex(), (SQLObject) event.getChild(), rollForward);
} else if (type == LogItemType.REMOVE) {
SPChildEvent event = (SPChildEvent) e;
removeChild(event.getIndex(), (SQLObject) event.getChild(), rollForward);
} else if (type == LogItemType.CHANGE) {
applyChange((PropertyChangeEvent) e);
} else {
throw new UnsupportedOperationException("Unknown log item type "+type);
}
} else {
System.out.println("Rolling back a "+type+": "+e);
if (type == LogItemType.INSERT) {
SPChildEvent event = (SPChildEvent) e;
removeChild(event.getIndex(), (SQLObject) event.getChild(), rollForward);
} else if (type == LogItemType.REMOVE) {
SPChildEvent event = (SPChildEvent) e;
insertChild(event.getIndex(), (SQLObject) event.getChild(), rollForward);
} else if (type == LogItemType.CHANGE) {
revertChange((PropertyChangeEvent) e);
} else {
throw new UnsupportedOperationException("Unknown log item type "+type);
}
}
return true;
}
}
}
public SQLObjectSnapshot makeSQLObjectSnapshot(SQLTable t) throws Exception {
return new SQLObjectSnapshot(t);
}
/**
* Applies all the changes in this log to the given snapshot.
* @param snapshot
*/
public void rollForward(SQLObjectSnapshot snapshot) throws Exception {
for (LogItem li : log) {
LogItemType type = li.getType();
EventObject e = li.getEvent();
snapshot.applyEvent(type, e, true);
}
}
/**
* Reverts all the changes in this log on the given snapshot, in reverse order.
* @param snapshot
*/
public void rollBack(SQLObjectSnapshot snapshot) throws Exception {
snapshot.clearRollbackSnapshots();
List<LogItem> revlog = new ArrayList(log);
Collections.reverse(revlog);
for (LogItem li : revlog) {
LogItemType type = li.getType();
EventObject e = li.getEvent();
snapshot.applyEvent(type, e, false);
}
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
for (LogItem li : log) {
sb.append(li.getType()).append(": ").append(li.getEvent()).append("\n");
}
return sb.toString();
}
}
/**
* Tests for a regression case where populating a table's exported keys,
* where that would cause recursive calls to populate other tables. Ideally,
* only one connection should ever be opened. This test was originally
* written for TestFolder, but the Folder class no longer exists.
*/
public void testPopulateActiveConnections() throws Exception{
SQLDatabase db = getDb();
assertEquals(0, db.getMaxActiveConnections());
SQLTable t = db.getTableByName("SQL_TABLE_POPULATE_TEST");
t.getExportedKeys();
assertEquals(1, db.getMaxActiveConnections());
}
/**
* This test was intended to be a regression test for bug 1640.
* Unfortunately, it doesn't fail when the problem described in bug 1640 can
* be reproduced manually. We've committed this test because it's still
* useful in testing an important part of the SQLObjectListener contract.
* <p>
* A failure in this test would happen when the InsertListener gets
* notified of a child insertion at an index that no longer exists in
* the parent, or that the new child is no longer at the index the event
* says it was inserted at. In either case, this would likely happen
* only because another listener (i.e. RelationshipManager) has modified
* the child list before the other listener(s) was/were notified.
*/
public void testInsertEventsConsistentWithReality() throws Exception {
class InsertListener extends AbstractPoolingSPListener {
@Override
public void childAddedImpl(SPChildEvent e) {
if (e.getChildType() == SQLColumn.class) {
try {
assertTrue(e.getIndex() < table.getColumns().size());
assertSame(table.getColumn(e.getIndex()), e.getChild());
} catch (SQLObjectException ex) {
throw new SQLObjectRuntimeException(ex);
}
}
}
}
table.addSPListener(new InsertListener());
SQLRelationship selfref = new SQLRelationship();
table.addSPListener(new InsertListener());
selfref.attachRelationship(table, table, true);
assertEquals(9, table.getColumns().size());
assertEquals(3, selfref.getChildCount());
table.addSPListener(new InsertListener());
table.changeColumnIndex(2, 0, true);
assertEquals(9, table.getColumns().size());
assertEquals(3, selfref.getChildCount());
System.out.println(table.getSPListeners());
}
/**
* This tests inheriting a column at the end of the primary key list and
* defining the inherited column to be a primary key does indeed set the
* column to be a member of the primary key.
*/
public void testInheritDefinesPK() throws Exception {
SQLTable t2 = new SQLTable(table.getParentDatabase(), true);
t2.setName("Another Test Table");
SQLColumn newcol = new SQLColumn(t2, "newcol", Types.INTEGER, 10, 0);
t2.addColumn(newcol, 0);
t2.addToPK(newcol);
assertTrue("Column should start in primary key", newcol.isPrimaryKey());
List<SQLColumn> columns = new ArrayList<SQLColumn>(table.getColumns());
//Defining the column to be a primary key
table.inherit(table.getPkSize(), newcol, true, TransferStyles.COPY, true);
List<SQLColumn> newColumns = new ArrayList<SQLColumn>(table.getColumns());
newColumns.removeAll(columns);
assertEquals(1, newColumns.size());
SQLColumn copyCol = newColumns.get(0);
assertTrue(copyCol.isPrimaryKey());
}
/**
* Tests if the first column in the primary key is moved to be the last
* column in the primary key it actually becomes the last column in the
* primary key and not the first column not inside the primary key.
*/
public void testFirstPKMovedToLastPK() throws Exception {
SQLColumn col = table.getColumn(0);
assertEquals(3, table.getPkSize());
assertTrue(col.isPrimaryKey());
table.changeColumnIndex(0, 2, true);
assertTrue(col.isPrimaryKey());
Column wrapperForCol = null;
for (Column wrapper : table.getPrimaryKeyIndex().getChildren(Column.class)) {
if (wrapper.getColumn() == col) {
wrapperForCol = wrapper;
break;
}
}
assertEquals(2, table.getPrimaryKeyIndex().getChildren().indexOf(wrapperForCol));
assertEquals(3, table.getPkSize());
}
/**
* This tests that a physical name of a table will be updated to match its
* name correctly if they both match when they change.
*/
public void testNamesInSync() throws Exception {
SQLTable table = new SQLTable();
assertEquals(table.getName(), table.getPhysicalName());
//test changing the name updates physical and primary key names
table.setName("Testing1");
assertEquals("Testing1", table.getPhysicalName());
assertTrue(table.getPrimaryKeyIndex().getName().startsWith("Testing1"));
//you should still be able to change the physical name independent of the logical.
table.setPhysicalName("something else");
assertEquals("Testing1", table.getName());
assertEquals("something else", table.getPhysicalName());
assertTrue(table.getPrimaryKeyIndex().getName().startsWith("something else"));
//when the names are not the same, changing the logical will leave the physical alone
table.setName("Test2");
assertEquals("something else", table.getPhysicalName());
//if the names get back to be the same the logical should update the physical again.
table.setName("something else");
table.setName("Trial");
assertEquals("Trial", table.getPhysicalName());
assertTrue(table.getPrimaryKeyIndex().getName().startsWith("Trial"));
}
}