浏览代码

AMBARI-7690. Change alterColumn statement for Derby DB. (mapirkovskyy)

Myroslav Papirkovskyy 10 年之前
父节点
当前提交
54a3de777f

+ 22 - 0
ambari-server/src/main/java/org/apache/ambari/server/orm/DBAccessor.java

@@ -151,6 +151,18 @@ public interface DBAccessor {
   public int updateTable(String tableName, String columnName, Object value,
                          String whereClause) throws SQLException;
 
+  /**
+   * Simple update operation on table
+   *
+   * @param tableName
+   * @param columnNameSrc
+   * @param columnNameTgt
+   * @return
+   * @throws SQLException
+   */
+  public void updateTable(String tableName, DBColumnInfo columnNameSrc,
+                         DBColumnInfo columnNameTgt) throws SQLException;
+
   /**
    * Helper method to run third party scripts like Quartz DDL
    * @param filePath
@@ -174,6 +186,16 @@ public interface DBAccessor {
    */
   ResultSet executeSelect(String query) throws SQLException;
 
+  /**
+   * Execute select query
+   * @param query
+   * @param resultSetType
+   * @param resultSetConcur
+   * @return
+   * @throws SQLException
+   */
+  ResultSet executeSelect(String query, int resultSetType, int resultSetConcur) throws SQLException;
+
   /**
    * Execute query on DB
    * @param query

+ 66 - 5
ambari-server/src/main/java/org/apache/ambari/server/orm/DBAccessorImpl.java

@@ -40,9 +40,12 @@ import org.eclipse.persistence.sessions.DatabaseLogin;
 import org.eclipse.persistence.sessions.DatabaseSession;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+ 
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.sql.Blob;
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
 import java.sql.DriverManager;
@@ -50,6 +53,7 @@ import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.sql.Types;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -386,11 +390,61 @@ public class DBAccessorImpl implements DBAccessor {
   }
 
   @Override
-  public void alterColumn(String tableName, DBColumnInfo columnInfo) throws SQLException {
+  public void alterColumn(String tableName, DBColumnInfo columnInfo)
+      throws SQLException {
     //varchar extension only (derby limitation, but not too much for others),
-    //use addColumn-update-drop-rename for more
-    String statement = dbmsHelper.getAlterColumnStatement(tableName, columnInfo);
-    executeQuery(statement);
+    if (dbmsHelper.supportsColumnTypeChange()) {
+      String statement = dbmsHelper.getAlterColumnStatement(tableName,
+          columnInfo);
+      executeQuery(statement);
+    } else {
+      //use addColumn: add_tmp-update-drop-rename for Derby
+      DBColumnInfo columnInfoTmp = new DBColumnInfo(
+          columnInfo.getName() + "_TMP",
+          columnInfo.getType(),
+          columnInfo.getLength());
+      String statement = dbmsHelper.getAddColumnStatement(tableName, columnInfoTmp);
+      executeQuery(statement);
+      updateTable(tableName, columnInfo, columnInfoTmp);
+      dropColumn(tableName, columnInfo.getName());
+      renameColumn(tableName, columnInfoTmp.getName(), columnInfo);
+    }
+  }
+
+  @Override
+  public void updateTable(String tableName, DBColumnInfo columnNameFrom,
+      DBColumnInfo columnNameTo) throws SQLException {
+    LOG.info("Executing query: UPDATE TABLE " + tableName + " SET "
+        + columnNameTo.getName() + "=" + columnNameFrom.getName());
+
+    String statement = "SELECT * FROM " + tableName;
+    int typeFrom = getColumnType(tableName, columnNameFrom.getName());
+    int typeTo = getColumnType(tableName, columnNameTo.getName());
+    ResultSet rs = executeSelect(statement, ResultSet.TYPE_SCROLL_SENSITIVE,
+        ResultSet.CONCUR_UPDATABLE);
+
+    while (rs.next()) {
+      convertUpdateData(rs, columnNameFrom, typeFrom, columnNameTo, typeTo);
+      rs.updateRow();
+    }
+    rs.close();
+  }
+
+  private void convertUpdateData(ResultSet rs, DBColumnInfo columnNameFrom,
+      int typeFrom,
+      DBColumnInfo columnNameTo, int typeTo) throws SQLException {
+    if (typeFrom == Types.BLOB && typeTo == Types.CLOB) {
+      //BLOB-->CLOB
+      Blob data = rs.getBlob(columnNameFrom.getName());
+      if (data != null) {
+        rs.updateClob(columnNameTo.getName(),
+            new BufferedReader(new InputStreamReader(data.getBinaryStream())));
+      }
+    } else {
+      Object data = rs.getObject(columnNameFrom.getName());
+      rs.updateObject(columnNameTo.getName(), data);
+    }
+
   }
 
   @Override
@@ -494,6 +548,11 @@ public class DBAccessorImpl implements DBAccessor {
   }
 
   @Override
+  public ResultSet executeSelect(String query, int resultSetType, int resultSetConcur) throws SQLException {
+    Statement statement = getConnection().createStatement(resultSetType, resultSetConcur);
+    return statement.executeQuery(query);
+  }
+
   public void truncateTable(String tableName) throws SQLException {
     String query = "DELETE FROM " + tableName;
     executeQuery(query);
@@ -501,7 +560,9 @@ public class DBAccessorImpl implements DBAccessor {
 
   @Override
   public void dropColumn(String tableName, String columnName) throws SQLException {
-    throw new UnsupportedOperationException("Drop column not supported");
+    if (tableHasColumn(tableName, columnName)) {
+      executeQuery("ALTER TABLE " + tableName + " DROP COLUMN " + columnName);
+    }
   }
 
   @Override

+ 97 - 0
ambari-server/src/test/java/org/apache/ambari/server/orm/DBAccessorImplTest.java

@@ -44,6 +44,9 @@ import org.junit.rules.ExpectedException;
 
 import com.google.inject.Guice;
 import com.google.inject.Injector;
+import java.io.ByteArrayInputStream;
+import java.sql.Clob;
+import java.sql.PreparedStatement;
 
 public class DBAccessorImplTest {
   private Injector injector;
@@ -83,6 +86,100 @@ public class DBAccessorImplTest {
     assertEquals(DBAccessor.DbType.DERBY, dbAccessor.getDbType());
   }
 
+  @Test
+  public void testAlterColumn() throws Exception {
+    String tableName = getFreeTableName();
+    createMyTable(tableName);
+    DBAccessorImpl dbAccessor = injector.getInstance(DBAccessorImpl.class);
+
+    ResultSet rs;
+    DBColumnInfo fromColumn;
+    DBColumnInfo toColumn;
+    Statement statement = dbAccessor.getConnection().createStatement();
+    final String dataString = "Data for inserting column.";
+
+    // 1 - VARACHAR --> VARCHAR
+    toColumn = new DBColumnInfo("name", String.class, 500, null, true);
+    statement.execute(
+        String.format("INSERT INTO %s(id, name) VALUES (1, '%s')", tableName,
+            dataString));
+
+    dbAccessor.alterColumn(tableName, toColumn);
+    rs = statement.executeQuery(
+        String.format("SELECT name FROM %s", tableName));
+    while (rs.next()) {
+      ResultSetMetaData rsm = rs.getMetaData();
+      assertEquals(rs.getString(toColumn.getName()), dataString);
+      assertEquals(rsm.getColumnTypeName(1), "VARCHAR");
+      assertEquals(rsm.getColumnDisplaySize(1), 500);
+    }
+    rs.close();
+
+    // 2 - VARACHAR --> CLOB
+    toColumn = new DBColumnInfo("name", char[].class, 999, null, true);
+    dbAccessor.alterColumn(tableName, toColumn);
+    rs = statement.executeQuery(
+        String.format("SELECT name FROM %s", tableName));
+    while (rs.next()) {
+      ResultSetMetaData rsm = rs.getMetaData();
+      Clob clob = rs.getClob(toColumn.getName());
+      assertEquals(clob.getSubString(1, (int) clob.length()), dataString);
+      assertEquals(rsm.getColumnTypeName(1), "CLOB");
+      assertEquals(rsm.getColumnDisplaySize(1), 999);
+    }
+    rs.close();
+
+    // 3 - BLOB --> CLOB
+    toColumn = new DBColumnInfo("name_blob_to_clob", char[].class, 567, null,
+        true);
+    fromColumn = new DBColumnInfo("name_blob_to_clob", byte[].class, 20000,
+        null, true);
+    dbAccessor.addColumn(tableName, fromColumn);
+
+    String sql = String.format(
+        "insert into %s(id, name_blob_to_clob) values (2, ?)", tableName);
+    PreparedStatement preparedStatement = dbAccessor.getConnection().prepareStatement(
+        sql);
+    preparedStatement.setBinaryStream(1,
+        new ByteArrayInputStream(dataString.getBytes()),
+        dataString.getBytes().length);
+    preparedStatement.executeUpdate();
+    preparedStatement.close();
+
+    dbAccessor.alterColumn(tableName, toColumn);
+    rs = statement.executeQuery(
+        String.format("SELECT name_blob_to_clob FROM %s WHERE id=2",
+            tableName));
+    while (rs.next()) {
+      ResultSetMetaData rsm = rs.getMetaData();
+      Clob clob = rs.getClob(toColumn.getName());
+      assertEquals(clob.getSubString(1, (int) clob.length()), dataString);
+      assertEquals(rsm.getColumnTypeName(1), "CLOB");
+      assertEquals(rsm.getColumnDisplaySize(1), 567);
+    }
+    rs.close();
+
+    // 4 - CLOB --> CLOB
+    toColumn = new DBColumnInfo("name_blob_to_clob", char[].class, 1500, null,
+        true);
+    dbAccessor.alterColumn(tableName, toColumn);
+    rs = statement.executeQuery(
+        String.format("SELECT name_blob_to_clob FROM %s WHERE id=2",
+            tableName));
+    while (rs.next()) {
+      ResultSetMetaData rsm = rs.getMetaData();
+      Clob clob = rs.getClob(toColumn.getName());
+      assertEquals(clob.getSubString(1, (int) clob.length()), dataString);
+      assertEquals(rsm.getColumnTypeName(1), "CLOB");
+      assertEquals(rsm.getColumnDisplaySize(1), 1500);
+    }
+    rs.close();
+
+    dbAccessor.dropTable(tableName);
+
+  }
+
+
   @Test
   public void testCreateTable() throws Exception {
     String tableName = getFreeTableName();