Add support for renaming an existing column

Change-Id: I8faea89aa7c3b8a239242dd5ae6f22515ed10775
Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java b/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java
index 007d5f4..04640fb 100644
--- a/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java
+++ b/src/main/java/com/google/gwtorm/jdbc/JdbcSchema.java
@@ -133,6 +133,38 @@
     }
   }
 
+  public void renameField(String table, String from, String to)
+      throws OrmException {
+    final RelationModel rel = findRelationModel(table);
+    if (rel == null) {
+      throw new OrmException("Relation " + table + " not defined");
+    }
+    final ColumnModel col = rel.getField(to);
+    if (col == null) {
+      throw new OrmException("Relation " + table + " does not have " + to);
+    }
+    try {
+      final Statement s = getConnection().createStatement();
+      try {
+        getDialect().renameColumn(s, table, from, col);
+      } finally {
+        s.close();
+      }
+    } catch (SQLException e) {
+      throw new OrmException("Cannot rename " + table + "." + from + " to "
+          + to, e);
+    }
+  }
+
+  private RelationModel findRelationModel(String table) throws OrmException {
+    for (final RelationModel rel : dbDef.getSchemaModel().getRelations()) {
+      if (table.equalsIgnoreCase(rel.getRelationName())) {
+        return rel;
+      }
+    }
+    return null;
+  }
+
   public void pruneSchema() throws OrmException {
     try {
       pruneSequences();
diff --git a/src/main/java/com/google/gwtorm/schema/sql/DialectH2.java b/src/main/java/com/google/gwtorm/schema/sql/DialectH2.java
index 8b79dd3..d2ec512 100644
--- a/src/main/java/com/google/gwtorm/schema/sql/DialectH2.java
+++ b/src/main/java/com/google/gwtorm/schema/sql/DialectH2.java
@@ -91,4 +91,17 @@
       stmt.execute(r.toString());
     }
   }
+
+  @Override
+  public void renameColumn(Statement stmt, String tableName, String fromColumn,
+      ColumnModel col) throws SQLException {
+    final StringBuilder r = new StringBuilder();
+    r.append("ALTER TABLE ");
+    r.append(tableName);
+    r.append(" ALTER COLUMN ");
+    r.append(fromColumn);
+    r.append(" RENAME TO ");
+    r.append(col.getColumnName());
+    stmt.execute(r.toString());
+  }
 }
diff --git a/src/main/java/com/google/gwtorm/schema/sql/DialectMySQL.java b/src/main/java/com/google/gwtorm/schema/sql/DialectMySQL.java
index 047e2fd..338c485 100644
--- a/src/main/java/com/google/gwtorm/schema/sql/DialectMySQL.java
+++ b/src/main/java/com/google/gwtorm/schema/sql/DialectMySQL.java
@@ -169,4 +169,19 @@
       rs.close();
     }
   }
+
+  @Override
+  public void renameColumn(Statement stmt, String tableName, String fromColumn,
+      ColumnModel col) throws SQLException {
+    StringBuffer r = new StringBuffer();
+    r.append("ALTER TABLE ");
+    r.append(tableName);
+    r.append(" CHANGE ");
+    r.append(fromColumn);
+    r.append(" ");
+    r.append(col.getColumnName());
+    r.append(" ");
+    r.append(getSqlTypeInfo(col).getSqlType(col, this));
+    stmt.execute(r.toString());
+  }
 }
diff --git a/src/main/java/com/google/gwtorm/schema/sql/DialectPostgreSQL.java b/src/main/java/com/google/gwtorm/schema/sql/DialectPostgreSQL.java
index bffc7ad..50cc2d3 100644
--- a/src/main/java/com/google/gwtorm/schema/sql/DialectPostgreSQL.java
+++ b/src/main/java/com/google/gwtorm/schema/sql/DialectPostgreSQL.java
@@ -16,6 +16,7 @@
 
 import com.google.gwtorm.client.OrmDuplicateKeyException;
 import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.schema.ColumnModel;
 import com.google.gwtorm.schema.RelationModel;
 
 import java.sql.Connection;
@@ -71,6 +72,19 @@
   }
 
   @Override
+  public void renameColumn(Statement stmt, String tableName, String fromColumn,
+      ColumnModel col) throws SQLException {
+    final StringBuilder r = new StringBuilder();
+    r.append("ALTER TABLE ");
+    r.append(tableName);
+    r.append(" RENAME COLUMN ");
+    r.append(fromColumn);
+    r.append(" TO ");
+    r.append(col.getColumnName());
+    stmt.execute(r.toString());
+  }
+
+  @Override
   public Set<String> listSequences(Connection db) throws SQLException {
     Statement s = db.createStatement();
     try {
diff --git a/src/main/java/com/google/gwtorm/schema/sql/SqlDialect.java b/src/main/java/com/google/gwtorm/schema/sql/SqlDialect.java
index 540b334..c05992e 100644
--- a/src/main/java/com/google/gwtorm/schema/sql/SqlDialect.java
+++ b/src/main/java/com/google/gwtorm/schema/sql/SqlDialect.java
@@ -284,5 +284,17 @@
     stmt.execute(r.toString());
   }
 
+  /**
+   * Rename an existing column in a table.
+   *
+   * @param stmt statement to use to execute the SQL command(s).
+   * @param tableName table to add the column onto.
+   * @param fromColumn source column name
+   * @param col destination column definition
+   * @throws SQLException the column could not be renamed.
+   */
+  public abstract void renameColumn(Statement stmt, String tableName,
+      String fromColumn, ColumnModel col) throws SQLException;
+
   public abstract String getNextSequenceValueSql(String seqname);
 }
diff --git a/src/test/java/com/google/gwtorm/data/PersonAccess2.java b/src/test/java/com/google/gwtorm/data/PersonAccess2.java
new file mode 100644
index 0000000..790ebbf
--- /dev/null
+++ b/src/test/java/com/google/gwtorm/data/PersonAccess2.java
@@ -0,0 +1,24 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.data;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+
+public interface PersonAccess2 extends Access<TestPerson2, TestPerson.Key> {
+  @PrimaryKey("name")
+  TestPerson2 get(TestPerson.Key key) throws OrmException;
+}
diff --git a/src/test/java/com/google/gwtorm/data/PhoneBookDb2.java b/src/test/java/com/google/gwtorm/data/PhoneBookDb2.java
new file mode 100644
index 0000000..908dc92
--- /dev/null
+++ b/src/test/java/com/google/gwtorm/data/PhoneBookDb2.java
@@ -0,0 +1,23 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.data;
+
+import com.google.gwtorm.client.Relation;
+import com.google.gwtorm.client.Schema;
+
+public interface PhoneBookDb2 extends Schema {
+  @Relation
+  PersonAccess2 people();
+}
diff --git a/src/test/java/com/google/gwtorm/data/TestPerson2.java b/src/test/java/com/google/gwtorm/data/TestPerson2.java
new file mode 100644
index 0000000..31628d4
--- /dev/null
+++ b/src/test/java/com/google/gwtorm/data/TestPerson2.java
@@ -0,0 +1,29 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.data;
+
+import com.google.gwtorm.client.Column;
+
+
+public class TestPerson2 {
+  @Column
+  protected TestPerson.Key name;
+
+  @Column
+  protected int age;
+
+  @Column
+  protected boolean isRegistered;
+}
diff --git a/src/test/java/com/google/gwtorm/schema/sql/DialectH2Test.java b/src/test/java/com/google/gwtorm/schema/sql/DialectH2Test.java
index d79bfcb..5e1c552 100644
--- a/src/test/java/com/google/gwtorm/schema/sql/DialectH2Test.java
+++ b/src/test/java/com/google/gwtorm/schema/sql/DialectH2Test.java
@@ -16,9 +16,11 @@
 
 import com.google.gwtorm.client.OrmException;
 import com.google.gwtorm.data.PhoneBookDb;
+import com.google.gwtorm.data.PhoneBookDb2;
 import com.google.gwtorm.data.TestAddress;
 import com.google.gwtorm.data.TestPerson;
 import com.google.gwtorm.jdbc.Database;
+import com.google.gwtorm.jdbc.JdbcSchema;
 import com.google.gwtorm.jdbc.SimpleDataSource;
 
 import junit.framework.TestCase;
@@ -35,6 +37,7 @@
   private Connection db;
   private SqlDialect dialect;
   private Database<PhoneBookDb> phoneBook;
+  private Database<PhoneBookDb2> phoneBook2;
 
   @Override
   protected void setUp() throws Exception {
@@ -48,6 +51,8 @@
     p.setProperty("url", db.getMetaData().getURL());
     phoneBook =
         new Database<PhoneBookDb>(new SimpleDataSource(p), PhoneBookDb.class);
+    phoneBook2 =
+        new Database<PhoneBookDb2>(new SimpleDataSource(p), PhoneBookDb2.class);
   }
 
   @Override
@@ -135,5 +140,12 @@
     } finally {
       p.close();
     }
+
+    final PhoneBookDb2 p2 = phoneBook2.open();
+    try {
+      ((JdbcSchema)p2).renameField("people", "registered", "isRegistered");
+    } finally {
+      p2.close();
+    }
   }
 }
diff --git a/src/test/java/com/google/gwtorm/schema/sql/DialectMySQLTest.java b/src/test/java/com/google/gwtorm/schema/sql/DialectMySQLTest.java
index 22a8bd3..f452455 100644
--- a/src/test/java/com/google/gwtorm/schema/sql/DialectMySQLTest.java
+++ b/src/test/java/com/google/gwtorm/schema/sql/DialectMySQLTest.java
@@ -17,9 +17,11 @@
 
 import com.google.gwtorm.client.OrmException;
 import com.google.gwtorm.data.PhoneBookDb;
+import com.google.gwtorm.data.PhoneBookDb2;
 import com.google.gwtorm.data.TestAddress;
 import com.google.gwtorm.data.TestPerson;
 import com.google.gwtorm.jdbc.Database;
+import com.google.gwtorm.jdbc.JdbcSchema;
 import com.google.gwtorm.jdbc.SimpleDataSource;
 
 import junit.framework.TestCase;
@@ -36,6 +38,7 @@
   private Connection db;
   private SqlDialect dialect;
   private Database<PhoneBookDb> phoneBook;
+  private Database<PhoneBookDb2> phoneBook2;
 
   @Override
   protected void setUp() throws Exception {
@@ -58,6 +61,8 @@
     p.setProperty("password", pass);
     phoneBook =
         new Database<PhoneBookDb>(new SimpleDataSource(p), PhoneBookDb.class);
+    phoneBook2 =
+      new Database<PhoneBookDb2>(new SimpleDataSource(p), PhoneBookDb2.class);
 
     drop("TABLE address_id");
     drop("TABLE addresses");
@@ -158,5 +163,12 @@
     } finally {
       p.close();
     }
+
+    final PhoneBookDb2 p2 = phoneBook2.open();
+    try {
+      ((JdbcSchema)p2).renameField("people", "registered", "isRegistered");
+    } finally {
+      p2.close();
+    }
   }
 }
diff --git a/src/test/java/com/google/gwtorm/schema/sql/DialectPostgreSQLTest.java b/src/test/java/com/google/gwtorm/schema/sql/DialectPostgreSQLTest.java
index fe6440a..df43316 100644
--- a/src/test/java/com/google/gwtorm/schema/sql/DialectPostgreSQLTest.java
+++ b/src/test/java/com/google/gwtorm/schema/sql/DialectPostgreSQLTest.java
@@ -17,9 +17,11 @@
 
 import com.google.gwtorm.client.OrmException;
 import com.google.gwtorm.data.PhoneBookDb;
+import com.google.gwtorm.data.PhoneBookDb2;
 import com.google.gwtorm.data.TestAddress;
 import com.google.gwtorm.data.TestPerson;
 import com.google.gwtorm.jdbc.Database;
+import com.google.gwtorm.jdbc.JdbcSchema;
 import com.google.gwtorm.jdbc.SimpleDataSource;
 
 import junit.framework.TestCase;
@@ -36,6 +38,7 @@
   private Connection db;
   private SqlDialect dialect;
   private Database<PhoneBookDb> phoneBook;
+  private Database<PhoneBookDb2> phoneBook2;
 
   @Override
   protected void setUp() throws Exception {
@@ -56,6 +59,8 @@
     p.setProperty("password", pass);
     phoneBook =
         new Database<PhoneBookDb>(new SimpleDataSource(p), PhoneBookDb.class);
+    phoneBook2 =
+      new Database<PhoneBookDb2>(new SimpleDataSource(p), PhoneBookDb2.class);
 
     drop("SEQUENCE address_id");
     drop("SEQUENCE cnt");
@@ -157,5 +162,12 @@
     } finally {
       p.close();
     }
+
+    final PhoneBookDb2 p2 = phoneBook2.open();
+    try {
+      ((JdbcSchema)p2).renameField("people", "registered", "isRegistered");
+    } finally {
+      p2.close();
+    }
   }
 }