Package nexj.core.persistence.sql

Source Code of nexj.core.persistence.sql.SQLSchemaManagerTest

// Copyright 2010 NexJ Systems Inc. This software is licensed under the terms of the Eclipse Public License 1.0
package nexj.core.persistence.sql;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.Iterator;

import nexj.core.admin.etl.sql.SQLUtil;
import nexj.core.meta.Component;
import nexj.core.meta.Metadata;
import nexj.core.meta.MetadataException;
import nexj.core.meta.Primitive;
import nexj.core.meta.persistence.DataSource;
import nexj.core.meta.persistence.DataSourceAdapter;
import nexj.core.meta.persistence.DataSourceType;
import nexj.core.meta.persistence.sql.Column;
import nexj.core.meta.persistence.sql.Index;
import nexj.core.meta.persistence.sql.IndexColumn;
import nexj.core.meta.persistence.sql.RelationalDatabase;
import nexj.core.meta.persistence.sql.RelationalDatabaseFragment;
import nexj.core.meta.persistence.sql.RelationalSchema;
import nexj.core.meta.persistence.sql.RelationalSchemaTest;
import nexj.core.meta.persistence.sql.SQLScript;
import nexj.core.meta.persistence.sql.SQLStatement;
import nexj.core.meta.persistence.sql.Table;
import nexj.core.meta.persistence.sql.upgrade.AlterColumnStep;
import nexj.core.meta.persistence.sql.upgrade.AlterTableStep;
import nexj.core.meta.persistence.sql.upgrade.ApplyIndexAspectStep;
import nexj.core.meta.persistence.sql.upgrade.ApplyTableAspectStep;
import nexj.core.meta.persistence.sql.upgrade.ColumnOutline;
import nexj.core.meta.persistence.sql.upgrade.CreateColumnStep;
import nexj.core.meta.persistence.sql.upgrade.CreateIndexStep;
import nexj.core.meta.persistence.sql.upgrade.CreateTableStep;
import nexj.core.meta.persistence.sql.upgrade.DropColumnStep;
import nexj.core.meta.persistence.sql.upgrade.DropIndexStep;
import nexj.core.meta.persistence.sql.upgrade.DropTableStep;
import nexj.core.meta.persistence.sql.upgrade.ExecStep;
import nexj.core.meta.persistence.sql.upgrade.IndexOutline;
import nexj.core.meta.persistence.sql.upgrade.RelationalSchemaUpgrade;
import nexj.core.meta.persistence.sql.upgrade.RelationalSchemaUpgradeState;
import nexj.core.meta.persistence.sql.upgrade.RelationalSchemaUpgradeStep;
import nexj.core.meta.persistence.sql.upgrade.RemoveIndexAspectStep;
import nexj.core.meta.persistence.sql.upgrade.RemoveTableAspectStep;
import nexj.core.meta.persistence.sql.upgrade.RenameColumnStep;
import nexj.core.meta.persistence.sql.upgrade.RenameIndexStep;
import nexj.core.meta.persistence.sql.upgrade.RenameTableStep;
import nexj.core.meta.persistence.sql.upgrade.SupportAdapterStep;
import nexj.core.meta.upgrade.LoadUpgrade;
import nexj.core.meta.upgrade.Upgrade;
import nexj.core.meta.upgrade.VersionUpgrade;
import nexj.core.meta.xml.XMLMetadata;
import nexj.core.persistence.SchemaVersion;
import nexj.core.persistence.sql.SQLSchemaManager.SQLAppender;
import nexj.core.rpc.TransferObject;
import nexj.core.util.Binary;
import nexj.core.util.IOUtil;
import nexj.core.util.LookupException;
import nexj.core.util.PropertyMap;
import nexj.core.util.UncheckedException;
import nexj.test.util.AssertUtil;

/**
* Base test case for SQLSchemaManager.
*/
public abstract class SQLSchemaManagerTest extends SQLDataTest
{
   /**
    * The adapter test suite that this schema manager test suite gets its meta information from.
    */
   protected SQLAdapterTest m_adapterTest;

   /**
    * Active DB connection to be used by tests.
    */
   protected SQLConnection m_connection;

   /**
    * The schema manager for the persistence adapter.
    */
   protected SQLSchemaManager m_manager;

   /**
    * Step for creating a single column table used in some tests.
    */
   protected CreateTableStep m_doubleColTableStep;

   /**
    * Step for creating a single column table used in some tests.
    */
   protected CreateTableStep m_singleColTableStep;

   /**
    * Step for creating a single column table with an Index used in some tests.
    */
   protected CreateTableStep m_singleIdxColTableStep;

   /**
    * Step for creating a single column table with Primary Key used in some tests.
    */
   protected CreateTableStep m_singlePKColTableStep;

   /**
    * Constructor.
    * @param sName The name of the test.
    * @param adapterTest The adapter test object containing the relevant metadata.
    */
   public SQLSchemaManagerTest(String sName, SQLAdapterTest adapterTest)
   {
      super(sName);
      m_adapterTest = adapterTest;
   }

   /**
    * @see nexj.core.persistence.sql.SQLDataTest#setUp()
    */
   protected void setUp() throws Exception
   {
      super.setUp();
      m_manager = m_adapter.createSchemaManager(m_database);
      m_connection = m_adapter.getConnection();
      m_manager.setOwner("test"); // same as in lock() during schema creation
      m_manager.setConnection(m_connection.getConnection());

      SQLUtil.execute(m_connection.getConnection(), getSQLScript(ST_DROP));
      m_doubleColTableStep = new CreateTableStep();
      m_singleColTableStep = new CreateTableStep();
      m_singleIdxColTableStep = new CreateTableStep();
      m_singlePKColTableStep = new CreateTableStep();
      m_doubleColTableStep.setName(((RelationalSchema)m_database.getSchema()).getPrefix() + "a");
      m_singleColTableStep.setName(m_doubleColTableStep.getName());   // readSchema() returns
      m_singleIdxColTableStep.setName(m_doubleColTableStep.getName()); // prefixed table names
      m_singlePKColTableStep.setName(m_doubleColTableStep.getName());

      ColumnOutline column = new ColumnOutline("id");
      column.setType(Primitive.INTEGER);
      column.setNullable(Boolean.FALSE);

      m_doubleColTableStep.addColumnOutline(column);
      m_singleColTableStep.addColumnOutline(column);
      m_singleIdxColTableStep.addColumnOutline(column);
      m_singlePKColTableStep.addColumnOutline(column);

      column = new ColumnOutline("value");
      column.setType(Primitive.STRING);
      column.setPrecision(256);
      m_doubleColTableStep.addColumnOutline(column);

      IndexOutline index = new IndexOutline();

      index.setName(m_manager.generateIndexName("a", "ind0", null));
      index.addColumn("id", true);
      m_singleIdxColTableStep.addIndexOutline(index);

      index = new IndexOutline();
      index.setName(m_manager.generateIndexName("a", getPrimaryKeyName("pk"), null));
      index.addColumn("id", true);
      m_singlePKColTableStep.addIndexOutline(index);
      m_singlePKColTableStep.setPrimaryKeyName(index.getName());
   }

   /**
    * @see nexj.core.persistence.sql.SQLDataTest#tearDown()
    */
   protected void tearDown() throws Exception
   {
      m_manager.setConnection(null);

      try
      {
         if (m_connection != null)
         {
            m_connection.decRef();
         }
      }
      finally
      {
         super.tearDown();
      }
   }

   /**
    * Assert actual type (read from DB) is equivalent to the target type (expected to be in DB).
    * @param target The expected column type in DB.
    * @param actual The actual column type read from DB.
    */
   protected void assertType(Primitive target, Primitive actual)
   {
      assertEquals(target, actual);
   }

   /**
    * Convert a column 'from' a specific type 'to' a specific type.
    * @param from The type of initial column.
    * @param bFromLOB The allocation of the initial column is LOB (if possible).
    * @param to The type of new column.
    * @param bToLOB The allocation of the new column is LOB (if possible).
    * @param seed The seed value to insert prior to conversion.
    */
   protected void convertColumnType(
      Primitive from, boolean bFromLOB, Primitive to, boolean bToLOB, Object seed)
   {
      m_doubleColTableStep.getColumnOutline("value").setType(from);
      m_doubleColTableStep.getColumnOutline("value")
         .setAllocation((bFromLOB) ? Column.LOCATOR : Column.VARYING);
      m_doubleColTableStep.getColumnOutline("value").setNullable(Boolean.FALSE);

      if (from == Primitive.BINARY || from == Primitive.STRING)
      {
         m_doubleColTableStep.getColumnOutline("value").setPrecision(100);
         m_doubleColTableStep.getColumnOutline("value").setCaseInsensitive(Boolean.FALSE);
      }

      RelationalSchema schema = upgrade(m_doubleColTableStep, null, null);
      Table table = schema.getTable(m_doubleColTableStep.getName());

      assertNotNull(table);
      assertEquals(2, table.getColumnCount());
      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB

      Column column0 = table.getColumn(0);
      Column column1 = table.getColumn(1);

      assertType(Primitive.INTEGER, column0.getType());
      assertFalse(column0.isNullable());
      assertType(from, column1.getType());
      assertFalse(column1.isNullable());
      column1.setType(from); // reset to proper type since this will be required further in test

      StringBuffer buf = new StringBuffer(128);
      ExecStep exec = new ExecStep();
      SQLScript script = new SQLScript();
      SQLStatement insert = new SQLStatement();

      buf.append("insert into ");
      buf.append(table.getFullName(null));
      buf.append(" (");
      buf.append(column0.getName());
      buf.append(", ");
      buf.append(column1.getName());
      buf.append(") values (1, ");
      m_adapter.appendLiteral(buf, column1, seed);
      buf.append(")");
      insert.setSQL(buf.toString()); // ensure at least one row in table
      script.addStatement(insert);
      exec.getScriptHolder().addScript(script);
      upgrade(exec, null, null); // insert a record into the table to test altering an actual value

      AlterColumnStep step = new AlterColumnStep();
      ColumnOutline outline = new ColumnOutline("value");

      outline.setType(to);

      if (bToLOB)
      {
         outline.setAllocation(Column.LOCATOR);
      }
      else
      {
         outline.setAllocation( // set Column.FIXED/Column.VARYING as per original column
            (column1.getAllocation() == Column.LOCATOR) ? Column.VARYING : column1.getAllocation());
      }

      outline.setNullable(Boolean.FALSE);

      if (to == Primitive.BINARY || to == Primitive.STRING)
      {
         outline.setPrecision(100);
         outline.setCaseInsensitive(Boolean.FALSE);
      }

      step.setOutline(outline);
      step.setTableName(m_doubleColTableStep.getName());

      try
      {
         schema = upgrade(step, schema, null);
      }
      catch (RuntimeException e)
      {
         throw new RuntimeException("Error: alter column from: " + from + " to: " + to, e);
      }

      table = schema.getTable(m_doubleColTableStep.getName());
      assertNotNull(table);
      assertEquals(2, table.getColumnCount());
      column1 = table.getColumn(1);
      assertType(to, column1.getType());
      assertFalse(column1.isNullable());

      DropTableStep drop = new DropTableStep();

      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB
      drop.setName(m_doubleColTableStep.getName());
      upgrade(drop, schema, null);
   }

   /**
    * Format a timestamp according to the DB literal timestamp representation.
    * @param ts The timestamp to format.
    * @return The unquoted String DB formated representation of the timestamp.
    */
   protected String formatTimestamp(Timestamp ts)
   {
      return Primitive.toString(ts);
   }

   /**
    * @see nexj.core.persistence.sql.SQLDataTest#getAdapterName()
    */
   protected String getAdapterName()
   {
      return (m_adapterTest == null) ? super.getAdapterName() : m_adapterTest.getAdapterName();
   }

   /**
    * @see nexj.core.persistence.sql.SQLDataTest#getMetadata()
    */
   protected Metadata getMetadata()
   {
      return (m_adapterTest == null) ? super.getMetadata() : m_adapterTest.getMetadata();
   }

   /**
    * Generate a valid index name for a primary key.
    * @param sIndex The index name to use as primary key.
    */
   protected String getPrimaryKeyName(String sIndex)
   {
      return sIndex;
   }

   /**
    * @see nexj.core.persistence.sql.SQLDataTest#getSQLScript(java.lang.String)
    */
   protected URL getSQLScript(String sType)
   {
      return m_adapterTest.getSQLScript(sType);
   }

   /**
    * Read the RDBMS schema into a new RelationalSchema object.
    * @param template The schema to get configuration from (not modified).
    * @return The new schema state as read from RDBMS.
    */
   protected RelationalSchema readSchema(RelationalSchema template)
   {
      RelationalSchema schema = new RelationalSchema(); // need empty schema for readSchema()
      String sSchema = template.getPrefix(); // determine schema to avoid random tables

      if (sSchema != null)
      {
         int nSchemaSep = sSchema.indexOf('.');
         sSchema = (nSchemaSep < 0) ? null : sSchema.substring(0, nSchemaSep);
      }

      schema.setDataSource(template.getDataSource());
      schema.setIndexFill(template.getIndexFill());
      schema.setIndexspaceName(template.getIndexspaceName());
      schema.setLongspaceName(template.getLongspaceName());
      schema.setPrefix(template.getPrefix());
      schema.setRoleName(template.getRoleName());
      schema.setTablespaceName(template.getTablespaceName());
      m_manager.readSchema(schema, null, sSchema, "_", null, null); //limit to single char table

      return schema;
   }

   public void testAlterColumnStep()
   {
      RelationalSchema schema = upgrade(m_singleColTableStep, null, null);
      Table table = schema.getTable(m_singleColTableStep.getName());

      assertNotNull(table);
      assertEquals(1, table.getColumnCount());
      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB

      Column column = table.getColumn(0);

      assertEquals(Primitive.INTEGER, column.getType());
      assertFalse(column.isNullable());

      AlterColumnStep step = new AlterColumnStep();
      ColumnOutline outline = new ColumnOutline("id");

      outline.setType(Primitive.STRING);
      outline.setNullable(Boolean.TRUE);
      step.setOutline(outline);
      step.setTableName(m_singleColTableStep.getName());
      schema = upgrade(step, schema, null);
      table = schema.getTable(m_singleColTableStep.getName());
      assertNotNull(table);
      assertEquals(1, table.getColumnCount());
      column = table.getColumn(0);
      assertEquals(Primitive.STRING, column.getType());
      assertTrue(column.isNullable());
   }

   public void testAlterTableStep()
   {
      RelationalSchema schema = upgrade(m_singleIdxColTableStep, null, null);
      Table table = schema.getTable(m_singleIdxColTableStep.getName());

      assertNotNull(table);
      assertNull(table.getPrimaryKey());
      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB

      AlterTableStep step = new AlterTableStep();

      step.setName(m_singleIdxColTableStep.getName());
      step.setPrimaryKeyName(table.getIndex(0).getName());
      schema = upgrade(step, schema, null);
      table = schema.getTable(m_singleIdxColTableStep.getName());

      assertNotNull(table);

      Index index = table.getPrimaryKey();

      assertNotNull(index);
      assertEquals(
         m_manager.generateIndexName(table.getTableName(),
                                     getPrimaryKeyName(step.getPrimaryKeyName()).replace('.', '_'),
                                     null),
         index.getName());
      assertEquals(1, index.getIndexColumnCount());

      Column column = index.getIndexColumn(0).getColumn();

      assertEquals("id", column.getName());
      assertEquals(Primitive.INTEGER, column.getType());
   }

   public abstract void testAppendConcatenate();

   public abstract void testAppendTSExtract();

   public abstract void testAppendTSIncrement();

   public void testApplyIndexAspectStep()
   {
      IndexOutline outline = new IndexOutline();

      outline.setName("ind0");
      outline.addColumn("id", true);
      m_doubleColTableStep.addIndexOutline(outline);

      RelationalSchema schema = upgrade(m_doubleColTableStep, null, null);
      Table table = schema.getTable(m_doubleColTableStep.getName());

      assertNotNull(table);
      assertEquals(1, table.getIndexCount());
      assertEquals(1, table.getIndex(0).getIndexColumnCount());
      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB

      Index aspect = new Index("aspect", Index.ASPECT, null);

      aspect.addIndexColumn(new IndexColumn(table.getColumn(1), true));
      schema.addIndex(aspect);

      ApplyIndexAspectStep step = new ApplyIndexAspectStep();

      step.setAspectName(aspect.getName());
      step.addPointcutPattern(table.getIndex(0).getName(), true);
      schema = upgrade(step, schema, null);
      table = schema.getTable(m_doubleColTableStep.getName());
      assertNotNull(table);
      assertEquals(1, table.getIndexCount());

      Index index = table.getIndex(0);

      assertEquals(2, index.getIndexColumnCount()); // aspect columns always before index columns
      assertEquals("value", index.getIndexColumn(0).getColumn().getName());
   }

   public void testApplyTableAspectStep()
   {
      RelationalSchema schema = upgrade(m_singleColTableStep, null, null);
      Table table = schema.getTable(m_singleColTableStep.getName());

      assertNotNull(table);
      assertEquals(1, table.getColumnCount());
      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB

      Table aspect = new Table(schema);
      Column column = new Column("value", aspect);

      aspect.setName(m_manager.getFullTableName(null, "aspect"));
      aspect.setType(Table.ASPECT);
      column.setType(Primitive.STRING);
      aspect.addColumn(column);

      ApplyTableAspectStep step = new ApplyTableAspectStep();

      step.setAspectName(aspect.getName());
      step.addPointcutPattern(table.getName(), true);
      schema.addTable(aspect);
      schema = upgrade(step, schema, null);
      table = schema.getTable(m_singleColTableStep.getName());
      assertNotNull(table);
      assertEquals(2, table.getColumnCount());
      assertEquals("id", table.getColumn(0).getName());
      assertEquals("value", table.getColumn(1).getName());
   }

   public void testConvertColumnType()
   {
      Object[] array = new Object[]
      {
         Primitive.BOOLEAN, Boolean.TRUE,
         Primitive.DECIMAL, new BigDecimal("1.101"),
         Primitive.DOUBLE, new Double(1.101d),
         Primitive.FLOAT, new Float(1.101f),
         Primitive.INTEGER, new Integer(12345),
         Primitive.LONG, new Long(12345),
         Primitive.STRING, new String("12345"),
      };

      for (int i = 0; i < array.length; i += 2)
      {
         for (int j = 0; j < array.length; j += 2)
         {
            convertColumnType((Primitive)array[i], false, (Primitive)array[j], false, array[i + 1]);
         }
      }

      // Binary conversion only supported to/from String
      Binary bin = new Binary(new byte[]{1,2,3});

      convertColumnType(Primitive.BINARY, false, Primitive.STRING, false, bin);
      convertColumnType(Primitive.BINARY, true, Primitive.STRING, true, bin);
      convertColumnType(Primitive.STRING, false, Primitive.BINARY, false, "abcd");
      convertColumnType(Primitive.STRING, true, Primitive.BINARY, true, "abcd");

      // Timestamp conversion not supported to/from binary/boolean
      Timestamp ts = new Timestamp(Integer.MAX_VALUE); // value that fits into a 32 bit signed int

      array = new Object[]
      {
         Primitive.DECIMAL, new BigDecimal("123"),
         Primitive.DOUBLE, new Double(123d),
         Primitive.FLOAT, new Float(123f),
         Primitive.INTEGER, new Integer(123),
         Primitive.LONG, new Long(123),
         Primitive.STRING, formatTimestamp(ts),
      };

      for (int i = 0; i < array.length; i += 2)
      {
         convertColumnType((Primitive)array[i], false, Primitive.TIMESTAMP, false, array[i + 1]);
         convertColumnType(Primitive.TIMESTAMP, false, (Primitive)array[i], false, ts);
      }
   }

   public void testCreateColumnStep()
   {
      RelationalSchema schema = upgrade(m_singleColTableStep, null, null);
      Table table = schema.getTable(m_singleColTableStep.getName());

      assertNotNull(table);
      assertEquals(1, table.getColumnCount());
      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB

      CreateColumnStep step = new CreateColumnStep();
      ColumnOutline column = new ColumnOutline("value");

      column.setType(Primitive.STRING);
      step.setTableName(m_singleColTableStep.getName());
      step.setOutline(column);
      schema = upgrade(step, schema, null);
      table = schema.getTable(m_singleColTableStep.getName());
      assertNotNull(table);
      assertEquals(2, table.getColumnCount());
      assertEquals("value", table.getColumn(1).getName());
   }

   public void testCreateIndexStep()
   {
      RelationalSchema schema = upgrade(m_singleColTableStep, null, null);
      Table table = schema.getTable(m_singleColTableStep.getName());

      assertNotNull(table);
      assertEquals(0, table.getIndexCount());
      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB

      CreateIndexStep step = new CreateIndexStep();
      IndexOutline outline = new IndexOutline();

      outline.setName("idx01");
      outline.addColumn("id", true);
      step.setTableName(table.getName());
      step.setOutline(outline);
      schema = upgrade(step, schema, null);
      table = schema.getTable(m_singleColTableStep.getName());
      assertNotNull(table);
      assertEquals(1, table.getIndexCount());

      Index index = table.getIndex(0);

      assertEquals(1, index.getIndexColumnCount());
      assertEquals("id", index.getIndexColumn(0).getColumn().getName());
   }

   public void testCreateTableStep()
   {
      RelationalSchema schema = upgrade(m_singlePKColTableStep, null, null);
      Table table = schema.getTable(m_singlePKColTableStep.getName());

      assertNotNull(table);
      assertEquals(1, table.getColumnCount());

      Column column = table.getColumn(0);

      assertEquals("id", column.getName());
      assertEquals(Primitive.INTEGER, column.getType());

      // validate tablespace propagation, cannot use DB since tablespaces not defined there
      schema = new RelationalSchema();
      schema.setDataSource(m_database);
      m_singlePKColTableStep.setIndexspaceName("iSpace");
      m_singlePKColTableStep.setLongspaceName("lSpace");
      m_singlePKColTableStep.setTablespaceName("tSpace");
      m_singlePKColTableStep.apply(new RelationalSchemaUpgradeState(schema, null, null));
      table = schema.getTable(m_singlePKColTableStep.getName());
      assertNotNull(table);
      assertEquals(m_singlePKColTableStep.getIndexspaceName(), table.getIndexspaceName());
      assertEquals(m_singlePKColTableStep.getLongspaceName(), table.getLongspaceName());
      assertEquals(m_singlePKColTableStep.getTablespaceName(), table.getTablespaceName());
   }

   public void testDropColumnStep()
   {
      RelationalSchema schema = upgrade(m_doubleColTableStep, null, null);
      Table table = schema.getTable(m_doubleColTableStep.getName());

      assertNotNull(table);
      assertEquals(2, table.getColumnCount());
      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB

      DropColumnStep step = new DropColumnStep();

      step.setName("value");
      step.setTableName(table.getName());
      schema = upgrade(step, schema, null);
      table = schema.getTable(m_doubleColTableStep.getName());
      assertNotNull(table);
      assertEquals(1, table.getColumnCount());
      assertEquals("id", table.getColumn(0).getName());
   }

   public void testDropIndexStep()
   {
      RelationalSchema schema = upgrade(m_singleIdxColTableStep, null, null);
      Table table = schema.getTable(m_singleIdxColTableStep.getName());

      assertNotNull(table);
      assertEquals(1, table.getIndexCount());
      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB

      DropIndexStep step = new DropIndexStep();

      step.setName(table.getIndex(0).getName());
      schema = upgrade(step, schema, null);
      table = schema.getTable(m_singleIdxColTableStep.getName());
      assertNotNull(table);
      assertEquals(0, table.getIndexCount());
   }

   public void testDropTableStep()
   {
      RelationalSchema schema = upgrade(m_singlePKColTableStep, null, null);
      Table table = schema.getTable(m_singlePKColTableStep.getName());

      assertNotNull(table);
      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB

      DropTableStep step = new DropTableStep();

      step.setName(m_singlePKColTableStep.getName());
      schema = upgrade(step, schema, null);
      assertNull(schema.findTable(m_singlePKColTableStep.getName()));
   }

   public void testDropUpgradeSchema()
   {
      SQLAppender origAppender = m_manager.getSQLAppender();
      RelationalSchema schema = new RelationalSchema(); // schema used for dropTable step
      Table table = new Table(schema);
      StringWriter sql = new StringWriter();
      DropTableStep dropStep = new DropTableStep();
      String sDropSQL;

      table.setName("test.A"); // this table exists in Main.upgrade
      schema.addTable(table);
      schema.setDataSource(m_database);
      dropStep.setName(table.getName());
      m_manager.m_state = new RelationalSchemaUpgradeState(schema, null, null); // required for drop
      m_manager.setSQLAppender(m_manager.new SQLWriterAppender(sql));

      try
      {
         m_manager.dropTable(dropStep);
         sDropSQL = sql.getBuffer().toString();
         sql.getBuffer().setLength(0);
         m_manager.dropSchema((RelationalSchema)m_database.getSchema());
      }
      finally
      {
         m_manager.setSQLAppender(origAppender);
      }

      int nPos = sql.getBuffer().indexOf(sDropSQL);

      assertTrue(nPos >= 0); // at least one occurrence
      assertTrue(sql.getBuffer().indexOf(sDropSQL, nPos + 1) < 0); // no more than one occurrence
   }

   public void testExecStep()
   {
      StringWriter sql = new StringWriter();

      upgrade(m_singleColTableStep, null, sql);

      ExecStep step = new ExecStep();
      SQLScript script = new SQLScript();
      SQLStatement stmt = new SQLStatement();
      String sSQL = sql.toString();
      int nSQLStmt = sSQL.indexOf(m_manager.getSeparator());

      stmt.setSQL((nSQLStmt < 0) ? sSQL : sSQL.substring(0, nSQLStmt)); // only first statement
      script.addStatement(stmt);
      step.getScriptHolder().addScript(script);

      RelationalSchema schema = upgrade(step, null, null);
      Table table = schema.getTable(m_singleColTableStep.getName());

      assertNotNull(table);
      assertEquals(Table.EXTERNAL, table.getType()); // comes in as Table.EXTERNAL from DB

      DataSource ds = schema.getDataSource();

      for (Iterator/*<DataSourceAdapter>*/ itr = ds.getType().getAdapterIterator(); itr.hasNext();)
      {
         String sName = ((DataSourceAdapter)itr.next()).getName();

         if (!ds.getAdapter().getName().equals(sName)) // adapter name not matching current adapter
         {
            stmt.addAdapter(sName, ds.getType());
            break;
         }
      }

      if (stmt.getAdapterCount() == 0)
      {
         return; // no way to test undefined adapter if only one adapter defined in system.dstypes
      }

      try
      {
         upgrade(step, null, null); // test ExecStep which does not match current adapter
         fail(); // nexj.core.meta.MetadataException expected
      }
      catch (MetadataException e)
      {
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertEquals("err.meta.sql.statementAdapter", e.getErrorCode());
      }
   }

   public void testHint()
   {
      RelationalSchemaUpgrade version = // version testing hints upgrade
         (RelationalSchemaUpgrade)getMetadata().getUpgrade("Main").getVersion("15");
      RelationalSchemaUpgradeState state =
         (RelationalSchemaUpgradeState)Upgrade.getState(Upgrade.getInitialState(version), version);

      assertNull(state.getSchema().findTable("A"));
      version.getStep(0).apply(state); // CreateTable

      Table table = state.getSchema().getTable("A");
      Iterator/*<String>*/ itr = table.getHintIterator();

      itr.next(); // 1st hint
      itr.next(); // 2nd hint
      assertFalse(itr.hasNext());
      assertTrue(table.isHintEnabled("test1"));
      assertTrue(table.isHintEnabled("test2"));
      version.getStep(1).apply(state); // AlterTable

      itr = table.getHintIterator();
      itr.next(); // 1st hint
      itr.next(); // 2nd hint
      assertFalse(itr.hasNext());
      assertTrue(table.isHintEnabled("test1"));
      assertTrue(table.isHintEnabled("test3"));
   }

   public void testIndexDiscardState()
   {
      SQLAppender origAppender = m_manager.getSQLAppender();
      RelationalSchema schema = new RelationalSchema();
      Table table = new Table(schema);
      Column column = new Column("col", table);
      Index index = new Index("indx", Index.CLUSTER, table);

      table.setName("test"); // table name required to add table to schema, to add index to schema
      schema.addTable(table);
      column.setType(Primitive.INTEGER);
      table.addColumn(column);
      index.addIndexColumn(new IndexColumn(column, true));
      table.addIndex(index);
      table.setPrimaryKey(index);
      schema.setDataSource(m_database);

      // validate initial schema state
      assertEquals(1, schema.getIndexCount());
      assertEquals(index, schema.findIndex("indx"));

      try
      {
         m_manager.setSQLAppender(m_manager.new SQLWriterAppender(new StringWriter()));
         m_manager.discardIndex(index, true);
      }
      finally
      {
         m_manager.setSQLAppender(origAppender);
      }

      // schema definition is untouched
      assertEquals(1, schema.getIndexCount());
      assertEquals(index, schema.findIndex("indx"));

      // index either removed or renamed
      assertTrue(table.getIndexCount() == 0 || !index.getName().equals("indx"));
   }

   public void testLoad()
   {
      Metadata metadata = getMetadata();
      RelationalSchema schema = (RelationalSchema)m_database.getSchema();
      Table origVersionTable = schema.getVersionTable(); // note original
      VersionUpgrade loadUpgrade = LoadUpgrade.create(metadata.getVersion(), metadata, null, null);
      String sSQL;

      try
      {
         schema.setVersionTable(schema.getTable("Version"));
         sSQL = upgrade(loadUpgrade, schema);
      }
      finally
      {
         schema.setVersionTable(origVersionTable); // reset so testUpgrade() will not fail
      }

      AssertUtil.assertContained("update test.Version set loaded=0", sSQL);
   }

   public void testRemoveIndexAspectStep()
   {
      IndexOutline outline = new IndexOutline();

      outline.setName("idx01");
      outline.addColumn("id", true);
      outline.addColumn("value", true);
      m_doubleColTableStep.addIndexOutline(outline);

      RelationalSchema schema = upgrade(m_doubleColTableStep, null, null);
      Table table = schema.getTable(m_doubleColTableStep.getName());

      assertNotNull(table);
      assertEquals(1, table.getIndexCount());
      assertEquals(2, table.getIndex(0).getIndexColumnCount());
      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB

      Index aspect = new Index("aspect", Index.ASPECT, null);

      aspect.addIndexColumn(new IndexColumn(table.getColumn("value"), true));
      table.getIndex(0).addAspect(aspect);
      schema.addIndex(aspect);

      RemoveIndexAspectStep step = new RemoveIndexAspectStep();

      step.setAspectName("aspect");
      step.addPointcutPattern(table.getIndex(0).getName(), true);
      schema = upgrade(step, schema, null);
      table = schema.getTable(m_doubleColTableStep.getName());
      assertNotNull(table);
      assertEquals(1, table.getIndexCount());

      Index index = table.getIndex(0);

      assertEquals(1, index.getIndexColumnCount());
      assertEquals("id", index.getIndexColumn(0).getColumn().getName());
   }

   public void testRemoveTableAspectStep()
   {
      RelationalSchema schema = upgrade(m_doubleColTableStep, null, null);
      Table table = schema.getTable(m_doubleColTableStep.getName());

      assertNotNull(table);
      assertEquals(2, table.getColumnCount());
      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB

      Table aspect = new Table(schema);

      aspect.setName(m_manager.getFullTableName(null, "aspect"));
      aspect.setType(Table.ASPECT);
      aspect.addColumn(new Column("value", aspect));
      table.addAspect(aspect);
      schema.addTable(aspect);

      RemoveTableAspectStep step = new RemoveTableAspectStep();

      step.setAspectName(aspect.getName());
      step.addPointcutPattern(table.getName(), true);
      schema = upgrade(step, schema, null);
      table = schema.getTable(m_doubleColTableStep.getName());
      assertNotNull(table);
      assertEquals(1, table.getColumnCount());
      assertEquals("id", table.getColumn(0).getName());
   }

   public void testRenameColumnStep() throws SQLException
   {
      RelationalSchema schema = upgrade(m_singleColTableStep, null, null);
      Table table = schema.getTable(m_singleColTableStep.getName());

      assertNotNull(table);
      assertEquals(1, table.getColumnCount());
      assertEquals("id", table.getColumn(0).getName());
      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB

      SQLStatement statement = new SQLStatement();
      SQLScript script = new SQLScript();
      ExecStep populate = new ExecStep();

      statement.setSQL("insert into " + table.getQuotedName() + " values (1)");
      script.addStatement(statement);
      populate.getScriptHolder().addScript(script);
      upgrade(populate, schema, null);

      RenameColumnStep step = new RenameColumnStep();

      step.setTableName(table.getName());
      step.setOldName("id");
      step.setNewName("id1");
      schema = upgrade(step, schema, null);
      table = schema.getTable(m_singleColTableStep.getName());
      assertNotNull(table);
      assertEquals(1, table.getColumnCount());
      assertEquals("id1", table.getColumn(0).getName());

      // ensure no data loss
      Statement stmt = null;
      ResultSet rs = null;

      try
      {
         stmt = m_connection.getConnection().createStatement();
         rs = stmt.executeQuery("select count(*) from " + table.getFullName(m_manager.getOwner()));
         assertNotNull(rs);
         assertTrue(rs.next());
         assertEquals(1, rs.getInt(1));
      }
      finally
      {
         if (rs != null)
         {
            rs.close();
         }

         if (stmt != null)
         {
            stmt.close();
         }
      }
   }

   public void testRenameIndexStep()
   {
      RelationalSchema schema = upgrade(m_singleIdxColTableStep, null, null);
      Table table = schema.getTable(m_singleIdxColTableStep.getName());

      assertNotNull(table);
      assertEquals(1, table.getIndexCount());
      assertEquals(m_manager.generateIndexName(table.getTableName(), "ind0", null),
                   table.getIndex(0).getName());
      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB

      RenameIndexStep step = new RenameIndexStep();

      step.setOldName(table.getIndex(0).getName());
      step.setNewName("ind1");
      schema = upgrade(step, schema, null);
      table = schema.getTable(m_singleIdxColTableStep.getName());
      assertNotNull(table);
      assertEquals(1, table.getIndexCount());
      assertEquals(m_manager.generateIndexName(table.getTableName(), "ind1", null),
                   table.getIndex(0).getName());
   }

   public void testRenameTableStep() throws SQLException
   {
      RelationalSchema schema = upgrade(m_singlePKColTableStep, null, null);
      Table table = schema.getTable(m_singlePKColTableStep.getName());

      assertNotNull(table);
      table.setType(Table.MANAGED); // comes in as Table.EXTERNAL from DB

      SQLStatement statement = new SQLStatement();
      SQLScript script = new SQLScript();
      ExecStep populate = new ExecStep();

      statement.setSQL("insert into " + table.getQuotedName() + " values (1)");
      script.addStatement(statement);
      populate.getScriptHolder().addScript(script);
      upgrade(populate, schema, null);

      RenameTableStep step = new RenameTableStep();
      step.setOldName(table.getName());
      step.setNewName("B");
      schema = upgrade(step, schema, null);

      assertNull(schema.findTable(m_singlePKColTableStep.getName()));

      table = schema.getTable(schema.getPrefix() + "b");

      assertNotNull(table);
      assertEquals(1, table.getColumnCount());

      Column column = table.getColumn(0);

      assertEquals("id", column.getName());
      assertEquals(Primitive.INTEGER, column.getType());

      // ensure no data loss
      Statement stmt = null;
      ResultSet rs = null;

      try
      {
         stmt = m_connection.getConnection().createStatement();
         rs = stmt.executeQuery("select count(*) from " + table.getFullName(m_manager.getOwner()));
         assertNotNull(rs);
         assertTrue(rs.next());
         assertEquals(1, rs.getInt(1));
      }
      finally
      {
         if (rs != null)
         {
            rs.close();
         }

         if (stmt != null)
         {
            stmt.close();
         }
      }
   }

   public void testSchemaVersion()
   {
      RelationalSchema schema = (RelationalSchema)m_database.getSchema();
      Metadata metadata = schema.getMetadata();
      StringWriter writer = new StringWriter();
      Table origVersionTable = schema.getVersionTable(); // note original
      SQLAppender origAppender = m_manager.getSQLAppender();

      try
      {
         schema.setVersionTable(schema.getTable("Version"));
         m_manager.setSQLAppender(m_manager.new SQLWriterAppender(writer));
         m_manager.createSchema(schema);
      }
      finally
      {
         m_manager.setSQLAppender(origAppender);
         schema.setVersionTable(origVersionTable); // reset so testUpgrade() will not fail
      }

      StringBuffer buf =
         new StringBuffer(".Version(namespace, version, step, upgradable, test, loaded) values (");

      m_adapter.appendLiteral(buf, Primitive.STRING, metadata.getNamespace());
      buf.append(", ");
      m_adapter.appendLiteral(buf, Primitive.STRING, metadata.getVersion());
      buf.append(", ");
      m_adapter.appendLiteral(buf, Primitive.INTEGER, Primitive.createInteger(SQLSchemaManager.UPGRADE_END_STEP));
      buf.append(", ");
      m_adapter.appendLiteral(buf, Primitive.BOOLEAN, Boolean.FALSE);
      buf.append(", ");
      m_adapter.appendLiteral(buf, Primitive.BOOLEAN, Boolean.FALSE);
      buf.append(", ");
      m_adapter.appendLiteral(buf, Primitive.BOOLEAN, Boolean.FALSE);
      buf.append(')');

      AssertUtil.assertContained(buf, writer.toString());
   }

   public void testSetupDefaultProperties()
   {
      RelationalSchema schema = (RelationalSchema)m_database.getSchema();
      PropertyMap defaults = m_manager.getDefaultDatabaseProperties(schema, null);
      PropertyMap custom = m_manager.getDefaultDatabaseProperties(schema, defaults); // reinterpret

      assertEquals(defaults.getValueCount(), custom.getValueCount());

      // ensure all keys exist in both sets and non-null values are set for same keys
      for (Iterator itr = defaults.getIterator(); itr.hasNext();)
      {
         String sKey = (String)itr.next();

         assertTrue(defaults.getValue(sKey) == null || custom.getValue(sKey) != null);
      }
   }

   public void testSetupDefaultsExtractor() throws IOException
   {
      PropertyMap propertyMap = new TransferObject();
      PropertyMap referenceMap = new TransferObject();
      String input = "${validKey}${emptyKey}${multiKey:multiValue}${multiKey:multiValue2}"
                   + "${for-each:tablespace:${recurKey:recurValue1}}"
                   + "${for-each:tablespace:${recurKey:recurValue2}}"
                   + "${emptyKey:emptyValue}";

      referenceMap.setValue("validKey", null);
      referenceMap.setValue("multiKey", null);
      referenceMap.setValue("recurKey", null);
      referenceMap.setValue("emptyKey", null);
      propertyMap.setValue("validKey", "validValue");
      propertyMap.setValue("multiKey", "multiValue");
      propertyMap.setValue("recurKey", null);
      propertyMap.setValue("emptyKey", null);
      m_manager.setTemplateDefaults(propertyMap, referenceMap, new StringReader(input), true);

      assertEquals(4, propertyMap.getValueCount());
      assertEquals("validValue", propertyMap.getValue("validKey"));
      assertEquals("multiValue", propertyMap.getValue("multiKey"));
      assertEquals("recurValue1", propertyMap.getValue("recurKey"));
      assertEquals("emptyValue", propertyMap.getValue("emptyKey"));

      // test ${iftest:...} with test environment enabled
      input = "${iftest:${emptyKey:emptyValue}}";
      propertyMap = new TransferObject();
      propertyMap.setValue("emptyKey", null);
      m_manager.setTemplateDefaults(propertyMap, referenceMap, new StringReader(input), true);
      assertEquals(1, propertyMap.getValueCount());
      assertEquals("emptyValue", propertyMap.getValue("emptyKey"));

      // test ${iftest:...} with test environment disabled
      propertyMap = new TransferObject();
      propertyMap.setValue("emptyKey", null);
      m_manager.setTemplateDefaults(propertyMap, referenceMap, new StringReader(input), false);
      assertEquals(1, propertyMap.getValueCount());
      assertNull(propertyMap.getValue("emptyKey"));
   }

   public void testSetupReader() throws IOException
   {
      StringWriter output = new StringWriter();
      RelationalSchema schema = new RelationalSchema();
      TransferObject valueMap = new TransferObject();
      TransferObject defaultsMap = new TransferObject();
      Table tmpTable;

      tmpTable = new Table(schema);
      tmpTable.setName("table1"); // required for addTable() to work
      tmpTable.setLongspaceName("longspace1");
      schema.addTable(tmpTable);
      tmpTable = new Table(schema);
      tmpTable.setName("table2"); // required for addTable() to work
      tmpTable.setLongspaceName("longspace2");
      schema.addTable(tmpTable);
      tmpTable = new Table(schema);
      tmpTable.setName("table3"); // required for addTable() to work
      tmpTable.setLongspaceName("longspace3");
      schema.addTable(tmpTable);
      valueMap.setValue("valid-key", "valid-value");
      valueMap.setValue("missing-key", null);
      valueMap.setValue("default-key", null);
      defaultsMap.setValue("default-key", "default-value");
      valueMap.setValue("tablespace", null);
      valueMap.setValue("indexspace", "indexspace-value");
      valueMap.setValue("longspace", null);

      // existing value
      IOUtil.copy(output,
                  new SQLSchemaManager.DatabaseTemplateSubstReader(
                     new StringReader("[${valid-key}]"),
                     schema,
                     valueMap,
                     defaultsMap,
                     '/'));
      assertEquals("[valid-value]", output.toString());

      // missing value
      output.getBuffer().setLength(0);
      try
      {
         IOUtil.copy(output,
                     new SQLSchemaManager.DatabaseTemplateSubstReader(
                        new StringReader("[${missing-key}]"),
                        schema,
                        valueMap,
                        defaultsMap,
                        '/'));
         fail(); // exception expected
      }
      catch (LookupException e)
      {
         assertEquals("err.meta.persistence.sql.undefined", e.getErrorCode());
      }

      // missing value with default
      output.getBuffer().setLength(0);
      IOUtil.copy(output,
                  new SQLSchemaManager.DatabaseTemplateSubstReader(
                     new StringReader("[${default-key}]"),
                     schema,
                     valueMap,
                     defaultsMap,
                     '/'));
      assertEquals("[default-value]", output.toString());

      // unknown value
      output.getBuffer().setLength(0);
      try
      {
         IOUtil.copy(output,
                     new SQLSchemaManager.DatabaseTemplateSubstReader(
                        new StringReader("[${unknown-key}]"),
                        schema,
                        valueMap,
                        defaultsMap,
                        '/'));
         fail(); // exception expected
      }
      catch (LookupException e)
      {
         assertEquals("err.meta.persistence.sql.variable", e.getErrorCode());
      }

      // foreach with no tables, no default tablespace
      output.getBuffer().setLength(0);
      IOUtil.copy(output,
                  new SQLSchemaManager.DatabaseTemplateSubstReader(
                     new StringReader("[${for-each:tablespace:${tablespace}}]"),
                     schema,
                     valueMap,
                     defaultsMap,
                     '/'));
      assertEquals("[]", output.toString());

      // foreach with no tables, only default tablespace
      output.getBuffer().setLength(0);
      IOUtil.copy(output,
                  new SQLSchemaManager.DatabaseTemplateSubstReader(
                     new StringReader("[${for-each:indexspace:${indexspace}}]"),
                     schema,
                     valueMap,
                     defaultsMap,
                     '/'));
      assertEquals("[indexspace-value]", output.toString());

      // foreach with several tables
      output.getBuffer().setLength(0);
      IOUtil.copy(output,
                  new SQLSchemaManager.DatabaseTemplateSubstReader(
                     new StringReader("[${for-each:longspace:${longspace}}]"),
                     schema,
                     valueMap,
                     defaultsMap,
                     '/'));
      assertEquals("[longspace1longspace2longspace3]", output.toString());

      // foreach not collection
      output.getBuffer().setLength(0);
      IOUtil.copy(output,
                  new SQLSchemaManager.DatabaseTemplateSubstReader(
                     new StringReader("[${for-each:non-collection:default-value}]"),
                     schema,
                     valueMap,
                     defaultsMap,
                     '/'));
      assertEquals("[non-collection:default-value]", output.toString());

      // set schema.getMetadata().isTestEnvironment() return value for test
      XMLMetadata metadata = new XMLMetadata(null, null, null, null, null);
      DataSourceType dsType = new DataSourceType(null);
      DataSource ds = new DataSource(null) {};

      dsType.setMetadata(metadata);
      ds.setType(dsType);
      schema.setDataSource(ds);

      // iftest enabled
      metadata.setTestEnvironment(true);
      output.getBuffer().setLength(0);
      IOUtil.copy(output,
                  new SQLSchemaManager.DatabaseTemplateSubstReader(
                     new StringReader("[${iftest:${valid-key}}]"),
                     schema,
                     valueMap,
                     defaultsMap,
                     '/'));
      assertEquals("[valid-value]", output.toString());

      // iftest disabled
      metadata.setTestEnvironment(false);
      output.getBuffer().setLength(0);
      IOUtil.copy(output,
                  new SQLSchemaManager.DatabaseTemplateSubstReader(
                     new StringReader("[${iftest:${valid-key}}]"),
                     schema,
                     valueMap,
                     defaultsMap,
                     '/'));
      assertEquals("[]", output.toString());

      // path MSFT compatible
      valueMap.setValue("datapath", "C:\\test\\path");
      output.getBuffer().setLength(0);
      IOUtil.copy(output,
                  new SQLSchemaManager.DatabaseTemplateSubstReader(
                     new StringReader("[${path:${datapath:/abc/def} /ghi/jkl}]"),
                     schema,
                     valueMap,
                     defaultsMap,
                     '\\'));
      assertEquals("[C:\\test\\path \\ghi\\jkl]", output.toString());

      output.getBuffer().setLength(0);
      IOUtil.copy(output,
                  new SQLSchemaManager.DatabaseTemplateSubstReader(
                     new StringReader("[${path:${datapath:C:\\abc\\def} D:\\ghi\\jkl ab:\\xy}]"),
                     schema,
                     valueMap,
                     defaultsMap,
                     '\\'));
      assertEquals("[C:\\test\\path D:\\ghi\\jkl ab:\\xy]", output.toString());

      // path generic
      valueMap.setValue("datapath", "/test/path");
      output.getBuffer().setLength(0);
      IOUtil.copy(output,
                  new SQLSchemaManager.DatabaseTemplateSubstReader(
                     new StringReader("[${path:${datapath:/abc/def} /ghi/jkl}]"),
                     schema,
                     valueMap,
                     defaultsMap,
                     '/'));
      assertEquals("[/test/path /ghi/jkl]", output.toString());

      output.getBuffer().setLength(0);
      IOUtil.copy(output,
                  new SQLSchemaManager.DatabaseTemplateSubstReader(
                     new StringReader("[${path:${datapath:c:\\abc\\def} d:\\ghi\\jkl ab:\\xy}]"),
                     schema,
                     valueMap,
                     defaultsMap,
                     '/'));
      assertEquals("[/test/path d:/ghi/jkl ab:/xy]", output.toString());
   }

   public void testViewScriptUpgradeMismtch()
   {
      Upgrade upgrade = new Upgrade(null);
      RelationalSchemaUpgrade version = new RelationalSchemaUpgrade("test");
      XMLMetadata metadata = new XMLMetadata(null, null, null, null, null);
      RelationalDatabase ds = new RelationalDatabase(null);
      RelationalSchema schema = new RelationalSchema();
      CreateTableStep step = new CreateTableStep(); // only step that can populate ViewScript
      Table table = new Table(schema);
      SQLScript finalScript = new SQLScript();
      SQLScript upgradeScript = new SQLScript();
      SQLStatement finalStmt = new SQLStatement();
      SQLStatement upgradeStmt = new SQLStatement();

      upgradeStmt.addAdapter("*", m_database.getSchema().getDataSource().getType());
      finalStmt.addAdapter("*", m_database.getSchema().getDataSource().getType());
      upgradeStmt.setSQL("upgrade view SQL");
      finalStmt.setSQL("final view SQL");
      upgradeScript.addStatement(upgradeStmt);
      finalScript.addStatement(finalStmt);
      table.setName("testTable");
      table.setType(Table.VIEW);
      step.setName(table.getName());
      step.setType(table.getType());
      step.setViewScript(upgradeScript);
      schema.addTable(table);
      schema.setDataSource(ds);
      ds.setAdapter(m_database.getSchema().getDataSource().getAdapter());
      ds.setSchema(schema);
      ds.setType(new DataSourceType(null));
      ds.getType().setMetadata(metadata);
      version.setDataSource(ds);
      version.addStep(step);
      metadata.setVersion(version.getName());
      upgrade.setMetadata(metadata);
      upgrade.addVersion(version);
      table.setViewScript(finalScript);

      try
      {
         upgrade.validate(metadata, null);
         fail(); // exception expected
      }
      catch (MetadataException e)
      {
         assertEquals("err.meta.upgrade.sql.viewTableMismatch",
                      ((UncheckedException)e.getCause()).getErrorCode());
      }

      upgradeStmt.setSQL(finalStmt.getSQL()); // different object with same value
      upgrade.validate(metadata, null); // this must pass without exception
   }

   public void testUpgradeValidation()
   {
      StringWriter buf = new StringWriter();
      DataSourceAdapter invalid = new DataSourceAdapter("InvalidAdapter");
      DataSourceAdapter valid = new DataSourceAdapter("ValidAdapter");
      RelationalDatabase ds = new RelationalDatabase("DataSource");
      RelationalSchema schema = new RelationalSchema();
      final Upgrade upgrade = new Upgrade(null);
      XMLMetadata metadata = new XMLMetadata(null, null, null, null, null)
      {
         public Upgrade getUpgrade(String sName) { return upgrade; }
      };
      SQLSchemaManager manager = m_adapter.createSchemaManager();
      RelationalSchemaUpgrade upgradeVersion;

      ds.setAdapter(valid);
      ds.setComponent(new Component("Component"));
      ds.setSchema(schema);
      ds.setType(new DataSourceType("DataSourceType"));
      ds.getType().setMetadata(metadata);
      ds.getType().addAdapter(ds.getAdapter());
      ds.getType().addAdapter(invalid);
      ((RelationalDatabaseFragment)ds.getDefaultFragment()).setDatabase("Database");
      metadata.addDataSource(ds);
      manager.setSQLAppender(manager.new SQLWriterAppender(buf));
      schema.setVersionTable(new Table(schema));
      schema.getVersionTable().setName("VersionTable");
      schema.addTable(schema.getVersionTable());
      upgrade.setMetadata(metadata);

      // setup for first upgrade version
      ExecStep execStep = new ExecStep(); // step lacking match for current adapter
      SQLScript stepScript = new SQLScript();
      SQLStatement stepStmt = new SQLStatement();

      stepStmt.addAdapter(invalid.getName(), ds.getType());
      stepStmt.setSQL("SQLStatement SQL");
      stepScript.addStatement(stepStmt);
      execStep.getScriptHolder().addScript(stepScript);

      // single incompatible step
      metadata.setVersion("1-step");
      upgradeVersion = new RelationalSchemaUpgrade(metadata.getVersion());
      upgradeVersion.addStep(execStep);
      upgradeVersion.setDataSource(ds);
      upgrade.addVersion(upgradeVersion);

      try
      {
         upgrade.validate(null, null); // must fail since an incompatible step "1-step" exists
         fail(); // exception expected
      }
      catch (MetadataException e)
      {
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertEquals("err.meta.sql.statementAdapter", e.getErrorCode());
      }

      try
      {
         manager.upgrade(schema, null); //must fail since no compatible versions found to start with
         fail(); // exception expected
      }
      catch (MetadataException e)
      {
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertEquals("err.meta.sql.statementAdapter", e.getErrorCode());
      }

      try
      {
         manager.upgrade(schema, "1-step"); // must fail since step "1-step" is incompatible
         fail(); // exception expected
      }
      catch (MetadataException e)
      {
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertEquals("err.meta.sql.statementAdapter", e.getErrorCode());
      }

      // setup for second upgrade version
      SupportAdapterStep adapterStep = new SupportAdapterStep();
      CreateTableStep createTableStep = new CreateTableStep(); // step has match for current adapter
      Table table = new Table(schema);
      SQLScript tableScript = new SQLScript();
      SQLStatement tableStmtInvalid = new SQLStatement();
      SQLStatement tableStmtValid = new SQLStatement();

      tableStmtInvalid.addAdapter(invalid.getName(), ds.getType());
      tableStmtInvalid.setSQL("SQLStatement Table SQL Invalid");
      tableStmtValid.addAdapter(ds.getAdapter().getName(), ds.getType());
      tableStmtValid.setSQL("SQLStatement Table SQL Valid");
      tableScript.addStatement(tableStmtValid);
      tableScript.addStatement(tableStmtInvalid);
      tableScript.addStatement(stepStmt);
      table.setName("Table");
      table.setType(Table.VIEW);
      table.setViewScript(tableScript);
      schema.addTable(table);
      createTableStep.setName(table.getName());
      createTableStep.setType(table.getType());
      createTableStep.setViewScript(tableScript);
      adapterStep.setAdapter(invalid);

      // incompatible step -> compatible step
      metadata.setVersion("2-step");
      upgradeVersion = new RelationalSchemaUpgrade(metadata.getVersion());
      upgradeVersion.addStep(adapterStep);
      upgradeVersion.addStep(createTableStep);
      upgradeVersion.setDataSource(ds);
      upgrade.addVersion(upgradeVersion);

      try
      {
         upgrade.validate(null, null); // must fail since an incompatible step "1-step" exists
         fail(); // exception expected
      }
      catch (MetadataException e)
      {
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertEquals("err.meta.sql.statementAdapter", e.getErrorCode());
      }

      try
      {
         manager.upgrade(schema, null); // must fail since an incompatible step "1-step" exists
         fail(); // exception expected
      }
      catch (MetadataException e)
      {
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertEquals("err.meta.sql.statementAdapter", e.getErrorCode());
      }

      buf.getBuffer().setLength(0);
      ds.setAdapter(invalid);
      manager.upgrade(schema, "2-step"); // validation of invalid adapter should not be prevented
      AssertUtil.assertContained(tableStmtInvalid.getSQL(), buf.toString());
      ds.setAdapter(valid);

      try
      {
         manager.upgrade(schema, "2-step"); // must fail since validating current adapter from start
         fail(); // exception expected
      }
      catch (MetadataException e)
      {
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertEquals("err.meta.sql.statementAdapter", e.getErrorCode());
      }

      adapterStep.setAdapter(ds.getAdapter());
      upgrade.validate(null, null); // must pass since adapter validation from "2-step"
      buf.getBuffer().setLength(0);
      manager.upgrade(schema, null); // must upgrade starting from "2-step"
      AssertUtil.assertContained(tableStmtValid.getSQL(), buf.toString());

      // setup for third upgrade version
      CreateColumnStep createColumnStep = new CreateColumnStep(); // step lacking match for adapter
      Column column = new Column("Column", table);
      ColumnOutline outline = new ColumnOutline(column.getName());

      column.setType(Primitive.INTEGER);
      table.addColumn(column);
      outline.setType(column.getType());
      createColumnStep.setOutline(outline);
      createColumnStep.setTableName(table.getName());
      createColumnStep.getScriptHolder().addScript(stepScript);

      // incompatible step -> compatible step -> incompatible step
      metadata.setVersion("3-step");
      upgradeVersion = new RelationalSchemaUpgrade(metadata.getVersion());
      upgradeVersion.addStep(createColumnStep);
      upgradeVersion.setDataSource(ds);
      upgrade.addVersion(upgradeVersion);

      try
      {
         upgrade.validate(null, null);//must fail since an incompatible step exists after compatible
         fail(); // exception expected
      }
         catch (MetadataException e)
      {
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertEquals("err.meta.sql.statementAdapter", e.getErrorCode());
      }

      try
      {
         manager.upgrade(schema, null); //must fail since no compatible versions found to start with
         fail(); // exception expected
      }
         catch (MetadataException e)
      {
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertEquals("err.meta.sql.statementAdapter", e.getErrorCode());
      }

      try
      {
         manager.upgrade(schema, "1-step"); // must fail since step "1-step" is incompatible
         fail(); // exception expected
      }
         catch (MetadataException e)
      {
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertEquals("err.meta.sql.statementAdapter", e.getErrorCode());
      }

      try
      {
         manager.upgrade(schema, "2-step"); // must fail since step "3-step" is incompatible
         fail(); // exception expected
      }
      catch (MetadataException e)
      {
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertTrue(e.getCause() instanceof MetadataException);
         e = (MetadataException)e.getCause();
         assertEquals("err.meta.sql.statementAdapter", e.getErrorCode());
      }
   }

   /**
    * Perform a RDBMS upgrade step.
    * @param step The step to perform.
    * @param schema The modifiable current schema state (null == use empty schema).
    * @param writer The writer to receive the generated SQL (null == use DB connection).
    * @return The new schema state as read from RDBMS.
    */
   protected RelationalSchema upgrade(
      RelationalSchemaUpgradeStep step, RelationalSchema schema, Writer writer)
   {
      if (schema == null)
      {
         RelationalSchema templateSchema = (RelationalSchema)m_database.getSchema();

         schema = new RelationalSchema();
         schema.setDataSource(templateSchema.getDataSource());
         schema.setIndexFill(templateSchema.getIndexFill());
         schema.setIndexspaceName(templateSchema.getIndexspaceName());
         schema.setLongspaceName(templateSchema.getLongspaceName());
         schema.setPrefix(templateSchema.getPrefix());
         schema.setRoleName(templateSchema.getRoleName());
         schema.setTablespaceName(templateSchema.getTablespaceName());

         // hints declared/used by core/test/nexj/base/upgrades/Main.upgrade, need for validation
         RelationalSchemaTest.addHint(schema, "test1");
         RelationalSchemaTest.addHint(schema, "test2");
         RelationalSchemaTest.addHint(schema, "test3");
      }

      RelationalSchemaUpgradeState state = new RelationalSchemaUpgradeState(schema, null, null);
      SchemaVersion version = new SchemaVersion();
      RelationalSchemaUpgrade upgrade = new RelationalSchemaUpgrade();
      SQLAppender origAppender = m_manager.getSQLAppender();

      upgrade.addStep(step);
      upgrade.setUpgrade(new Upgrade(null));
      m_manager.setSQLAppender(
         (writer == null) ? new SQLSchemaManager.SQLConnectionAppender(m_connection.getConnection())
                          : (SQLAppender)m_manager.new SQLWriterAppender(writer));

      try
      {
         m_manager.upgrade(upgrade, state, version);
      }
      finally
      {
         m_manager.setSQLAppender(origAppender);
      }

      return readSchema(schema);
   }

   /**
    * Perform a simulated RDBMS upgrade step with output as return value.
    * @param version The VersionUpgrade to simulate.
    * @param schema The modifiable current schema state (not null and getVersionTable() not null).
    * @return The SQL generated during the execution of the upgrade.
    */
   protected String upgrade(VersionUpgrade version, RelationalSchema schema)
   {
      Upgrade upgrade = new Upgrade(null);
      SQLAppender origAppender = m_manager.getSQLAppender();
      StringWriter sql = new StringWriter();

      upgrade.setMetadata(getMetadata());
      upgrade.addVersion(version);
      m_manager.setSQLAppender(m_manager.new SQLWriterAppender(sql));

      try
      {
         m_manager.upgrade(schema, upgrade, upgrade.getFirstVersion().getName());
      }
      finally
      {
         m_manager.setSQLAppender(origAppender);
      }

      return sql.getBuffer().toString();
   }
}
TOP

Related Classes of nexj.core.persistence.sql.SQLSchemaManagerTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.