List:Commits« Previous MessageNext Message »
From:Craig L Russell Date:October 27 2011 11:49pm
Subject:bzr push into mysql-5.1-telco-7.1 branch (Craig.Russell:4316 to 4317)
View as plain text  
 4317 Craig L Russell	2011-10-27
      Add a new method to clusterj Session to unload cached schema information
      after the schema has changed (due to alter table). This allows for recovery
      from error code 284 without restarting the VM. If there is an error while
      creating the domain type handler also unload the schema.
      A test case changes schema for a table and verifies recovery of the schema.
      This change was requested by a community customer using clusterj with Tomcat.

    added:
      storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/SchemaChangeTest.java
      storage/ndb/clusterj/clusterj-tie/src/test/java/testsuite/clusterj/tie/SchemaChangeTest.java
    modified:
      storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/Session.java
      storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/SessionFactoryImpl.java
      storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/SessionImpl.java
      storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/DomainTypeHandlerFactoryImpl.java
      storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/DomainTypeHandlerImpl.java
      storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/store/Dictionary.java
      storage/ndb/clusterj/clusterj-core/src/main/resources/com/mysql/clusterj/core/Bundle.properties
      storage/ndb/clusterj/clusterj-test/Makefile.am
      storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/DictionaryImpl.java
 4316 Jonas Oreland	2011-10-24 [merge]
      ndb - merge 71 to 72

    modified:
      storage/ndb/include/util/OutputStream.hpp
      storage/ndb/src/common/util/OutputStream.cpp
      storage/ndb/src/mgmapi/mgmapi.cpp
      storage/ndb/src/mgmsrv/Services.cpp
      storage/ndb/test/include/NdbMgmd.hpp
      storage/ndb/test/ndbapi/testMgm.cpp
=== modified file 'storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/Session.java'
--- a/storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/Session.java	2011-01-31 09:07:01 +0000
+++ b/storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/Session.java	2011-10-27 23:43:25 +0000
@@ -229,4 +229,12 @@ public interface Session {
      */
     void markModified(Object instance, String fieldName);
 
+    /** Unload the schema definition for a class. This must be done after the schema
+     * definition has changed in the database due to an alter table command.
+     * The next time the class is used the schema will be reloaded.
+     * @param cls the class for which the schema is unloaded
+     * @return the name of the schema that was unloaded
+     */
+    String unloadSchema(Class<?> cls);
+
 }

=== modified file 'storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/SessionFactoryImpl.java'
--- a/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/SessionFactoryImpl.java	2011-08-03 01:00:56 +0000
+++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/SessionFactoryImpl.java	2011-10-27 23:43:25 +0000
@@ -489,4 +489,19 @@ public class SessionFactoryImpl implemen
         return result;
     }
 
+    public String unloadSchema(Class<?> cls, Dictionary dictionary) {
+        synchronized(typeToHandlerMap) {
+            String tableName = null;
+            DomainTypeHandler<?> domainTypeHandler = typeToHandlerMap.remove(cls);
+            if (domainTypeHandler != null) {
+                // remove the ndb dictionary cached table definition
+                tableName = domainTypeHandler.getTableName();
+                if (tableName != null) {
+                    dictionary.removeCachedTable(tableName);
+                }
+            }
+            return tableName;
+        }
+    }
+
 }

=== modified file 'storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/SessionImpl.java'
--- a/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/SessionImpl.java	2011-10-22 00:40:34 +0000
+++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/SessionImpl.java	2011-10-27 23:43:25 +0000
@@ -1384,4 +1384,8 @@ public class SessionImpl implements Sess
         }
     }
 
+    public String unloadSchema(Class<?> cls) {
+        return factory.unloadSchema(cls, dictionary);
+    }
+
 }

=== modified file 'storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/DomainTypeHandlerFactoryImpl.java'
--- a/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/DomainTypeHandlerFactoryImpl.java	2011-02-02 09:52:33 +0000
+++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/DomainTypeHandlerFactoryImpl.java	2011-10-27 23:43:25 +0000
@@ -1,5 +1,5 @@
 /*
-   Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+   Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -53,7 +53,7 @@ public class DomainTypeHandlerFactoryImp
     }
 
     public <T> DomainTypeHandler<T> createDomainTypeHandler(Class<T> domainClass, Dictionary dictionary) {
-        DomainTypeHandler<T> handler;
+        DomainTypeHandler<T> handler = null;
         StringBuffer errorMessages = new StringBuffer();
         for (DomainTypeHandlerFactory factory: domainTypeHandlerFactories) {
             try {
@@ -82,6 +82,15 @@ public class DomainTypeHandlerFactoryImp
         } catch (Exception e) {
             errorMessages.append(e.toString());
             throw new ClusterJUserException(errorMessages.toString(), e);
+        } finally {
+            // if handler is null, there may be a problem with the schema, so remove it from the local dictionary
+            if (handler == null) {
+                String tableName = DomainTypeHandlerImpl.getTableName(domainClass);
+                if (tableName != null) {
+                    logger.info(local.message("MSG_Removing_Schema", tableName, domainClass.getName()));
+                    dictionary.removeCachedTable(tableName);                    
+                }
+            }
         }
     }
 

=== modified file 'storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/DomainTypeHandlerImpl.java'
--- a/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/DomainTypeHandlerImpl.java	2011-02-02 09:52:33 +0000
+++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/DomainTypeHandlerImpl.java	2011-10-27 23:43:25 +0000
@@ -244,7 +244,29 @@ public class DomainTypeHandlerImpl<T> ex
         }
     }
 
-    protected <O extends DynamicObject> String getTableNameForDynamicObject(Class<O> cls) {
+    /** Get the table name mapped to the domain class.
+     * @param cls the domain class
+     * @return the table name for the domain class
+     */
+    protected static String getTableName(Class<?> cls) {
+        String tableName = null;
+        if (DynamicObject.class.isAssignableFrom(cls)) {
+            tableName = getTableNameForDynamicObject((Class<DynamicObject>)cls);
+        } else {
+            PersistenceCapable persistenceCapable = cls.getAnnotation(PersistenceCapable.class);
+            if (persistenceCapable != null) {
+                tableName = persistenceCapable.table();            
+            }
+        }
+        return tableName;
+    }
+
+    /** Get the table name for a dynamic object. The table name is available either from
+     * the PersistenceCapable annotation or via the table() method.
+     * @param cls the dynamic object class
+     * @return the table name for the dynamic object class
+     */
+    protected static <O extends DynamicObject> String getTableNameForDynamicObject(Class<O> cls) {
         DynamicObject dynamicObject;
         PersistenceCapable persistenceCapable = cls.getAnnotation(PersistenceCapable.class);
         String tableName = null;

=== modified file 'storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/store/Dictionary.java'
--- a/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/store/Dictionary.java	2011-06-30 16:04:23 +0000
+++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/store/Dictionary.java	2011-10-27 23:43:25 +0000
@@ -1,6 +1,5 @@
 /*
-   Copyright (c) 2010 Sun Microsystems, Inc.
-   Use is subject to license terms.
+   Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -27,4 +26,6 @@ public interface Dictionary {
 
     public Table getTable(String tableName);
 
+    public void removeCachedTable(String tableName);
+
 }

=== modified file 'storage/ndb/clusterj/clusterj-core/src/main/resources/com/mysql/clusterj/core/Bundle.properties'
--- a/storage/ndb/clusterj/clusterj-core/src/main/resources/com/mysql/clusterj/core/Bundle.properties	2011-08-29 08:17:26 +0000
+++ b/storage/ndb/clusterj/clusterj-core/src/main/resources/com/mysql/clusterj/core/Bundle.properties	2011-10-27 23:43:25 +0000
@@ -133,3 +133,4 @@ ERR_Wrong_Parameter_Type_For_In:For fiel
 either an array of Object types or a List.
 ERR_Parameter_Too_Big_For_In:For field ''{0}'', the parameter of length {1} for query operator ''in'' \
 is too big; it must contain fewer than 4097 items.
+MSG_Removing_Schema:Removing schema {0} after failure to initialize domain type handler for class {1}.

=== modified file 'storage/ndb/clusterj/clusterj-test/Makefile.am'
--- a/storage/ndb/clusterj/clusterj-test/Makefile.am	2011-06-20 23:34:36 +0000
+++ b/storage/ndb/clusterj/clusterj-test/Makefile.am	2011-10-27 23:43:25 +0000
@@ -107,6 +107,7 @@ clusterj_test_java = \
   $(clusterj_test_src)/testsuite/clusterj/QueryUniqueKeyTest.java \
   $(clusterj_test_src)/testsuite/clusterj/QueryYearTypesTest.java \
   $(clusterj_test_src)/testsuite/clusterj/SaveTest.java \
+  $(clusterj_test_src)/testsuite/clusterj/SchemaChangeTest.java \
   $(clusterj_test_src)/testsuite/clusterj/SerialTransactionsTest.java \
   $(clusterj_test_src)/testsuite/clusterj/TimeAsSqlTimeTypesTest.java \
   $(clusterj_test_src)/testsuite/clusterj/TimeAsUtilDateTypesTest.java \

=== added file 'storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/SchemaChangeTest.java'
--- a/storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/SchemaChangeTest.java	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/SchemaChangeTest.java	2011-10-27 23:43:25 +0000
@@ -0,0 +1,119 @@
+/*
+   Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+
+   This program 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; version 2 of the License.
+
+   This program 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, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+*/
+
+package testsuite.clusterj;
+
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import com.mysql.clusterj.ClusterJDatastoreException;
+import com.mysql.clusterj.ClusterJUserException;
+import com.mysql.clusterj.ColumnMetadata;
+import com.mysql.clusterj.DynamicObject;
+import com.mysql.clusterj.annotation.PersistenceCapable;
+
+import testsuite.clusterj.model.StringTypes;
+
+public class SchemaChangeTest extends AbstractClusterJModelTest {
+
+    private static final String modifyTableStatement = 
+        "alter table stringtypes drop column string_not_null_none";
+
+    private static final String restoreTableStatement = 
+        "alter table stringtypes add string_not_null_none varchar(20) DEFAULT NULL";
+
+    @Override
+    public void localSetUp() {
+        createSessionFactory();
+        session = sessionFactory.getSession();
+        // create will cache the schema
+        session.deletePersistentAll(StringTypes.class);
+        session.makePersistent(session.newInstance(StringTypes.class, 0));
+        addTearDownClasses(StringTypes.class);
+    }
+
+    public void testFind() {
+        // change the schema (drop a column)
+        modifyTableDefinition();
+        try {
+            // find the row (with a different schema) which will fail
+            session.find(StringTypes.class, 0);
+        } catch (ClusterJDatastoreException dex) {
+            // make sure it's the right exception
+            if (!dex.getMessage().contains("code 284")) {
+                error("ClusterJDatastoreException must contain code 284 but contains only " + dex.getMessage());
+            }
+            // unload the schema for StringTypes which also clears the cached dictionary table
+            String tableName = session.unloadSchema(StringTypes.class);
+            // make sure we unloaded the right table
+            errorIfNotEqual("Table name mismatch", "stringtypes", tableName);
+            // it should work with a different schema that doesn't include the dropped column
+            StringTypes2 zero = session.find(StringTypes2.class, 0);
+            // verify that column string_not_null_none does not exist
+            ColumnMetadata[] metadatas = zero.columnMetadata();
+            for (ColumnMetadata metadata: metadatas) {
+                if ("string_not_null_none".equals(metadata.name())) {
+                    error("Column string_not_null_none should not exist after schema change.");
+                }
+            }
+            try {
+                // find the row (with a different schema) which will fail with a user exception
+                session.find(StringTypes.class, 0);
+                error("Unexpected success using StringTypes class without column string_not_null_none defined");
+            } catch (ClusterJUserException uex) {
+                // StringTypes can't be loaded because of the missing column, but
+                // the cached dictionary table was removed when the domain type handler couldn't be created
+                restoreTableDefinition();
+                // after restoreTableDefinition, string_not_null_none is defined again
+                // find the row (with a different schema) which will now work
+                session.find(StringTypes.class, 0);
+            }
+        }
+        failOnError();
+    }
+
+    private void modifyTableDefinition() {
+        try {
+            boolean autoCommit = connection.getAutoCommit();
+            connection.setAutoCommit(true);
+            Statement statement = connection.createStatement();
+            statement.execute(modifyTableStatement);
+            connection.setAutoCommit(autoCommit);
+        } catch (SQLException e) {
+            error("Caught SQLException from modifyTableDefinition: ", e);
+        }
+    }
+
+    private void restoreTableDefinition() {
+        try {
+            boolean autoCommit = connection.getAutoCommit();
+            connection.setAutoCommit(true);
+            Statement statement = connection.createStatement();
+            statement.execute(restoreTableStatement);
+            connection.setAutoCommit(autoCommit);
+        } catch (SQLException e) {
+            error("Caught SQLException from restoreTableDefinition: ", e);
+        }
+    }
+
+    /** StringTypes dynamic class to map stringtypes after column string_not_null_none is removed.
+     */
+    @PersistenceCapable(table="stringtypes")
+    public static class StringTypes2 extends DynamicObject {
+        public StringTypes2() {}
+    }
+}

=== modified file 'storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/DictionaryImpl.java'
--- a/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/DictionaryImpl.java	2011-02-02 09:52:33 +0000
+++ b/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/DictionaryImpl.java	2011-10-27 23:43:25 +0000
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ *  Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -17,11 +17,12 @@
 
 package com.mysql.clusterj.tie;
 
+import com.mysql.ndbjtie.ndbapi.NdbDictionary.Dictionary;
 import com.mysql.ndbjtie.ndbapi.NdbDictionary.DictionaryConst;
-import com.mysql.ndbjtie.ndbapi.NdbDictionary.IndexConst;
-import com.mysql.ndbjtie.ndbapi.NdbDictionary.TableConst;
 import com.mysql.ndbjtie.ndbapi.NdbDictionary.DictionaryConst.ListConst.Element;
 import com.mysql.ndbjtie.ndbapi.NdbDictionary.DictionaryConst.ListConst.ElementArray;
+import com.mysql.ndbjtie.ndbapi.NdbDictionary.IndexConst;
+import com.mysql.ndbjtie.ndbapi.NdbDictionary.TableConst;
 
 import com.mysql.clusterj.core.store.Index;
 import com.mysql.clusterj.core.store.Table;
@@ -43,9 +44,9 @@ class DictionaryImpl implements com.mysq
     static final Logger logger = LoggerFactoryService.getFactory()
             .getInstance(DictionaryImpl.class);
 
-    private DictionaryConst ndbDictionary;
+    private Dictionary ndbDictionary;
 
-    public DictionaryImpl(DictionaryConst ndbDictionary) {
+    public DictionaryImpl(Dictionary ndbDictionary) {
         this.ndbDictionary = ndbDictionary;
     }
 
@@ -122,4 +123,8 @@ class DictionaryImpl implements com.mysq
         }
     }
 
+    public void removeCachedTable(String tableName) {
+        ndbDictionary.removeCachedTable(tableName);
+    }
+
 }

=== added file 'storage/ndb/clusterj/clusterj-tie/src/test/java/testsuite/clusterj/tie/SchemaChangeTest.java'
--- a/storage/ndb/clusterj/clusterj-tie/src/test/java/testsuite/clusterj/tie/SchemaChangeTest.java	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/clusterj/clusterj-tie/src/test/java/testsuite/clusterj/tie/SchemaChangeTest.java	2011-10-27 23:43:25 +0000
@@ -0,0 +1,22 @@
+/*
+   Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+
+   This program 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; version 2 of the License.
+
+   This program 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, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+*/
+
+package testsuite.clusterj.tie;
+
+public class SchemaChangeTest extends testsuite.clusterj.SchemaChangeTest {
+
+}

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-5.1-telco-7.1 branch (Craig.Russell:4316 to 4317) Craig L Russell28 Oct