List:Commits« Previous MessageNext Message »
From:Craig L Russell Date:December 21 2010 12:53am
Subject:bzr push into mysql-5.1-telco-7.1 branch (Craig.Russell:4028 to 4029)
View as plain text  
 4029 Craig L Russell	2010-12-20
      Add DynamicObject to clusterj

    added:
      storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/ColumnMetadata.java
      storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/DynamicObject.java
      storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/DynamicObjectDelegate.java
      storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/DynamicObjectTest.java
      storage/ndb/clusterj/clusterj-tie/src/test/java/testsuite/clusterj/tie/DynamicObjectTest.java
    modified:
      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/AbstractDomainFieldHandlerImpl.java
      storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/AbstractDomainTypeHandlerImpl.java
      storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/DomainFieldHandlerImpl.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/metadata/InvocationHandlerImpl.java
      storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/store/Column.java
      storage/ndb/clusterj/clusterj-core/src/main/resources/com/mysql/clusterj/core/Bundle.properties
      storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ColumnImpl.java
 4028 Jonas Oreland	2010-12-20 [merge]
      ndb - merge 70 to 71

    modified:
      mysql-test/suite/ndb/ndb_config_config.ini
      storage/ndb/include/kernel/signaldata/AllocMem.hpp
      storage/ndb/include/kernel/signaldata/NodeStateSignalData.hpp
      storage/ndb/include/mgmapi/mgmapi_config_parameters.h
      storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp
      storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.hpp
      storage/ndb/src/kernel/blocks/ndbfs/AsyncIoThread.cpp
      storage/ndb/src/kernel/blocks/ndbfs/AsyncIoThread.hpp
      storage/ndb/src/kernel/blocks/ndbfs/Ndbfs.cpp
      storage/ndb/src/kernel/blocks/ndbfs/Ndbfs.hpp
      storage/ndb/src/kernel/blocks/ndbfs/VoidFs.cpp
      storage/ndb/src/kernel/blocks/record_types.hpp
      storage/ndb/src/kernel/ndbd.cpp
      storage/ndb/src/kernel/vm/ndbd_malloc_impl.cpp
      storage/ndb/src/kernel/vm/ndbd_malloc_impl.hpp
      storage/ndb/src/mgmsrv/ConfigInfo.cpp
=== added file 'storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/ColumnMetadata.java'
--- a/storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/ColumnMetadata.java	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/ColumnMetadata.java	2010-12-21 00:52:28 +0000
@@ -0,0 +1,114 @@
+/*
+   Copyright 2010, 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 com.mysql.clusterj;
+
+public interface ColumnMetadata {
+
+    /** Return the name of the column.
+     * @return the name of the column
+     */
+    String name();
+
+    /** Return the type of the column.
+     * @return the type of the column
+     */
+    Type type();
+
+    /** Return the java type of the column.
+     * @return the java type of the column
+     */
+    Class<?> javaType();
+
+    /** Return the maximum number of bytes that can be stored in the column
+     * after translating the characters using the character set.
+     * @return the maximum number of bytes that can be stored in the column
+     */
+    int maximumLength();
+
+    /** Return the column number. This number is used as the first parameter in
+     * the get and set methods of DynamicColumn.
+     * @return the column number.
+     */
+    int number();
+
+    /** Return whether this column is a primary key column.
+     * @return true if this column is a primary key column
+     */
+    boolean isPrimaryKey();
+
+    /** Return whether this column is a partition key column.
+     * @return true if this column is a partition key column
+     */
+    boolean isPartitionKey();
+
+    /** Return the precision of the column.
+     * @return the precision of the column
+     */
+    int precision();
+
+    /** Return the scale of the column.
+     * @return the scale of the column
+     */
+    int scale();
+
+    /** Return whether this column is nullable.
+     * @return whether this column is nullable
+     */
+    boolean nullable();
+
+    /** Return the charset name.
+     * @return the charset name
+     */
+    String charsetName();
+
+    public enum Type {
+
+        Bigint,          ///< 64 bit. 8 byte signed integer, can be used in array
+        Bigunsigned,     ///< 64 Bit. 8 byte signed integer, can be used in array
+        Binary,          ///< Len
+        Bit,             ///< Bit, length specifies no of bits
+        Blob,            ///< Binary large object (see NdbBlob)
+        Char,            ///< Len. A fixed array of 1-byte chars
+        Date,            ///< Precision down to 1 day(sizeof(Date) == 4 bytes )
+        Datetime,        ///< Precision down to 1 sec (sizeof(Datetime) == 8 bytes )
+        Double,          ///< 64-bit float. 8 byte float, can be used in array
+        Decimal,         ///< MySQL >= 5.0 signed decimal,  Precision, Scale
+        Decimalunsigned,
+        Float,           ///< 32-bit float. 4 bytes float, can be used in array
+        Int,             ///< 32 bit. 4 byte signed integer, can be used in array
+        Longvarchar,     ///< Length bytes: 2, little-endian
+        Longvarbinary,   ///< Length bytes: 2, little-endian
+        Mediumint,       ///< 24 bit. 3 byte signed integer, can be used in array
+        Mediumunsigned,  ///< 24 bit. 3 byte unsigned integer, can be used in array
+        Olddecimal,
+        Olddecimalunsigned,
+        Smallint,        ///< 16 bit. 2 byte signed integer, can be used in array
+        Smallunsigned,   ///< 16 bit. 2 byte unsigned integer, can be used in array
+        Text,            ///< Text blob
+        Time,            ///< Time without date
+        Timestamp,       ///< Unix time
+        Tinyint,         ///< 8 bit. 1 byte signed integer, can be used in array
+        Tinyunsigned,    ///< 8 bit. 1 byte unsigned integer, can be used in array
+        Undefined,
+        Unsigned,        ///< 32 bit. 4 byte unsigned integer, can be used in array
+        Varbinary,       ///< Length bytes: 1, Max: 255
+        Varchar,         ///< Length bytes: 1, Max: 255
+        Year             ///< Year 1901-2155 (1 byte)
+    }
+
+}

=== added file 'storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/DynamicObject.java'
--- a/storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/DynamicObject.java	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/DynamicObject.java	2010-12-21 00:52:28 +0000
@@ -0,0 +1,45 @@
+/*
+   Copyright 2010, 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 com.mysql.clusterj;
+
+public abstract class DynamicObject {
+
+    private DynamicObjectDelegate delegate;
+
+    public abstract String tableName();
+
+    public final void delegate(DynamicObjectDelegate delegate) {
+        this.delegate = delegate;
+    }
+
+    public final DynamicObjectDelegate delegate() {
+        return delegate;
+    }
+
+    public final Object get(int columnNumber) {
+        return delegate.get(columnNumber);
+    }
+
+    public final void set(int columnNumber, Object value) {
+        delegate.set(columnNumber, value);
+    }
+
+    public final ColumnMetadata[] columnMetadata() {
+        return delegate.columnMetadata();
+    }
+}

=== added file 'storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/DynamicObjectDelegate.java'
--- a/storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/DynamicObjectDelegate.java	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/DynamicObjectDelegate.java	2010-12-21 00:52:28 +0000
@@ -0,0 +1,29 @@
+/*
+   Copyright 2010, Oracle and/or its affiliates. All rights reserved.
+   All rights reserved. Use is subject to license terms.
+
+   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 com.mysql.clusterj;
+
+public interface DynamicObjectDelegate {
+
+    public Object get(int columnNumber);
+
+    public void set(int columnNumber, Object value);
+
+    public ColumnMetadata[] columnMetadata();
+
+}

=== 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	2010-12-17 19:54:39 +0000
+++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/SessionImpl.java	2010-12-21 00:52:28 +0000
@@ -260,16 +260,34 @@ public class SessionImpl implements Sess
         return instance;
     }
 
-    /** Make an instance persistent.
+    /** Make an instance persistent. Also recursively make an iterable collection or array persistent.
      * 
-     * @param object the instance
+     * @param object the instance or array or iterable collection of instances
      * @return the instance
      */
     public <T> T makePersistent(T object) {
         if (object == null) {
             return null;
         }
-        DomainTypeHandler domainTypeHandler = getDomainTypeHandler(object);
+        if (Iterable.class.isAssignableFrom(object.getClass())) {
+            startAutoTransaction();
+            Iterable<?> instances = (Iterable<?>)object;
+            for (Object instance:instances) {
+                makePersistent(instance);
+            }
+            endAutoTransaction();
+            return object;
+        }
+        if (object.getClass().isArray()) {
+            startAutoTransaction();
+            Object[] instances = (Object[])object;
+            for (Object instance:instances) {
+                makePersistent(instance);
+            }
+            endAutoTransaction();
+            return object;
+        }
+        DomainTypeHandler<?> domainTypeHandler = getDomainTypeHandler(object);
         ValueHandler valueHandler = domainTypeHandler.getValueHandler(object);
         insert(domainTypeHandler, valueHandler);
         return object;

=== modified file 'storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/AbstractDomainFieldHandlerImpl.java'
--- a/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/AbstractDomainFieldHandlerImpl.java	2010-09-13 10:48:19 +0000
+++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/AbstractDomainFieldHandlerImpl.java	2010-12-21 00:52:28 +0000
@@ -18,11 +18,12 @@
 
 package com.mysql.clusterj.core.metadata;
 
-import com.mysql.clusterj.core.spi.ValueHandler;
-import com.mysql.clusterj.core.spi.DomainTypeHandler;
 import com.mysql.clusterj.ClusterJDatastoreException;
 import com.mysql.clusterj.ClusterJFatalInternalException;
 import com.mysql.clusterj.ClusterJUserException;
+import com.mysql.clusterj.ColumnMetadata;
+import com.mysql.clusterj.core.spi.ValueHandler;
+import com.mysql.clusterj.core.spi.DomainTypeHandler;
 import com.mysql.clusterj.core.query.CandidateIndexImpl;
 import com.mysql.clusterj.core.query.PredicateImpl;
 import com.mysql.clusterj.core.spi.DomainFieldHandler;
@@ -32,7 +33,6 @@ import com.mysql.clusterj.core.store.Ope
 import com.mysql.clusterj.core.store.PartitionKey;
 import com.mysql.clusterj.core.store.ResultData;
 import com.mysql.clusterj.core.store.ScanFilter;
-import com.mysql.clusterj.core.store.Column.Type;
 import com.mysql.clusterj.core.util.I18NHelper;
 import com.mysql.clusterj.core.util.Logger;
 import com.mysql.clusterj.core.util.LoggerFactoryService;
@@ -50,7 +50,7 @@ import java.util.Set;
 /**
  *
  */
-public abstract class AbstractDomainFieldHandlerImpl implements DomainFieldHandler {
+public abstract class AbstractDomainFieldHandlerImpl implements DomainFieldHandler, ColumnMetadata {
 
     public AbstractDomainFieldHandlerImpl() {}
 
@@ -62,8 +62,8 @@ public abstract class AbstractDomainFiel
     /** The domain type handler for this field */
     protected DomainTypeHandler<?> domainTypeHandler;
 
-    /** "true" if the mapped column allows null values */
-    protected String columnAllowsNull;
+    /** true if the mapped column allows null values */
+    protected boolean nullable;
 
     /** The default value for the column if the field is null */
     protected String columnDefaultValue = null;
@@ -77,6 +77,18 @@ public abstract class AbstractDomainFiel
     /** The Charset name for the column. */
     protected String charsetName = null;
 
+    /** The precision of the column in the database */
+    protected int precision;
+
+    /** The scale of the column in the database */
+    protected int scale;
+
+    /** The length of the column in the database */
+    protected int maximumLength;
+
+    /** true if the column is part of the partition key */
+    protected boolean partitionKey;
+
     /** The Store Type for the column. */
     protected Type storeColumnType = null;
 
@@ -2613,4 +2625,59 @@ public abstract class AbstractDomainFiel
         }
     };
 
+    /* These methods implement ColumnMetadata
+     */
+
+    protected void initializeColumnMetadata(com.mysql.clusterj.core.store.Column storeColumn) {
+        this.columnName = storeColumn.getName();;
+        this.storeColumnType = storeColumn.getType();
+        this.charsetName = storeColumn.getCharsetName();
+        this.primaryKey = storeColumn.isPrimaryKey();
+        this.partitionKey = storeColumn.isPartitionKey();
+        this.precision = storeColumn.getPrecision();
+        this.scale = storeColumn.getScale();
+        this.maximumLength = storeColumn.getLength();
+        this.nullable = storeColumn.getNullable();
+    }
+
+    public boolean isPartitionKey() {
+        return partitionKey;
+    }
+
+    public int maximumLength() {
+        return maximumLength;
+    }
+
+    public String name() {
+        return name;
+    }
+
+    public int number() {
+        return fieldNumber;
+    }
+
+    public int precision() {
+        return precision;
+    }
+
+    public int scale() {
+        return scale;
+    }
+
+    public Type type() {
+        return this.storeColumnType;
+    }
+
+    public boolean nullable() {
+        return nullable;
+    }
+
+    public Class<?> javaType() {
+        return this.type;
+    }
+
+    public String charsetName() {
+        return this.charsetName;
+    }
+
 }

=== modified file 'storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/AbstractDomainTypeHandlerImpl.java'
--- a/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/AbstractDomainTypeHandlerImpl.java	2010-09-22 18:34:38 +0000
+++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/AbstractDomainTypeHandlerImpl.java	2010-12-21 00:52:28 +0000
@@ -130,12 +130,14 @@ public abstract class AbstractDomainType
             if (primaryKeyColumnNames[i].equals(columnName)) {
                 idFieldHandlers[i] = fmd;
                 idFieldNumbers[i] = fmd.getFieldNumber();
+                if (logger.isDetailEnabled()) logger.detail("registerPrimaryKeyColumn found primary key " + columnName);
             }
         }
         // find the partition key column that matches the primary key column
         for (int j = 0; j < partitionKeyColumnNames.length; ++j) {
             if (partitionKeyColumnNames[j].equals(columnName)) {
                 partitionKeyFieldHandlers[j] = fmd;
+                if (logger.isDetailEnabled()) logger.detail("registerPrimaryKeyColumn found partition key " + columnName);
             }
         }
         return;

=== modified file 'storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/DomainFieldHandlerImpl.java'
--- a/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/DomainFieldHandlerImpl.java	2010-10-28 09:50:56 +0000
+++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/DomainFieldHandlerImpl.java	2010-12-21 00:52:28 +0000
@@ -18,7 +18,6 @@
 
 package com.mysql.clusterj.core.metadata;
 
-import com.mysql.clusterj.core.spi.DomainFieldHandler;
 import com.mysql.clusterj.core.spi.ValueHandler;
 import com.mysql.clusterj.ClusterJDatastoreException;
 import com.mysql.clusterj.ClusterJUserException;
@@ -47,7 +46,7 @@ import java.math.BigInteger;
  * type-specific handlers for Ndb operations.
  * 
  */
-public class DomainFieldHandlerImpl extends AbstractDomainFieldHandlerImpl implements DomainFieldHandler {
+public class DomainFieldHandlerImpl extends AbstractDomainFieldHandlerImpl {
 
     /** The NullValue setting of the column from the Persistent annotation. */
     NullValue nullValue = NullValue.NONE;
@@ -70,6 +69,9 @@ public class DomainFieldHandlerImpl exte
     /** The Column annotation on the get method. */
     protected Column columnAnnotation = null;
 
+    /** The AllowsNull annotation */
+    protected String columnAllowsNull;
+
     /** Lob annotation is not null if annotated with @Lob. */
     protected Lob lobAnnotation;
 
@@ -83,6 +85,16 @@ public class DomainFieldHandlerImpl exte
         return compareTo((DomainFieldHandlerImpl)other);
     }
 
+    /** Create a domain field handler for annotated interfaces.
+     * 
+     * @param domainTypeHandler the domain type handler
+     * @param table the table
+     * @param fieldNumber the field number (in schema definition order)
+     * @param name the field name
+     * @param type the java type
+     * @param getMethod the get method for the field
+     * @param setMethod the set method for the field
+     */
     public DomainFieldHandlerImpl(DomainTypeHandlerImpl<?> domainTypeHandler, Table table,
             int fieldNumber, String name, Class<?> type,
             Method getMethod, Method setMethod) {
@@ -136,13 +148,11 @@ public class DomainFieldHandlerImpl exte
                 throw new ClusterJUserException(local.message("ERR_No_Column",
                         name, table.getName(), columnName));
             }
-            storeColumnType = storeColumn.getType();
-            charsetName = storeColumn.getCharsetName();
+            initializeColumnMetadata(storeColumn);
             if (logger.isDebugEnabled())
                 logger.debug("Column type for " + name + " is "
                         + storeColumnType.toString() + "; charset name is "
                         + charsetName);
-            primaryKey = storeColumn.isPrimaryKey();
             domainTypeHandler.registerPrimaryKeyColumn(this, columnName);
             lobAnnotation = getMethod.getAnnotation(Lob.class);
         }
@@ -205,7 +215,7 @@ public class DomainFieldHandlerImpl exte
             } else if (type.equals(Long.class)) {
                 objectOperationHandlerDelegate = objectOperationHandlerObjectLong;
             } else if (type.equals(Short.class)) {
-                if (com.mysql.clusterj.core.store.Column.Type.Year.equals(storeColumnType)) {
+                if (Type.Year.equals(storeColumnType)) {
                     objectOperationHandlerDelegate = objectOperationHandlerObjectShortYear;
                 } else {
                     objectOperationHandlerDelegate = objectOperationHandlerObjectShort;
@@ -217,7 +227,7 @@ public class DomainFieldHandlerImpl exte
             } else if (type.equals(long.class)) {
                 objectOperationHandlerDelegate = objectOperationHandlerLong;
             } else if (type.equals(short.class)) {
-                if (com.mysql.clusterj.core.store.Column.Type.Year.equals(storeColumnType)) {
+                if (Type.Year.equals(storeColumnType)) {
                     objectOperationHandlerDelegate = objectOperationHandlerShortYear;
                 } else {
                     objectOperationHandlerDelegate = objectOperationHandlerShort;
@@ -247,6 +257,7 @@ public class DomainFieldHandlerImpl exte
         // Handle indexes. One index can be annotated on this field.
         // Other indexes including the column mapped to this field
         // are annotated on the class.
+        // TODO: indexes are ignored since they are handled by reading the column metadata
         indexAnnotation = getMethod.getAnnotation(
                 com.mysql.clusterj.annotation.Index.class);
         String indexName = null;
@@ -257,10 +268,7 @@ public class DomainFieldHandlerImpl exte
                         local.message("ERR_Index_Annotation_Columns", domainTypeHandler.getName(), name));
             }
         }
-        indices = domainTypeHandler.registerIndices(this, columnName);
-        indexNames = domainTypeHandler.getIndexNames(indices);
-        logger.debug("Index names for " + name + " are " + indexNames);
-        logger.debug("Indices for " + name + " are " + printIndices());
+        registerIndices(domainTypeHandler);
 
         persistentAnnotation = getMethod.getAnnotation(Persistent.class);
         if (persistentAnnotation != null) {
@@ -289,10 +297,174 @@ public class DomainFieldHandlerImpl exte
         reportErrors();
     }
 
+    /** Create a domain field handler for dynamic objects.
+     * 
+     * @param domainTypeHandler the domain type handler
+     * @param table the table
+     * @param i the field number
+     * @param storeColumn the store column definition
+     */
+    public DomainFieldHandlerImpl(
+            DomainTypeHandlerImpl<?> domainTypeHandler, Table table, int i,
+            com.mysql.clusterj.core.store.Column storeColumn) {
+        if (logger.isDebugEnabled()) logger.debug("new dynamic DomainFieldHandlerImpl: " +
+                "fieldNumber: " + fieldNumber + "; name:" + name);
+        this.domainTypeHandler = domainTypeHandler;
+        this.fieldNumber = i;
+        this.storeColumn = storeColumn;
+        initializeColumnMetadata(storeColumn);
+        this.name = this.columnName;
+        this.columnNames = new String[]{columnName};
+        if (primaryKey) {
+            domainTypeHandler.registerPrimaryKeyColumn(this, columnName);
+            switch (this.storeColumnType) {
+                case Int:
+                case Unsigned:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerKeyInt;
+                    this.type = Integer.class;
+                    break;
+                case Char:
+                case Varchar:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerKeyString;
+                    this.type = String.class;
+                    break;
+                case Bigint:
+                case Bigunsigned:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerKeyLong;
+                    this.type = Long.class;
+                    break;
+                default:
+                    error(local.message("ERR_Primary_Field_Type", name));
+                }
+        } else {
+            switch (this.storeColumnType) {
+                case Bigint:
+                case Bigunsigned:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerObjectLong;
+                    this.type = Long.class;
+                    break;
+                case Binary:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerBytes;
+                    this.type = byte[].class;
+                    break;
+                case Bit:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerObjectLong;
+                    this.type = Long.class;
+                    break;
+                case Blob:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerBytesLob;
+                    this.type = byte[].class;
+                    break;
+                case Char:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerString;
+                    this.type = String.class;
+                    break;
+                case Date:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerJavaSqlDate;
+                    this.type = java.sql.Date.class;
+                    break;
+                case Datetime:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerJavaSqlTimestamp;
+                    this.type = java.sql.Timestamp.class;
+                    break;
+                case Decimal:
+                case Decimalunsigned:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerDecimal;
+                    this.type = BigDecimal.class;
+                    break;
+                case Double:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerObjectDouble;
+                    this.type = Double.class;
+                    break;
+                case Float:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerObjectFloat;
+                    this.type = Float.class;
+                    break;
+                case Int:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerObjectInteger;
+                    this.type = Integer.class;
+                    break;
+                case Longvarbinary:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerBytes;
+                    this.type = byte[].class;
+                    break;
+                case Longvarchar:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerString;
+                    this.type = String.class;
+                    break;
+                case Mediumint:
+                case Mediumunsigned:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerObjectInteger;
+                    this.type = Integer.class;
+                    break;
+                case Olddecimal:
+                    error(local.message("ERR_Unsupported_Field_Type", "Olddecimal", name));
+                    objectOperationHandlerDelegate = objectOperationHandlerUnsupportedType;
+                    break;
+                case Olddecimalunsigned:
+                    error(local.message("ERR_Unsupported_Field_Type", "Olddecimalunsigned", name));
+                    objectOperationHandlerDelegate = objectOperationHandlerUnsupportedType;
+                    break;
+                case Smallint:
+                case Smallunsigned:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerObjectShort;
+                    this.type = Short.class;
+                    break;
+                case Text:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerStringLob;
+                    this.type = String.class;
+                    break;
+                case Time:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerJavaSqlTime;
+                    this.type = java.sql.Time.class;
+                    break;
+                case Timestamp:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerJavaSqlTimestamp;
+                    this.type = java.sql.Timestamp.class;
+                    break;
+                case Tinyint:
+                case Tinyunsigned:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerObjectByte;
+                    this.type = Byte.class;
+                    break;
+                case Undefined:
+                    error(local.message("ERR_Unsupported_Field_Type", "Undefined"));
+                    objectOperationHandlerDelegate = objectOperationHandlerUnsupportedType;
+                    break;
+                case Unsigned:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerObjectInteger;
+                    this.type = Integer.class;
+                    break;
+                case Varbinary:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerBytes;
+                    this.type = byte[].class;
+                    break;
+                case Varchar:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerString;
+                    this.type = String.class;
+                    break;
+                case Year:
+                    this.objectOperationHandlerDelegate = objectOperationHandlerObjectShortYear;
+                    this.type = Short.class;
+                    break;
+            }
+        }
+        nullValueDelegate = nullValueNONE;
+        registerIndices(domainTypeHandler);
+        reportErrors();
+    }
+
     public boolean isPersistent() {
         return notPersistentAnnotation == null;
     }
 
+    protected void registerIndices(DomainTypeHandlerImpl<?> domainTypeHandler) {
+        this.indices = domainTypeHandler.registerIndices(this, columnName);
+        this.indexNames = domainTypeHandler.getIndexNames(indices);
+        if (logger.isDebugEnabled()) logger.debug("Index names for " + name + " are " + indexNames);
+        if (logger.isDebugEnabled()) logger.debug("Indices for " + name + " are " + printIndices());
+    }
+
     @Override
     public void operationSetValue(ValueHandler handler, Operation op) {
         // handle NullValue here

=== 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	2010-01-22 19:10:41 +0000
+++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/DomainTypeHandlerFactoryImpl.java	2010-12-21 00:52:28 +0000
@@ -18,6 +18,7 @@
 
 package com.mysql.clusterj.core.metadata;
 
+import com.mysql.clusterj.ClusterJException;
 import com.mysql.clusterj.ClusterJHelper;
 import com.mysql.clusterj.ClusterJUserException;
 import com.mysql.clusterj.core.spi.DomainTypeHandlerFactory;
@@ -76,6 +77,9 @@ public class DomainTypeHandlerFactoryImp
             errorMessages.append("Trying standard factory com.mysql.clusterj.core.metadata.DomainTypeHandlerImpl.\n");
             handler = new DomainTypeHandlerImpl<T>(domainClass, dictionary);
             return handler;
+        } catch (ClusterJException e) {
+            errorMessages.append(e.toString());
+            throw e;
         } catch (Exception e) {
             errorMessages.append(e.toString());
             throw new ClusterJUserException(errorMessages.toString(), e);

=== 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	2010-09-22 18:34:38 +0000
+++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/DomainTypeHandlerImpl.java	2010-12-21 00:52:28 +0000
@@ -23,11 +23,15 @@ import com.mysql.clusterj.core.spi.Value
 import com.mysql.clusterj.ClusterJException;
 import com.mysql.clusterj.ClusterJFatalInternalException;
 import com.mysql.clusterj.ClusterJUserException;
+import com.mysql.clusterj.DynamicObject;
+import com.mysql.clusterj.ColumnMetadata;
+import com.mysql.clusterj.DynamicObjectDelegate;
 
 import com.mysql.clusterj.annotation.PersistenceCapable;
 
 import com.mysql.clusterj.core.CacheManager;
 
+import com.mysql.clusterj.core.store.Column;
 import com.mysql.clusterj.core.store.Index;
 import com.mysql.clusterj.core.store.Dictionary;
 import com.mysql.clusterj.core.store.Operation;
@@ -59,6 +63,9 @@ public class DomainTypeHandlerImpl<T> ex
     /** The domain class. */
     Class<T> cls;
 
+    /** Dynamic class indicator */
+    boolean dynamic = false;
+
     /** The methods of the properties. */
     private Map<String, Method> unmatchedGetMethods = new HashMap<String, Method>();
     private Map<String, Method> unmatchedSetMethods = new HashMap<String, Method>();
@@ -84,19 +91,24 @@ public class DomainTypeHandlerImpl<T> ex
      */
     @SuppressWarnings( "unchecked" )
     public DomainTypeHandlerImpl(Class<T> cls, Dictionary dictionary) {
-        if (logger.isDebugEnabled()) logger.debug("New DomainTypeHandlerImpl for class " + cls.getName());
         this.cls = cls;
         this.name = cls.getName();
-        // Create a proxy class for the domain class
-        proxyClass = (Class<T>)Proxy.getProxyClass(
-                cls.getClassLoader(), new Class[]{cls});
-        ctor = getConstructorForInvocationHandler (proxyClass);
-        persistenceCapable = cls.getAnnotation(PersistenceCapable.class);
-        if (persistenceCapable == null) {
-            throw new ClusterJUserException(local.message(
-                    "ERR_No_Persistence_Capable_Annotation", name));
+        this.dynamic = DynamicObject.class.isAssignableFrom(cls);
+        if (dynamic) {
+            // Dynamic object has a handler but no proxy
+            this.tableName = getTableNameForDynamicObject((Class<DynamicObject>)cls);
+        } else {
+            // Create a proxy class for the domain class
+            proxyClass = (Class<T>)Proxy.getProxyClass(
+                    cls.getClassLoader(), new Class[]{cls});
+            ctor = getConstructorForInvocationHandler (proxyClass);
+            persistenceCapable = cls.getAnnotation(PersistenceCapable.class);
+            if (persistenceCapable == null) {
+                throw new ClusterJUserException(local.message(
+                        "ERR_No_Persistence_Capable_Annotation", name));
+            }
+            this.tableName = persistenceCapable.table();
         }
-        tableName = persistenceCapable.table();
         this.table = getTable(dictionary);
         if (table == null) {
             throw new ClusterJUserException(local.message("ERR_Get_NdbTable", name, tableName));
@@ -132,76 +144,95 @@ public class DomainTypeHandlerImpl<T> ex
             indexHandlerImpls.add(imd);
         }
 
-        // Now iterate the fields in the class
-        List<String> fieldNameList = new ArrayList<String>();
-        Method[] methods = cls.getMethods();
-        for (Method method: methods) {
-            // remember get methods
-            String methodName = method.getName();
-            String name = convertMethodName(methodName);
-            Class type = getType(method);
-            DomainFieldHandlerImpl domainFieldHandler = null;
-            if (methodName.startsWith("get")) {
-                Method unmatched = unmatchedSetMethods.get(name);
-                if (unmatched == null) {
-                    // get is first of the pair; put it into the unmatched map
-                    unmatchedGetMethods.put(name, method);
-                } else {
-                    // found the potential match
-                    if (getType(unmatched).equals(type)) {
-                        // method names and types match
-                        unmatchedSetMethods.remove(name);
-                        domainFieldHandler = new DomainFieldHandlerImpl(this, table,
-                                numberOfFields++, name, type, method, unmatched);
-                    } else {
-                        // both unmatched because of type mismatch
-                        unmatchedGetMethods.put(name, method);
-                    }
+        if (dynamic) {
+            // for each column in the database, create a field
+            List<String> fieldNameList = new ArrayList<String>();
+            for (String columnName: table.getColumnNames()) {
+                Column storeColumn = table.getColumn(columnName);
+                DomainFieldHandlerImpl domainFieldHandler = null;
+                domainFieldHandler = 
+                    new DomainFieldHandlerImpl(this, table, numberOfFields++, storeColumn);
+                String fieldName = domainFieldHandler.getName();
+                fieldNameList.add(fieldName);
+                fieldNameToNumber.put(domainFieldHandler.getName(), domainFieldHandler.getFieldNumber());
+                persistentFieldHandlers.add(domainFieldHandler);
+                if (!storeColumn.isPrimaryKey()) {
+                    nonPKFieldHandlers.add(domainFieldHandler);
                 }
-            } else if (methodName.startsWith("set")) {
-                Method unmatched = unmatchedGetMethods.get(name);
-                if (unmatched == null) {
-                    // set is first of the pair; put it into the unmatched map
-                    unmatchedSetMethods.put(name, method);
-                } else {
-                    // found the potential match
-                    if (getType(unmatched).equals(type)) {
-                        // method names and types match
-                        unmatchedGetMethods.remove(name);
-                        domainFieldHandler = new DomainFieldHandlerImpl(this, table,
-                                numberOfFields++, name, type, unmatched, method);
+            }
+            fieldNames = fieldNameList.toArray(new String[fieldNameList.size()]);
+        } else {
+            // Iterate the fields (names and types based on get/set methods) in the class
+            List<String> fieldNameList = new ArrayList<String>();
+            Method[] methods = cls.getMethods();
+            for (Method method: methods) {
+                // remember get methods
+                String methodName = method.getName();
+                String name = convertMethodName(methodName);
+                Class type = getType(method);
+                DomainFieldHandlerImpl domainFieldHandler = null;
+                if (methodName.startsWith("get")) {
+                    Method unmatched = unmatchedSetMethods.get(name);
+                    if (unmatched == null) {
+                        // get is first of the pair; put it into the unmatched map
+                        unmatchedGetMethods.put(name, method);
                     } else {
-                        // both unmatched because of type mismatch
+                        // found the potential match
+                        if (getType(unmatched).equals(type)) {
+                            // method names and types match
+                            unmatchedSetMethods.remove(name);
+                            domainFieldHandler = new DomainFieldHandlerImpl(this, table,
+                                    numberOfFields++, name, type, method, unmatched);
+                        } else {
+                            // both unmatched because of type mismatch
+                            unmatchedGetMethods.put(name, method);
+                        }
+                    }
+                } else if (methodName.startsWith("set")) {
+                    Method unmatched = unmatchedGetMethods.get(name);
+                    if (unmatched == null) {
+                        // set is first of the pair; put it into the unmatched map
                         unmatchedSetMethods.put(name, method);
+                    } else {
+                        // found the potential match
+                        if (getType(unmatched).equals(type)) {
+                            // method names and types match
+                            unmatchedGetMethods.remove(name);
+                            domainFieldHandler = new DomainFieldHandlerImpl(this, table,
+                                    numberOfFields++, name, type, unmatched, method);
+                        } else {
+                            // both unmatched because of type mismatch
+                            unmatchedSetMethods.put(name, method);
+                        }
                     }
                 }
-            }
-            if (domainFieldHandler != null) {
-                // found matching methods
-                // set up field name to number map
-                String fieldName = domainFieldHandler.getName();
-                fieldNameList.add(fieldName);
-                fieldNameToNumber.put(domainFieldHandler.getName(), domainFieldHandler.getFieldNumber());
-                // put field into either persistent or not persistent list
-                if (domainFieldHandler.isPersistent()) {
-                    persistentFieldHandlers.add(domainFieldHandler);
-                    if (!domainFieldHandler.isPrimaryKey()) {
-                        nonPKFieldHandlers.add(domainFieldHandler);
+                if (domainFieldHandler != null) {
+                    // found matching methods
+                    // set up field name to number map
+                    String fieldName = domainFieldHandler.getName();
+                    fieldNameList.add(fieldName);
+                    fieldNameToNumber.put(domainFieldHandler.getName(), domainFieldHandler.getFieldNumber());
+                    // put field into either persistent or not persistent list
+                    if (domainFieldHandler.isPersistent()) {
+                        persistentFieldHandlers.add(domainFieldHandler);
+                        if (!domainFieldHandler.isPrimaryKey()) {
+                            nonPKFieldHandlers.add(domainFieldHandler);
+                        }
+                    }
+                    if (domainFieldHandler.isPrimitive()) {
+                        primitiveFieldHandlers.add(domainFieldHandler);
                     }
-                }
-                if (domainFieldHandler.isPrimitive()) {
-                    primitiveFieldHandlers.add(domainFieldHandler);
                 }
             }
-        }
-        fieldNames = fieldNameList.toArray(new String[fieldNameList.size()]);
-        // done with methods; if anything in unmatched we have a problem
-        if ((!unmatchedGetMethods.isEmpty()) || (!unmatchedSetMethods.isEmpty())) {
-            throw new ClusterJUserException(
-                    local.message("ERR_Unmatched_Methods", 
-                    unmatchedGetMethods, unmatchedSetMethods));
-        }
+            fieldNames = fieldNameList.toArray(new String[fieldNameList.size()]);
+            // done with methods; if anything in unmatched we have a problem
+            if ((!unmatchedGetMethods.isEmpty()) || (!unmatchedSetMethods.isEmpty())) {
+                throw new ClusterJUserException(
+                        local.message("ERR_Unmatched_Methods", 
+                        unmatchedGetMethods, unmatchedSetMethods));
+            }
 
+        }
         // Check that all index columnNames have corresponding fields
         // indexes without fields will be unusable for query
         for (IndexHandlerImpl indexHandler:indexHandlerImpls) {
@@ -214,6 +245,24 @@ public class DomainTypeHandlerImpl<T> ex
         }
     }
 
+    protected <O extends DynamicObject> String getTableNameForDynamicObject(Class<O> cls) {
+        DynamicObject dynamicObject;
+        String tableName;
+        try {
+            dynamicObject = cls.newInstance();
+            tableName = dynamicObject.tableName();
+        } catch (InstantiationException e) {
+            throw new ClusterJUserException(local.message("ERR_Dynamic_Object_Instantiation", cls.getName()), e);
+        } catch (IllegalAccessException e) {
+            throw new ClusterJUserException(local.message("ERR_Dynamic_Object_Illegal_Access", cls.getName()), e);
+        }
+        if (tableName == null) {
+            throw new ClusterJUserException(local.message("ERR_Dynamic_Object_Null_Table_Name",
+                    cls.getName()));
+        }
+        return tableName;
+    }
+
     /** Is this type supported? */
     public boolean isSupportedType() {
         // if unsupported, throw an exception
@@ -224,6 +273,8 @@ public class DomainTypeHandlerImpl<T> ex
             throws IllegalArgumentException {
         if (instance instanceof ValueHandler) {
             return (ValueHandler)instance;
+        } else if (instance instanceof DynamicObject) {
+            return (ValueHandler)((DynamicObject)instance).delegate();
         } else {
             ValueHandler handler = (ValueHandler)
                     Proxy.getInvocationHandler(instance);
@@ -284,11 +335,17 @@ public class DomainTypeHandlerImpl<T> ex
     }
 
     public T newInstance() {
+        T instance;
         try {
             InvocationHandlerImpl<T> handler = new InvocationHandlerImpl<T>(this);
-            T proxy = ctor.newInstance(new Object[] {handler});
-            handler.setProxy(proxy);
-            return proxy;
+            if (dynamic) {
+                instance = cls.newInstance();
+                ((DynamicObject)instance).delegate((DynamicObjectDelegate)handler);
+            } else {
+                instance = ctor.newInstance(new Object[] {handler});
+                handler.setProxy(instance);
+            }
+            return instance;
         } catch (InstantiationException ex) {
             throw new ClusterJException(
                     local.message("ERR_Create_Instance", cls.getName()), ex);
@@ -431,4 +488,9 @@ public class DomainTypeHandlerImpl<T> ex
         throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
     }
 
+    protected ColumnMetadata[] columnMetadata() {
+        ColumnMetadata[] result = new ColumnMetadata[numberOfFields];
+        return persistentFieldHandlers.toArray(result);
+    }
+
 }

=== modified file 'storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/InvocationHandlerImpl.java'
--- a/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/InvocationHandlerImpl.java	2010-01-22 19:10:41 +0000
+++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/metadata/InvocationHandlerImpl.java	2010-12-21 00:52:28 +0000
@@ -21,6 +21,8 @@ package com.mysql.clusterj.core.metadata
 import com.mysql.clusterj.core.spi.ValueHandler;
 import com.mysql.clusterj.core.spi.DomainTypeHandler;
 import com.mysql.clusterj.ClusterJUserException;
+import com.mysql.clusterj.ColumnMetadata;
+import com.mysql.clusterj.DynamicObjectDelegate;
 
 import com.mysql.clusterj.core.CacheManager;
 import com.mysql.clusterj.core.StateManager;
@@ -44,7 +46,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 public class InvocationHandlerImpl<T> implements InvocationHandler,
-        StateManager, ValueHandler {
+        StateManager, ValueHandler, DynamicObjectDelegate {
 
     /** My message translator */
     static final I18NHelper local = I18NHelper.getInstance(InvocationHandlerImpl.class);
@@ -441,4 +443,16 @@ public class InvocationHandlerImpl<T> im
         properties[fieldNumber] = value;
     }
 
+    public Object get(int columnNumber) {
+        return properties[columnNumber];
+    }
+
+    public void set(int columnNumber, Object value) {
+        properties[columnNumber] = value;
+    }
+
+    public ColumnMetadata[] columnMetadata() {
+        return domainTypeHandler.columnMetadata();
+    }
+
 }

=== modified file 'storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/store/Column.java'
--- a/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/store/Column.java	2010-09-13 10:48:19 +0000
+++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/store/Column.java	2010-12-21 00:52:28 +0000
@@ -18,6 +18,8 @@
 
 package com.mysql.clusterj.core.store;
 
+import com.mysql.clusterj.ColumnMetadata.Type;
+
 /** Column metadata for ndb columns.
  *
  */
@@ -39,6 +41,11 @@ public interface Column {
      */
     public boolean isPrimaryKey();
 
+    /** Is this column a partition key column?
+     * @return true if this column is a partition key column
+     */
+    public boolean isPartitionKey();
+
     /** Get the Charset name of the column. This is the value of the 
      * CHARACTER SET parameter in the column definition, or if not specified,
      * the value of the DEFAULT CHARACTER SET parameter in the table definition.
@@ -53,42 +60,10 @@ public interface Column {
      */
     public int getCharsetNumber();
 
-    /** The store type of the column. This is mapped from the ndb column type
-     * to the abstract type used in the store api.
+    /** For character columns, get the maximum length in bytes that can be stored
+     * in the column, excluding the prefix, after conversion via the charset.
      */
-    public enum Type {
-        Bigint,
-        Bigunsigned,
-        Binary,
-        Bit,
-        Blob,
-        Char,
-        Date,
-        Datetime,
-        Decimal,
-        Decimalunsigned,
-        Double,
-        Float,
-        Int,
-        Longvarbinary,
-        Longvarchar,
-        Mediumint,
-        Mediumunsigned,
-        Olddecimal,
-        Olddecimalunsigned,
-        Smallint,
-        Smallunsigned,
-        Text,
-        Time,
-        Timestamp,
-        Tinyint,
-        Tinyunsigned,
-        Undefined,
-        Unsigned,
-        Varbinary,
-        Varchar,
-        Year
-    }
+    public int getLength();
 
     /** For variable size columns, get the length of the prefix (one or two bytes) 
      * that specifies the length of the column data.

=== 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	2010-09-22 18:34:38 +0000
+++ b/storage/ndb/clusterj/clusterj-core/src/main/resources/com/mysql/clusterj/core/Bundle.properties	2010-12-21 00:52:28 +0000
@@ -109,3 +109,8 @@ cannot have a null parameter.
 ERR_Query_Where_Must_Not_Be_Null:Query where must not be null.
 ERR_Cannot_Set_Coordinated_Transaction_Id_After_Transaction_Begin:\
 Cannot set coordinated transaction after transaction begin.
+ERR_Dynamic_Object_Instantiation:InstantiationException: \
+The dynamic class {0} must be static, public, and have a public constructor.
+ERR_Dynamic_Object_Illegal_Access:IllegalAccessException: \
+The dynamic class {0} must be static, public, and have a public constructor.
+ERR_Dynamic_Object_Null_Table_Name:The method {0}.tableName() must return a non-null table name.

=== added file 'storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/DynamicObjectTest.java'
--- a/storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/DynamicObjectTest.java	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/DynamicObjectTest.java	2010-12-21 00:52:28 +0000
@@ -0,0 +1,232 @@
+/*
+   Copyright (C) 2009 Sun Microsystems Inc.
+   All rights reserved. Use is subject to license terms.
+
+   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.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.mysql.clusterj.ClusterJUserException;
+import com.mysql.clusterj.DynamicObject;
+import com.mysql.clusterj.ColumnMetadata;
+import com.mysql.clusterj.Query;
+import com.mysql.clusterj.ColumnMetadata.Type;
+import com.mysql.clusterj.query.QueryBuilder;
+import com.mysql.clusterj.query.QueryDomainType;
+
+public class DynamicObjectTest extends AbstractClusterJModelTest {
+
+    private static final String tablename = "t_basic";
+
+    private static final int NUMBER_TO_INSERT = 5;
+
+    private DynamicObject[] instances = new TBasic[NUMBER_TO_INSERT];
+
+    private DynamicObject tbasic;
+
+    private Object[] expectedTBasicNames = new Object[] {"id", "name", "age", "magic"};
+
+    private Object[] expectedTBasicTypes = new Object[] {Type.Int, Type.Varchar, Type.Int, Type.Int};
+
+    private Object[] expectedTBasicJavaTypes = new Object[] {Integer.class, String.class, Integer.class, Integer.class};
+
+    private Object[] expectedTBasicMaximumLengths = new Object[] {1, 32, 1, 1};
+
+    private Object[] expectedTBasicNumbers = new Object[] {0, 1, 2, 3};
+
+    private Object[] expectedTBasicIsPrimaryKeys = new Object[] {true, false, false, false};
+
+    private Object[] expectedTBasicIsPartitionKeys = new Object[] {true, false, false, false};
+
+    private Object[] expectedTBasicPrecisions = new Object[] {0, 0, 0, 0};
+
+    private Object[] expectedTBasicScales = new Object[] {0, 0, 0, 0};
+
+    private Object[] expectedTBasicNullables = new Object[] {false, true, true, false};
+
+    private Object[] expectedTBasicCharsetNames = new Object[] {null, "latin1", null, null};
+    
+    @Override
+    public void localSetUp() {
+        createSessionFactory();
+        session = sessionFactory.getSession();
+        createDynamicInstances(TBasic.class, NUMBER_TO_INSERT);
+        tbasic = instances[0];
+        tx = session.currentTransaction();
+        int count = 0;
+        for (int i = 0; i < NUMBER_TO_INSERT; ++i) {
+            try {
+                session.deletePersistent(TBasic.class, i);
+                ++count;
+            } catch (Exception ex) {
+                // ignore exceptions -- might not be any instances to delete
+            }
+        }
+        addTearDownClasses(TBasic.class);
+    }
+
+    private <T extends DynamicObject> void createDynamicInstances(Class<T> dynamicClass, int numberToInsert) {
+        for (int i = 0; i < numberToInsert; ++i) {
+            DynamicObject instance = createInstance(dynamicClass, i);
+            instance.set(1, String.valueOf(i)); // name
+            instance.set(2, i); // age
+            instance.set(3, i); // magic
+            instances[i] = instance;
+        }
+    }
+
+    private <T> T createInstance(Class<T> cls, int i) {
+        T instance = session.newInstance(cls, i);
+        return instance;
+    }
+
+    public static class TBasic extends DynamicObject {
+        @Override
+        public String tableName() {
+            return tablename;
+        }
+    }
+
+    public void test() {
+        insert();
+        find();
+        lookup();
+        query();
+        badClass(DynamicObjectPrivate.class);
+        badClass(DynamicObjectProtectedConstructor.class);
+        badClass(DynamicObjectNonStatic.class);
+        badClass(DynamicObjectPrivateConstructor.class);
+        badClass(DynamicObjectNullTableName.class);
+        badClass(DynamicObjectTableDoesNotExist.class);
+        checkMetadata();
+        failOnError();
+    }
+    private void insert() {
+        session.makePersistent(instances);
+    }
+
+    private void find() {
+        TBasic instance = session.find(TBasic.class, 0);
+        validateInstance(instance);
+    }
+
+    private void lookup() {
+        QueryBuilder builder = session.getQueryBuilder();
+        QueryDomainType<TBasic> queryTBasic = builder.createQueryDefinition(TBasic.class);
+        queryTBasic.where(queryTBasic.get("magic").equal(queryTBasic.param("magic")));
+        Query<TBasic> query = session.createQuery(queryTBasic);
+        query.setParameter("magic", 1);
+        TBasic instance = query.getResultList().get(0);
+        validateInstance(instance);
+    }
+
+    private void query() {
+        QueryBuilder builder = session.getQueryBuilder();
+        QueryDomainType<TBasic> queryTBasic = builder.createQueryDefinition(TBasic.class);
+        queryTBasic.where(queryTBasic.get("name").equal(queryTBasic.param("name")));
+        Query<TBasic> query = session.createQuery(queryTBasic);
+        query.setParameter("name", "2");
+        TBasic instance = query.getResultList().get(0);
+        validateInstance(instance);
+    }
+
+    private void validateInstance(TBasic instance) {
+        int id = (Integer)instance.get(0);
+        errorIfNotEqual("validate name", String.valueOf(id), instance.get(1)); // name
+        errorIfNotEqual("validate age", id, instance.get(2)); // age
+        errorIfNotEqual("validate magic", id, instance.get(3)); // magic
+    }
+    
+    private void badClass(Class<?> cls) {
+        try {
+            session.newInstance(cls);
+        } catch (ClusterJUserException e) {
+            // good catch
+        } catch (Throwable t) {
+            error(cls.getClass().getName() + " threw wrong exception: " + t.getMessage());
+        }
+    }
+
+    public static class DynamicObjectProtectedConstructor extends DynamicObject {
+        protected DynamicObjectProtectedConstructor() {}
+        @Override
+        public String tableName() {
+            return "DynamicObjectProtectedConstructor";
+        }        
+    }
+
+    private static class DynamicObjectPrivate extends DynamicObject {
+        public DynamicObjectPrivate() {}
+        @Override
+        public String tableName() {
+            return "DynamicObjectProtectedConstructor";
+        }        
+    }
+
+    public class DynamicObjectNonStatic extends DynamicObject {
+        public DynamicObjectNonStatic() {}
+        @Override
+        public String tableName() {
+            return "DynamicObjectProtectedConstructor";
+        }        
+    }
+
+    public static class DynamicObjectPrivateConstructor extends DynamicObject {
+        private DynamicObjectPrivateConstructor() {}
+        @Override
+        public String tableName() {
+            return "DynamicObjectPrivateConstructor";
+        }        
+    }
+
+    public static class DynamicObjectTableDoesNotExist extends DynamicObject {
+        private DynamicObjectTableDoesNotExist() {}
+        @Override
+        public String tableName() {
+            return "DynamicObjectTableDoesNotExist";
+        }        
+    }
+
+    public static class DynamicObjectNullTableName extends DynamicObject {
+        public DynamicObjectNullTableName() {}
+        @Override
+        public String tableName() {
+            return null;
+        }        
+    }
+
+    protected void checkMetadata() {
+        ColumnMetadata[] metadata = tbasic.columnMetadata();
+        for (int i = 0; i < metadata.length; ++i) {
+            errorIfNotEqual("t_basic column " + i + " name", expectedTBasicNames[i], metadata[i].name());
+            errorIfNotEqual("t_basic column " + i + " type", expectedTBasicTypes[i], metadata[i].type());
+            errorIfNotEqual("t_basic column " + i + " javaType", expectedTBasicJavaTypes[i], metadata[i].javaType());
+            errorIfNotEqual("t_basic column " + i + " maximumLength", expectedTBasicMaximumLengths[i], metadata[i].maximumLength());
+            errorIfNotEqual("t_basic column " + i + " charsetName", expectedTBasicCharsetNames [i], metadata[i].charsetName());
+            errorIfNotEqual("t_basic column " + i + " number", expectedTBasicNumbers[i], metadata[i].number());
+            errorIfNotEqual("t_basic column " + i + " isPrimaryKey", expectedTBasicIsPrimaryKeys[i], metadata[i].isPrimaryKey());
+            errorIfNotEqual("t_basic column " + i + " isPartitionKey", expectedTBasicIsPartitionKeys[i], metadata[i].isPartitionKey());
+            errorIfNotEqual("t_basic column " + i + " precision", expectedTBasicPrecisions[i], metadata[i].precision());
+            errorIfNotEqual("t_basic column " + i + " scale", expectedTBasicScales[i], metadata[i].scale());
+            errorIfNotEqual("t_basic column " + i + " nullable", expectedTBasicNullables[i], metadata[i].nullable());
+        }
+    }
+
+
+}

=== modified file 'storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ColumnImpl.java'
--- a/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ColumnImpl.java	2010-09-13 10:48:19 +0000
+++ b/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ColumnImpl.java	2010-12-21 00:52:28 +0000
@@ -24,6 +24,8 @@ import com.mysql.ndbjtie.ndbapi.NdbDicti
 import com.mysql.clusterj.ClusterJDatastoreException;
 import com.mysql.clusterj.ClusterJFatalInternalException;
 
+import com.mysql.clusterj.ColumnMetadata.Type;
+
 import com.mysql.clusterj.core.store.Column;
 
 import com.mysql.clusterj.core.util.I18NHelper;
@@ -56,7 +58,7 @@ class ColumnImpl implements Column {
     private int charsetNumber = 0;
 
     /** The ndb column type for the column */
-    private Column.Type columnType;
+    private Type columnType;
 
     /** The prefix length for variable size columns */
     private int prefixLength = -1;
@@ -76,6 +78,9 @@ class ColumnImpl implements Column {
     /** Is this column a primary key column? */
     private boolean primaryKey;
 
+    /** Is this column a partition key column? */
+    private boolean partitionKey;
+
     private int length;
 
     private int inlineSize;
@@ -95,6 +100,7 @@ class ColumnImpl implements Column {
         int ndbType = ndbColumn.getType();
         this.columnType = convertType(ndbType);
         this.primaryKey = ndbColumn.getPrimaryKey();
+        this.partitionKey = ndbColumn.getPartitionKey();
         this.nullable = ndbColumn.getNullable();
         this.length = ndbColumn.getLength();
         this.inlineSize = ndbColumn.getInlineSize();
@@ -235,43 +241,43 @@ class ColumnImpl implements Column {
         }
     }
 
-    public Column.Type getType() {
+    public Type getType() {
         return columnType;
     }
 
     private Type convertType(int type) {
         switch (type) {
-            case ColumnConst.Type.Bigint: return Column.Type.Bigint;
-            case ColumnConst.Type.Bigunsigned: return Column.Type.Bigunsigned;
-            case ColumnConst.Type.Binary: return Column.Type.Binary;
-            case ColumnConst.Type.Bit: return Column.Type.Bit;
-            case ColumnConst.Type.Blob: return Column.Type.Blob;
-            case ColumnConst.Type.Char: return Column.Type.Char;
-            case ColumnConst.Type.Date: return Column.Type.Date;
-            case ColumnConst.Type.Datetime: return Column.Type.Datetime;
-            case ColumnConst.Type.Decimal: return Column.Type.Decimal;
-            case ColumnConst.Type.Decimalunsigned: return Column.Type.Decimalunsigned;
-            case ColumnConst.Type.Double: return Column.Type.Double;
-            case ColumnConst.Type.Float: return Column.Type.Float;
-            case ColumnConst.Type.Int: return Column.Type.Int;
-            case ColumnConst.Type.Longvarbinary: return Column.Type.Longvarbinary;
-            case ColumnConst.Type.Longvarchar: return Column.Type.Longvarchar;
-            case ColumnConst.Type.Mediumint: return Column.Type.Mediumint;
-            case ColumnConst.Type.Mediumunsigned: return Column.Type.Mediumunsigned;
-            case ColumnConst.Type.Olddecimal: return Column.Type.Olddecimal;
-            case ColumnConst.Type.Olddecimalunsigned: return Column.Type.Olddecimalunsigned;
-            case ColumnConst.Type.Smallint: return Column.Type.Smallint;
-            case ColumnConst.Type.Smallunsigned: return Column.Type.Smallunsigned;
-            case ColumnConst.Type.Text: return Column.Type.Text;
-            case ColumnConst.Type.Time: return Column.Type.Time;
-            case ColumnConst.Type.Timestamp: return Column.Type.Timestamp;
-            case ColumnConst.Type.Tinyint: return Column.Type.Tinyint;
-            case ColumnConst.Type.Tinyunsigned: return Column.Type.Tinyunsigned;
-            case ColumnConst.Type.Undefined: return Column.Type.Undefined;
-            case ColumnConst.Type.Unsigned: return Column.Type.Unsigned;
-            case ColumnConst.Type.Varbinary: return Column.Type.Varbinary;
-            case ColumnConst.Type.Varchar: return Column.Type.Varchar;
-            case ColumnConst.Type.Year: return Column.Type.Year;
+            case ColumnConst.Type.Bigint: return Type.Bigint;
+            case ColumnConst.Type.Bigunsigned: return Type.Bigunsigned;
+            case ColumnConst.Type.Binary: return Type.Binary;
+            case ColumnConst.Type.Bit: return Type.Bit;
+            case ColumnConst.Type.Blob: return Type.Blob;
+            case ColumnConst.Type.Char: return Type.Char;
+            case ColumnConst.Type.Date: return Type.Date;
+            case ColumnConst.Type.Datetime: return Type.Datetime;
+            case ColumnConst.Type.Decimal: return Type.Decimal;
+            case ColumnConst.Type.Decimalunsigned: return Type.Decimalunsigned;
+            case ColumnConst.Type.Double: return Type.Double;
+            case ColumnConst.Type.Float: return Type.Float;
+            case ColumnConst.Type.Int: return Type.Int;
+            case ColumnConst.Type.Longvarbinary: return Type.Longvarbinary;
+            case ColumnConst.Type.Longvarchar: return Type.Longvarchar;
+            case ColumnConst.Type.Mediumint: return Type.Mediumint;
+            case ColumnConst.Type.Mediumunsigned: return Type.Mediumunsigned;
+            case ColumnConst.Type.Olddecimal: return Type.Olddecimal;
+            case ColumnConst.Type.Olddecimalunsigned: return Type.Olddecimalunsigned;
+            case ColumnConst.Type.Smallint: return Type.Smallint;
+            case ColumnConst.Type.Smallunsigned: return Type.Smallunsigned;
+            case ColumnConst.Type.Text: return Type.Text;
+            case ColumnConst.Type.Time: return Type.Time;
+            case ColumnConst.Type.Timestamp: return Type.Timestamp;
+            case ColumnConst.Type.Tinyint: return Type.Tinyint;
+            case ColumnConst.Type.Tinyunsigned: return Type.Tinyunsigned;
+            case ColumnConst.Type.Undefined: return Type.Undefined;
+            case ColumnConst.Type.Unsigned: return Type.Unsigned;
+            case ColumnConst.Type.Varbinary: return Type.Varbinary;
+            case ColumnConst.Type.Varchar: return Type.Varchar;
+            case ColumnConst.Type.Year: return Type.Year;
             default: throw new ClusterJFatalInternalException(
                     local.message("ERR_Unknown_Column_Type",
                     tableName, columnName, type));
@@ -290,6 +296,14 @@ class ColumnImpl implements Column {
         return primaryKey;
     }
 
+    public boolean isPartitionKey() {
+        return partitionKey;
+    }
+
+    public int getLength() {
+        return length;
+    }
+
     public int getPrefixLength() {
         if (prefixLength != -1) {
             return prefixLength;

=== added file 'storage/ndb/clusterj/clusterj-tie/src/test/java/testsuite/clusterj/tie/DynamicObjectTest.java'
--- a/storage/ndb/clusterj/clusterj-tie/src/test/java/testsuite/clusterj/tie/DynamicObjectTest.java	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/clusterj/clusterj-tie/src/test/java/testsuite/clusterj/tie/DynamicObjectTest.java	2010-12-21 00:52:28 +0000
@@ -0,0 +1,23 @@
+/*
+   Copyright (C) 2009 Sun Microsystems Inc.
+   All rights reserved. Use is subject to license terms.
+
+   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 DynamicObjectTest extends testsuite.clusterj.DynamicObjectTest {
+
+}


Attachment: [text/bzr-bundle] bzr/craig.russell@oracle.com-20101221005228-5zrdu3fqziu6w4sk.bundle
Thread
bzr push into mysql-5.1-telco-7.1 branch (Craig.Russell:4028 to 4029)Craig L Russell21 Dec