You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by rh...@apache.org on 2012/10/01 14:49:56 UTC

svn commit: r1392307 - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/services/loader/ engine/org/apache/derby/impl/sql/compile/ engine/org/apache/derby/loc/ testing/org/apache/derbyTesting/functionTests/tests/lang/

Author: rhillegas
Date: Mon Oct  1 12:49:55 2012
New Revision: 1392307

URL: http://svn.apache.org/viewvc?rev=1392307&view=rev
Log:
DERBY-672: Allow binding of user-defined aggregates to generic classes.

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/loader/ClassInspector.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/loader/Java5ClassInspector.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/AggregateNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/UserAggregateDefinition.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ValueNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UserDefinedAggregatesTest.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/loader/ClassInspector.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/loader/ClassInspector.java?rev=1392307&r1=1392306&r2=1392307&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/loader/ClassInspector.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/loader/ClassInspector.java Mon Oct  1 12:49:55 2012
@@ -484,15 +484,11 @@ public class ClassInspector
 	}
 
 	/**
-	 * Given an implementation of a parameterized class/interface, return
-     * the actual concrete types of the parameters. Types are returned in the
-     * order that they are declared by the parameterized class/interface.
-     * So for instance, if the parameterized class is Map<K,V> and the
-     * implementation is HashMap<Integer,String>, then the return value is
-     * [ Integer.class, String.class ]. This method raises an exception if the
+	 * Given an implementation of a parameterized interface, return
+     * the bounds on the type variables. This method raises an exception if the
      * JVM does not support generics. May return null if type resolution fails.
 	 */
-	public Class[] getGenericParameterTypes( Class parameterizedType, Class implementation )
+	public Class[][] getTypeBounds( Class parameterizedInterface, Class implementation )
         throws StandardException
 	{
 		throw StandardException.newException( SQLState.VM_LEVEL_TOO_LOW, "Java 5" );

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/loader/Java5ClassInspector.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/loader/Java5ClassInspector.java?rev=1392307&r1=1392306&r2=1392307&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/loader/Java5ClassInspector.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/loader/Java5ClassInspector.java Mon Oct  1 12:49:55 2012
@@ -66,150 +66,88 @@ public class Java5ClassInspector extends
     //
     ///////////////////////////////////////////////////////////////////////////////////
 
-    @Override
-    public Class[] getGenericParameterTypes( Class parameterizedType, Class implementation )
-        throws StandardException
-	{
-        // construct the inheritance chain stretching from the parameterized type
-        // down to the concrete implemention
-        ArrayList<Class<?>>    chain = getTypeChain( parameterizedType, implementation );
-
-        // walk the chain, filling in a map of generic types to their resolved types
-        HashMap<Type,Type>  resolvedTypes = getResolvedTypes( chain );
-
-        // compose the resolved types together in order to compute the actual
-        // classes which are plugged into the variables of the parameterized type
-        ArrayList<Class<?>>    parameterTypes = getParameterTypes( parameterizedType, resolvedTypes );
-
-        // turn the list into an array
-        if ( parameterTypes == null ) { return null; }
-
-        Class[] result = new Class[ parameterTypes.size() ];
-        parameterTypes.toArray( result );
-
-        return result;
-    }
-
     /**
-     * Construct an inheritance chain of types stretching from a supertype down
-     * to a concrete implementation.
+     * Get the bounds for the type variables of a parameterized interface
+     * as declared for an implementing class. A given set of type bounds could
+     * be null if we're confused.
      */
-    private ArrayList<Class<?>>    getTypeChain( Class<?> chainEnd, Class<?> start )
+    @Override
+	public Class[][] getTypeBounds( Class parameterizedInterface, Class implementation )
+        throws StandardException
     {
-        ArrayList<Class<?>>    result = null;
+        if ( implementation == null ) { return null; }
         
-        if ( start == null ) { return null; }
-        if ( !chainEnd.isAssignableFrom(  start ) ) { return null; }
-
-        if ( start == chainEnd )    { result = new ArrayList<Class<?>>(); }
-        if ( result == null )
+        Type[]  genericInterfaces = implementation.getGenericInterfaces();
+        for ( Type genericInterface : genericInterfaces )
         {
-            result = getTypeChain( chainEnd, start.getSuperclass() );
-        
-            if ( result == null )
+            //
+            // Look for the generic interface whose raw type is the parameterized interface
+            // we're interested in.
+            //
+            if ( genericInterface instanceof ParameterizedType )
             {
-                for ( Class<?> iface : start.getInterfaces() )
+                ParameterizedType   pt = (ParameterizedType) genericInterface;
+                Type    rawType = pt.getRawType();
+
+                // found it!
+                if ( parameterizedInterface == rawType )
                 {
-                    result = getTypeChain( chainEnd, iface );
-                    if ( result != null ) { break; }
+                    return findTypeBounds( pt );
                 }
             }
         }
 
-        if ( result != null ) { result.add( start ); }
-
-        return result;
+        // couldn't find the interface we're looking for. check our superclass.
+        return getTypeBounds( parameterizedInterface, implementation.getSuperclass() );
     }
 
     /**
-     * Given an inheritance chain of types, stretching from a superclass down
-     * to a terminal concrete class, construct a map of generic types to their
-     * resolved types.
+     * Get the type bounds for all of the type variables of the given
+     * parameterized type.
      */
-    private HashMap<Type,Type>  getResolvedTypes( ArrayList<Class<?>> chain )
+    private Class[][]   findTypeBounds( ParameterizedType pt )
     {
-        if ( chain ==  null ) { return null; }
-        
-        HashMap<Type,Type>  resolvedTypes = new HashMap<Type,Type>();
+        Type[]  actualTypeArguments = pt.getActualTypeArguments();
+        int     argCount = actualTypeArguments.length;
+        Class[][]   retval = new Class[ argCount ][];
 
-        for ( Class<?> klass : chain )
-        {
-            addResolvedTypes( resolvedTypes, klass.getGenericSuperclass() );
+        for ( int i = 0; i < argCount; i++ ) { retval[ i ] = boundType( actualTypeArguments[ i ] ); }
 
-            for ( Type iface : klass.getGenericInterfaces() )
-            {
-                addResolvedTypes( resolvedTypes, iface );
-            }
-        }
-
-        return resolvedTypes;
+        return retval;
     }
 
     /**
-     * Given a generic type, add its parameter types to an evolving
-     * map of resolved types. Some of the resolved types may be
-     * generic type variables which will need further resolution from
-     * other generic types.
+     * Get the bounds for a single type variable.
      */
-    private void    addResolvedTypes
-        ( HashMap<Type,Type> resolvedTypes, Type genericType )
+    private Class[]    boundType( Type type )
     {
-        if ( genericType == null ) { return; }
-
-        if ( genericType instanceof ParameterizedType )
+        if ( type instanceof Class )
         {
-            ParameterizedType   pt = (ParameterizedType) genericType;
-            Class       rawType = (Class) pt.getRawType();
-            
-            Type[] actualTypeArguments = pt.getActualTypeArguments();
-            TypeVariable[] typeParameters = rawType.getTypeParameters();
-            for (int i = 0; i < actualTypeArguments.length; i++)
-            {
-                resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
-            }
+            return new Class[] { (Class) type };
         }
-    }
+        else if ( type instanceof TypeVariable )
+        {
+            Type[]  bounds = ((TypeVariable) type).getBounds();
+            int     count = bounds.length;
+            Class[] retval = new Class[ count ];
 
-    /**
-     * Given a map of resolved types, compose them together in order
-     * to resolve the actual concrete types that are plugged into the
-     * parameterized type.
-     */
-    private ArrayList<Class<?>>    getParameterTypes
-        ( Class<?> parameterizedType, HashMap<Type,Type> resolvedTypes )
-    {
-        if ( resolvedTypes == null ) { return null; }
-        
-        Type[] actualTypeArguments = parameterizedType.getTypeParameters();
+            for ( int i = 0; i < count; i++ ) { retval[ i ] = getRawType( bounds[ i ] ); }
 
-        ArrayList<Class<?>> result = new ArrayList<Class<?>>();
-        
-        // resolve types by composing type variables.
-        for (Type baseType: actualTypeArguments)
-        {
-            while ( resolvedTypes.containsKey( baseType ) )
-            {
-                baseType = resolvedTypes.get(baseType);
-            }
-            
-            result.add( getClass( baseType ) );
+            return retval;
         }
-        
-        return result;
+        else { return null; }
     }
 
     /**
-     * Get the underlying class for a type, or null if the type is a variable type.
+     * Get the raw type of a type bound.
      */
-    private Class<?> getClass( Type type )
+    private Class   getRawType( Type bound )
     {
-        if ( type instanceof Class ) { return (Class) type; }
-        else if (type instanceof ParameterizedType)
-        {
-            return getClass( ((ParameterizedType) type).getRawType() );
-        }
+        if ( bound instanceof Class ) { return (Class) bound; }
+        else if ( bound instanceof ParameterizedType ) { return getRawType( ((ParameterizedType) bound).getRawType() ); }
         else { return null; }
     }
 
+
 }
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/AggregateNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/AggregateNode.java?rev=1392307&r1=1392306&r2=1392307&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/AggregateNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/AggregateNode.java Mon Oct  1 12:49:55 2012
@@ -415,9 +415,12 @@ public class AggregateNode extends Unary
 
 		if (resultType == null)
 		{
-			throw StandardException.newException(SQLState.LANG_USER_AGGREGATE_BAD_TYPE, 
-						aggregateName, 
-						operand.getTypeId().getSQLTypeName());
+			throw StandardException.newException
+                (
+                 SQLState.LANG_USER_AGGREGATE_BAD_TYPE, 
+                 getSQLName(), 
+                 operand.getTypeId().getSQLTypeName()
+                 );
 		}
 
 		checkAggregatorClassName(aggregatorClassName.toString());
@@ -699,4 +702,15 @@ public class AggregateNode extends Unary
 	{
 		return false;
 	}
+
+    /** Get the SQL name of the aggregate */
+    public  String  getSQLName()
+        throws StandardException
+    {
+        if ( uad instanceof UserAggregateDefinition )
+        {
+            return ((UserAggregateDefinition) uad).getAliasDescriptor().getQualifiedName();
+        }
+        else { return aggregateName; }
+    }
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/UserAggregateDefinition.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/UserAggregateDefinition.java?rev=1392307&r1=1392306&r2=1392307&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/UserAggregateDefinition.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/UserAggregateDefinition.java Mon Oct  1 12:49:55 2012
@@ -35,9 +35,6 @@ import org.apache.derby.iapi.types.TypeI
 import org.apache.derby.iapi.types.JSQLType;
 import org.apache.derby.iapi.types.DataTypeDescriptor;
 
-import org.apache.derby.iapi.sql.compile.TypeCompiler;
-import org.apache.derby.iapi.sql.compile.TypeCompilerFactory;
-
 import org.apache.derby.iapi.sql.compile.CompilerContext;
 import org.apache.derby.iapi.sql.dictionary.AliasDescriptor;
 
@@ -107,69 +104,83 @@ public class UserAggregateDefinition imp
 	{
 		try
 		{
-			TypeId compType = inputType.getTypeId();
-		
 			CompilerContext cc = (CompilerContext)
 				ContextService.getContext(CompilerContext.CONTEXT_ID);
-			TypeCompilerFactory tcf = cc.getTypeCompilerFactory();
-			TypeCompiler tc = tcf.getTypeCompiler(compType);
             ClassFactory    classFactory = cc.getClassFactory();
 
             Class   userAggregatorClass = classFactory.loadApplicationClass( _alias.getJavaClassName() );
             Class   derbyAggregatorInterface = classFactory.loadApplicationClass( "org.apache.derby.agg.Aggregator" );
 
-            Class[] aggregatorTypes = classFactory.getClassInspector().getGenericParameterTypes
+            Class[][]   typeBounds = classFactory.getClassInspector().getTypeBounds
                 ( derbyAggregatorInterface, userAggregatorClass );
 
             if (
-                !derbyAggregatorInterface.isAssignableFrom( userAggregatorClass ) ||
-                (aggregatorTypes == null) ||
-                (aggregatorTypes.length != AGGREGATOR_PARAM_COUNT) ||
-                (aggregatorTypes[ INPUT_TYPE ] == null) ||
-                (aggregatorTypes[ RETURN_TYPE ] == null)
-               )
+                (typeBounds == null) ||
+                (typeBounds.length != AGGREGATOR_PARAM_COUNT) ||
+                (typeBounds[ INPUT_TYPE ] == null) ||
+                (typeBounds[ RETURN_TYPE ] == null)
+                )
             {
-				throw StandardException.newException
+                throw StandardException.newException
                     (
                      SQLState.LANG_ILLEGAL_UDA_CLASS,
                      _alias.getSchemaName(),
                      _alias.getName(),
-                     _alias.getJavaClassName()
+                     userAggregatorClass.getName()
                      );
             }
 
-            Class   actualInputClass = aggregatorTypes[ INPUT_TYPE ];
-            Class   actualReturnClass = aggregatorTypes[ RETURN_TYPE ];
-
             AggregateAliasInfo  aai = (AggregateAliasInfo) _alias.getAliasInfo();
             DataTypeDescriptor  expectedInputType = DataTypeDescriptor.getType( aai.getForType() );
             DataTypeDescriptor  expectedReturnType = DataTypeDescriptor.getType( aai.getReturnType() );
             Class       expectedInputClass = getJavaClass( expectedInputType );
             Class       expectedReturnClass = getJavaClass( expectedReturnType );
 
-            // check that the aggregator has the correct input and return types
-            if ( actualInputClass != expectedInputClass )
+            // the input operand must be the expected input type of the aggregate
+            if ( !inputType.getTypeId().equals( expectedInputType.getTypeId() ) ) { return null; }
+            
+            //
+            // Make sure that the declared input type of the UDA actually falls within
+            // the type bounds of the Aggregator implementation.
+            //
+            Class[] inputBounds = typeBounds[ INPUT_TYPE ];
+            for ( int i = 0; i < inputBounds.length; i++ )
             {
-				throw StandardException.newException
-                    (
-                     SQLState.LANG_UDA_WRONG_INPUT_TYPE,
-                     _alias.getSchemaName(),
-                     _alias.getName(),
-                     expectedInputClass.toString(),
-                     actualInputClass.toString()
-                     );
+                Class   inputBound = inputBounds[ i ];
+                
+                if ( !inputBound.isAssignableFrom( expectedInputClass ) )
+                {
+                    throw StandardException.newException
+                        (
+                         SQLState.LANG_UDA_WRONG_INPUT_TYPE,
+                         _alias.getSchemaName(),
+                         _alias.getName(),
+                         expectedInputClass.toString(),
+                         inputBound.toString()
+                         );
+                }
             }
-		
-            if ( actualReturnClass != expectedReturnClass )
+
+            //
+            // Make sure that the declared return type of the UDA actually falls within
+            // the type bounds of the Aggregator implementation.
+            //
+            Class[] returnBounds = typeBounds[ RETURN_TYPE ];
+            for ( int i = 0; i < returnBounds.length; i++ )
             {
-				throw StandardException.newException
-                    (
-                     SQLState.LANG_UDA_WRONG_RETURN_TYPE,
-                     _alias.getSchemaName(),
-                     _alias.getName(),
-                     expectedReturnClass.toString(),
-                     actualReturnClass.toString()
+                Class   returnBound = returnBounds[ i ];
+                
+                if ( !returnBound.isAssignableFrom( expectedReturnClass ) )
+                {
+                    throw StandardException.newException
+                        (
+                         SQLState.LANG_UDA_WRONG_RETURN_TYPE,
+                         _alias.getSchemaName(),
+                         _alias.getName(),
+                         expectedReturnClass.toString(),
+                         returnBound.toString()
                      );
+                }
             }
 
             aggregatorClass.append( ClassName.UserDefinedAggregator );

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ValueNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ValueNode.java?rev=1392307&r1=1392306&r2=1392307&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ValueNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ValueNode.java Mon Oct  1 12:49:55 2012
@@ -442,7 +442,13 @@ public abstract class ValueNode extends 
 									C_NodeTypes.JAVA_TO_SQL_VALUE_NODE,
 									stjvn,
 									getContextManager());
-		jtsvn.setType(DataTypeDescriptor.getSQLDataTypeDescriptor(stjvn.getJavaTypeName()));
+
+        DataTypeDescriptor  resultType;
+        if ( (getTypeServices() != null) && getTypeId().userType() ) { resultType = getTypeServices(); }
+        else { resultType = DataTypeDescriptor.getSQLDataTypeDescriptor(stjvn.getJavaTypeName()); }
+
+		jtsvn.setType( resultType );
+
 		return jtsvn;
 	}
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml?rev=1392307&r1=1392306&r2=1392307&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml Mon Oct  1 12:49:55 2012
@@ -2979,7 +2979,7 @@ Guide.
 
             <msg>
                 <name>42ZC6</name>
-                <text>User defined aggregate '{0}'.'{1}' was declared to have this input Java type: '{2}'. However, the actual input Java type is '{3}'.</text>
+                <text>User defined aggregate '{0}'.'{1}' was declared to have this input Java type: '{2}'. This does not extend the following actual bounding input Java type: '{3}'.</text>
                 <arg>schemaName</arg>
                 <arg>aggregateName</arg>
                 <arg>javaDataType</arg>
@@ -2988,7 +2988,7 @@ Guide.
 
             <msg>
                 <name>42ZC7</name>
-                <text>User defined aggregate '{0}'.'{1}' was declared to have this return Java type: '{2}'. However, the actual return Java type is '{3}'.</text>
+                <text>User defined aggregate '{0}'.'{1}' was declared to have this return Java type: '{2}'. This does not extend the following actual bounding return Java type: '{3}'.</text>
                 <arg>schemaName</arg>
                 <arg>aggregateName</arg>
                 <arg>javaDataType</arg>

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UserDefinedAggregatesTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UserDefinedAggregatesTest.java?rev=1392307&r1=1392306&r2=1392307&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UserDefinedAggregatesTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UserDefinedAggregatesTest.java Mon Oct  1 12:49:55 2012
@@ -54,6 +54,7 @@ public class UserDefinedAggregatesTest  
     public static final String MISSING_SCHEMA = "42Y07";
     public static final String BAD_AGGREGATE_USAGE = "42903";
     public static final String BAD_ORDER_BY = "42Y35";
+    public static final String INPUT_MISMATCH = "42Y22";
 
     ///////////////////////////////////////////////////////////////////////////////////
     //
@@ -661,7 +662,7 @@ public class UserDefinedAggregatesTest  
              conn,
              "intMode",
              "int",
-             "IntMode",
+             "org.apache.derbyTesting.functionTests.tests.lang.GenericMode$IntMode",
              "( 1, 1 ), ( 1, 2 ), ( 1, 2 ), ( 1, 2 ), ( 2, 3 ), ( 2, 3 ), ( 2, 4 )",
              new String[][]
              {
@@ -688,7 +689,7 @@ public class UserDefinedAggregatesTest  
              conn,
              "varcharMode",
              "varchar( 5 )",
-             "StringMode",
+             "org.apache.derbyTesting.functionTests.tests.lang.GenericMode$StringMode",
              "( 1, 'a' ), ( 1, 'ab' ), ( 1, 'ab' ), ( 1, 'ab' ), ( 2, 'abc' ), ( 2, 'abc' ), ( 2, 'abcd' )",
              new String[][]
              {
@@ -716,7 +717,7 @@ public class UserDefinedAggregatesTest  
          Connection conn,
          String aggName,
          String sqlType,
-         String nestedClassName,
+         String externalName,
          String values,
          String[][] scalarResult,
          String[][] groupedResult,
@@ -729,7 +730,7 @@ public class UserDefinedAggregatesTest  
         
         goodStatement
             ( conn, "create derby aggregate " + aggName + " for " + sqlType + "\n" +
-              "external name 'org.apache.derbyTesting.functionTests.tests.lang.GenericMode$" + nestedClassName + "'" );
+              "external name '" + externalName + "'" );
         goodStatement( conn, "create table " + tableName + "( a int, b " + sqlType + " )" );
         goodStatement( conn, "insert into " + tableName + "( a, b ) values " + values );
 
@@ -749,21 +750,27 @@ public class UserDefinedAggregatesTest  
              false
              );
 
-        assertResults
-            (
-             conn,
-             "select " + aggName + "( distinct b ) from " + tableName,
-             distinctScalarResult,
-             false
-             );
-
-        assertResults
-            (
-             conn,
-             "select a, " + aggName + "( distinct b ) from " + tableName + " group by a",
-             distinctGroupedResult,
-             false
-             );
+        if ( distinctScalarResult != null )
+        {
+            assertResults
+                (
+                 conn,
+                 "select " + aggName + "( distinct b ) from " + tableName,
+                 distinctScalarResult,
+                 false
+                 );
+        }
+
+        if ( distinctGroupedResult != null )
+        {
+            assertResults
+                (
+                 conn,
+                 "select a, " + aggName + "( distinct b ) from " + tableName + " group by a",
+                 distinctGroupedResult,
+                 false
+                 );
+        }
     }
 
     /**
@@ -882,4 +889,133 @@ public class UserDefinedAggregatesTest  
              );
     }
 
+    /**
+     * <p>
+     * Test aggregates bound to generic classes.
+     * </p>
+     */
+    public void test_09_genericAggregates() throws Exception
+    {
+        Connection conn = getConnection();
+
+        vetParameterizedAggregate
+            (
+             conn,
+             "intMode_09",
+             "int",
+             "org.apache.derbyTesting.functionTests.tests.lang.GenericMode",
+             "( 1, 1 ), ( 1, 2 ), ( 1, 2 ), ( 1, 2 ), ( 2, 3 ), ( 2, 3 ), ( 2, 4 )",
+             new String[][]
+             {
+                 { "2" },
+             },
+             new String[][]
+             {
+                 { "1", "2" },
+                 { "2", "3" },
+             },
+             new String[][]
+             {
+                 { "4" },
+             },
+             new String[][]
+             {
+                 { "1", "2" },
+                 { "2", "4" },
+             }
+             );
+
+        vetParameterizedAggregate
+            (
+             conn,
+             "varcharMode_09",
+             "varchar( 5 )",
+             "org.apache.derbyTesting.functionTests.tests.lang.GenericMode",
+             "( 1, 'a' ), ( 1, 'ab' ), ( 1, 'ab' ), ( 1, 'ab' ), ( 2, 'abc' ), ( 2, 'abc' ), ( 2, 'abcd' )",
+             new String[][]
+             {
+                 { "ab" },
+             },
+             new String[][]
+             {
+                 { "1", "ab" },
+                 { "2", "abc" },
+             },
+             new String[][]
+             {
+                 { "abcd" },
+             },
+             new String[][]
+             {
+                 { "1", "ab" },
+                 { "2", "abcd" },
+             }
+             );
+
+        goodStatement
+            (
+             conn,
+             "create type FullName_09 external name 'org.apache.derbyTesting.functionTests.tests.lang.FullName' language java"
+             );
+        goodStatement
+            (
+             conn,
+             "create function makeFullName_09( firstName varchar( 32672 ), lastName varchar( 32672 ) )\n" +
+             "returns FullName_09 language java parameter style java\n" +
+             "external name 'org.apache.derbyTesting.functionTests.tests.lang.FullName.makeFullName'\n"
+             );
+        vetParameterizedAggregate
+            (
+             conn,
+             "fullNameMode_09",
+             "FullName_09",
+             "org.apache.derbyTesting.functionTests.tests.lang.GenericMode",
+             "( 1, makeFullName_09( 'one', 'name'  ) ),\n" +
+             "( 1, makeFullName_09( 'two', 'name' ) ),\n" +
+             "( 1, makeFullName_09( 'two', 'name' ) ),\n" +
+             "( 1, makeFullName_09( 'two', 'name' ) ),\n" +
+             "( 2, makeFullName_09( 'three', 'name' ) ),\n" +
+             "( 2, makeFullName_09( 'three', 'name' ) ),\n" +
+             "( 2, makeFullName_09( 'four', 'name' ) )\n",
+             new String[][]
+             {
+                 { "two name" },
+             },
+             new String[][]
+             {
+                 { "1", "two name" },
+                 { "2", "three name" },
+             },
+             null,
+             null
+             );
+    }
+
+    /**
+     * <p>
+     * Negative tests.
+     * </p>
+     */
+    public void test_10_negative() throws Exception
+    {
+        Connection conn = getConnection();
+
+        //
+        // Input operand must agree with input type of aggregate
+        //
+        goodStatement
+            (
+             conn,
+             "create derby aggregate intMode_10 for int\n" +
+             "external name 'org.apache.derbyTesting.functionTests.tests.lang.GenericMode$IntMode'\n"
+             );
+        goodStatement
+            (
+             conn,
+             "create table intMode_10_inputs( a int, b varchar( 10 ) )"
+             );
+        expectCompilationError( INPUT_MISMATCH, "select intMode_10( b ) from intMode_10_inputs" );
+
+    }
+
 }