db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rhille...@apache.org
Subject svn commit: r1420165 - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/services/loader/ engine/org/apache/derby/impl/sql/catalog/ engine/org/apache/derby/impl/sql/compile/ testing/org/apache/derbyTesting/functionTests/tests/lang/
Date Tue, 11 Dec 2012 13:59:15 GMT
Author: rhillegas
Date: Tue Dec 11 13:59:13 2012
New Revision: 1420165

URL: http://svn.apache.org/viewvc?rev=1420165&view=rev
Log:
DERBY-3069: Enable the execution of varargs routines.

Added:
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/VarargsRoutines.java
  (with props)
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/catalog/DataDictionaryImpl.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MethodCallNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/NewInvocationNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/StaticMethodCallNode.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/VarargsTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/build.xml

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=1420165&r1=1420164&r2=1420165&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
Tue Dec 11 13:59:13 2012
@@ -215,14 +215,18 @@ public class ClassInspector
 	 * @see	Member
 	 * @see Modifier
 	 */
-	public Member findPublicMethod(String receiverType,
-								String methodName,
-								String[] parmTypes,
-								String[] primParmTypes,
-								boolean[] isParam,
-								boolean staticMethod,
-								boolean repeatLastParameter)
-					throws ClassNotFoundException, StandardException
+	public Member findPublicMethod
+        (
+         String receiverType,
+         String methodName,
+         String[] parmTypes,
+         String[] primParmTypes,
+         boolean[] isParam,
+         boolean staticMethod,
+         boolean repeatLastParameter,
+         boolean hasVarargs
+         )
+        throws ClassNotFoundException, StandardException
 	{
 		Class receiverClass = getClass(receiverType);
 		if (receiverClass == null)
@@ -248,6 +252,10 @@ public class ClassInspector
 					}
 				}
 
+                // If the routine was declared to be varargs, then we eliminate
+                // all non-varargs methods from consideration
+                if ( hasVarargs && !isVarArgsMethod( methods[index] ) ) { continue;
}
+
 				if (methodName.equals(methods[index].getName())) {
 					// We found a match
 					return methods[index];
@@ -312,8 +320,12 @@ public class ClassInspector
 			}
 		}
 
-		return resolveMethod(receiverClass, methodName, paramClasses,
-						primParamClasses, isParam, staticMethod, repeatLastParameter, methodList);
+		return resolveMethod
+            (
+             receiverClass, methodName, paramClasses,
+             primParamClasses, isParam, staticMethod, repeatLastParameter, methodList,
+             hasVarargs
+             );
 	}
 
 
@@ -478,9 +490,13 @@ public class ClassInspector
 		}
 
 		// name is only used for debugging
-		return resolveMethod(receiverClass, "<init>", paramClasses, 
-							 primParamClasses, isParam, false, false,
-							 receiverClass.getConstructors());
+		return resolveMethod
+            (
+             receiverClass, "<init>", paramClasses, 
+             primParamClasses, isParam, false, false,
+             receiverClass.getConstructors(),
+             false
+             );
 	}
 
 	/**
@@ -495,6 +511,16 @@ public class ClassInspector
     }
     
 	/**
+	 * Return true if the method or constructor supports varargs.
+	 */
+	public boolean  isVarArgsMethod( Member member )
+	{
+        // Varargs were introduced by Java 5. So this 1.4 ClassInspector always
+        // return false;
+        return false;
+    }
+    
+	/**
 	 * Given an implementation of a parameterized interface, return
      * the actual types of the interface type variables. This method raises an exception
if the
      * JVM does not support generics. May return null or an array of nulls if type resolution
fails.
@@ -574,16 +600,19 @@ public class ClassInspector
 	 *  @return	the matched method
 	 *
 	 **/
-	private Member resolveMethod(
-				Class receiverClass,
-				String methodName,
-				Class[] paramClasses,
-				Class[] primParamClasses,
-				boolean[] isParam,
-				boolean staticMethod,
-				boolean repeatLastParameter,
-				Member[] methods)
-			throws StandardException
+	private Member resolveMethod
+        (
+         Class receiverClass,
+         String methodName,
+         Class[] paramClasses,
+         Class[] primParamClasses,
+         boolean[] isParam,
+         boolean staticMethod,
+         boolean repeatLastParameter,
+         Member[] methods,
+         boolean hasVarargs
+         )
+        throws StandardException
 	{
 
 		if (SanityManager.DEBUG) {
@@ -657,6 +686,13 @@ nextMethod:	for (int i = 0; i < methods.
 						continue;
 					}
 
+                    // If the routine was declared to be varargs, then we eliminate
+                    // all non-varargs methods from consideration
+					if ( hasVarargs && !isVarArgsMethod( currentMethod )) {
+						methods[i] = null; // remove non-applicable methods
+						continue;
+					}
+
 					/* Look only at methods with the right name */
 					if (!methodName.startsWith("<")) {
 						if ( ! methodName.equals(currentMethod.getName())) {

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=1420165&r1=1420164&r2=1420165&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
Tue Dec 11 13:59:13 2012
@@ -21,6 +21,9 @@
 
 package org.apache.derby.iapi.services.loader;
 
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
@@ -100,6 +103,15 @@ public class Java5ClassInspector extends
         // couldn't find the interface we're looking for. check our superclass.
         return getTypeBounds( parameterizedInterface, implementation.getSuperclass() );
     }
+    
+    @Override
+	public boolean  isVarArgsMethod( Member member )
+	{
+        if ( member instanceof Method ) { return ((Method) member).isVarArgs(); }
+        if ( member instanceof Constructor ) { return ((Constructor) member).isVarArgs();
}
+        else { return false; }
+    }
+    
 
     @Override
     public Class[] getGenericParameterTypes( Class parameterizedType, Class implementation
)

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java?rev=1420165&r1=1420164&r2=1420165&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
Tue Dec 11 13:59:13 2012
@@ -240,7 +240,8 @@ public final class	DataDictionaryImpl
     *[2]    = Java class
     *[3]    = method name and signature
     *[4]    = "true" or "false" depending on whether the function is DETERMINSTIC
-    *[5..N] = arguments (optional, if not present zero arguments is assumed)
+    *[5]    = "true" or "false" depending on whether the function has VARARGS
+    *[6..N] = arguments (optional, if not present zero arguments is assumed)
 	*
 	*/
 	private static final String[][] SYSFUN_FUNCTIONS = {
@@ -251,7 +252,7 @@ public final class	DataDictionaryImpl
         {"COS", "DOUBLE", "java.lang.StrictMath", "cos(double)",  "true", "false", "DOUBLE"
},
         {"SIN", "DOUBLE", "java.lang.StrictMath", "sin(double)",  "true", "false", "DOUBLE"
},
         {"TAN", "DOUBLE", "java.lang.StrictMath", "tan(double)",  "true", "false", "DOUBLE"
},
-        {"PI", "DOUBLE", "org.apache.derby.catalog.SystemProcedures", "PI()", "false", "true"
},
+        {"PI", "DOUBLE", "org.apache.derby.catalog.SystemProcedures", "PI()", "true", "false"
},
         {"DEGREES", "DOUBLE", "java.lang.StrictMath", "toDegrees(double)", "true", "false",
"DOUBLE" },
         {"RADIANS", "DOUBLE", "java.lang.StrictMath", "toRadians(double)",  "true", "false",
"DOUBLE" },
         {"LN", "DOUBLE", "java.lang.StrictMath", "log(double)",  "true", "false", "DOUBLE"
},

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MethodCallNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MethodCallNode.java?rev=1420165&r1=1420164&r2=1420165&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MethodCallNode.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MethodCallNode.java
Tue Dec 11 13:59:13 2012
@@ -23,6 +23,7 @@ package	org.apache.derby.impl.sql.compil
 
 import org.apache.derby.iapi.services.loader.ClassInspector;
 
+import org.apache.derby.iapi.services.compiler.LocalField;
 import org.apache.derby.iapi.services.compiler.MethodBuilder;
 
 import org.apache.derby.iapi.services.sanity.SanityManager;
@@ -55,6 +56,8 @@ import org.apache.derby.iapi.util.JBitSe
 import org.apache.derby.catalog.types.RoutineAliasInfo;
 
 import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 
 import java.sql.ResultSet;
 import java.util.Enumeration;
@@ -512,6 +515,12 @@ abstract class MethodCallNode extends Ja
 		return this;
 	}
 
+    /** Return true if the routine has varargs */
+    public  boolean hasVarargs()
+    {
+        return (routineInfo == null ) ? false : routineInfo.hasVarargs();
+    }   
+
 	/**
 	 * Generate the parameters to the given method call
 	 *
@@ -529,56 +538,130 @@ abstract class MethodCallNode extends Ja
 	{
 		int				param;
 
-		String[] expectedTypes = methodParameterTypes;
-
-		ClassInspector classInspector = getClassFactory().getClassInspector();
+        int                 nonVarargCount = hasVarargs() ? routineInfo.getParameterCount()
- 1 : methodParms.length;
+        int                 totalArgCount = hasVarargs() ? nonVarargCount + 1 : nonVarargCount;
 
 		/* Generate the code for each user parameter, generating the appropriate
 		 * cast when the passed type needs to get widened to the expected type.
 		 */
-		for (param = 0; param < methodParms.length; param++)
+		for ( param = 0; param < nonVarargCount; param++ )
 		{
-			generateOneParameter( acb, mb, param );
+            generateAndCastOneParameter( acb, mb, param, methodParameterTypes[ param ] );
+		}
 
-			// type from the SQL-J expression
-			String argumentType = getParameterTypeName( methodParms[param] );
+        if ( hasVarargs() ) { generateVarargs( acb, mb ); }
 
-			// type of the method
-			String parameterType = expectedTypes[param];
+		return totalArgCount;
+	}
 
-			if (!parameterType.equals(argumentType))
-			{
-				// since we reached here through method resolution
-				// casts are only required for primitive types.
-				// In any other case the expression type must be assignable
-				// to the parameter type.
-				if (ClassInspector.primitiveType(parameterType)) {
-					mb.cast(parameterType);
-				} else {
-
-					// for a prodcedure
-					if (routineInfo != null) {
-						continue; // probably should be only for INOUT/OUT parameters.
-					}
+    /**
+     * <p>
+     * Generate and cast one parameter, pushing the result onto the stack.
+     * </p>
+     */
+    private void    generateAndCastOneParameter
+        ( ExpressionClassBuilder acb, MethodBuilder mb, int param, String parameterType )
+        throws StandardException
+    {
+		ClassInspector classInspector = getClassFactory().getClassInspector();
 
-					if (SanityManager.DEBUG) {
-						SanityManager.ASSERT(classInspector.assignableTo(argumentType, parameterType),
-							"Argument type " + argumentType + " is not assignable to parameter " + parameterType);
-					}
+        generateOneParameter( acb, mb, param );
 
-					/*
-					** Set the parameter type in case the argument type is narrower
-					** than the parameter type.
-					*/
-					mb.upCast(parameterType);
+        // type from the SQL-J expression
+        String argumentType = getParameterTypeName( methodParms[param] );
 
-				}
-			}
+        if (!parameterType.equals(argumentType))
+        {
+            // since we reached here through method resolution
+            // casts are only required for primitive types.
+            // In any other case the expression type must be assignable
+            // to the parameter type.
+            if (ClassInspector.primitiveType(parameterType))
+            {
+                mb.cast(parameterType);
+            } else
+            {
+                // for a procedure
+                if (routineInfo != null)
+                {
+                    return; // probably should be only for INOUT/OUT parameters.
+                }
 
-		}
+                if (SanityManager.DEBUG)
+                {
+                    SanityManager.ASSERT(classInspector.assignableTo(argumentType, parameterType),
+                                         "Argument type " + argumentType + " is not assignable
to parameter " + parameterType);
+                }
 
-		return methodParms.length;
-	}
+                /*
+                ** Set the parameter type in case the argument type is narrower
+                ** than the parameter type.
+                */
+                mb.upCast(parameterType);
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Generate the trailing routine arguments into a varargs array and
+     * push that array onto the stack.
+     * </p>
+     */
+    private void    generateVarargs
+        ( ExpressionClassBuilder acb, MethodBuilder mb )
+        throws StandardException
+    {
+        // the vararg is the last declared arg of the Java method. it is always
+        // an array type. right now we only support vararg static methods.
+        // if we have to support vararg constructors in the future, then this code
+        // will need adjustment.
+        Class[]     parameterTypes = ((Method) method).getParameterTypes();
+        int         firstVarargIdx = parameterTypes.length - 1;
+        Class       varargType = parameterTypes[ firstVarargIdx ].getComponentType();
+        
+        int         varargCount = methodParms.length - firstVarargIdx;
+        if ( varargCount < 0 ) { varargCount = 0; }
+
+        // allocate an array to hold the varargs
+		LocalField arrayField = acb.newFieldDeclaration( Modifier.PRIVATE, varargType.getName()
+ "[]" );
+		MethodBuilder cb = acb.getConstructor();
+		cb.pushNewArray( varargType.getName(), varargCount );
+		cb.setField( arrayField );
+
+        // now put the arguments into the array
+        for ( int i = 0; i < varargCount; i++ )
+        {
+			mb.getField( arrayField ); // push the array onto the stack
+            // evaluate the parameter and push it onto the stack
+            generateAndCastOneParameter( acb, mb, i + firstVarargIdx, methodParameterTypes[
firstVarargIdx ] );
+            mb.setArrayElement( i ); // move the parameter into the array, pop the stack
+        }
+        
+        // push the array onto the stack. it is the last parameter to the varargs routine.
+        mb.getField( arrayField );
+    }
+
+    /**
+     * <p>
+     * Get the offset into the routine arguments corresponding to the index
+     * of the invocation parameter. The two indexes may be different in the case of
+     * varargs methods. There may be more invocation args than declared routine args.
+     * For a varargs routine, all of the trailing invocation parameters correspond to the
+     * last argument declared by the CREATE FUNCTION/PROCEDURE statement.
+     * </p>
+     */
+    protected   int getRoutineArgIdx( int invocationArgIdx )
+    {
+        if ( routineInfo == null ) { return invocationArgIdx; }
+        if ( !routineInfo.hasVarargs() ) { return invocationArgIdx; }
+
+        // ok, this is a varargs routine
+        int         firstVarargIdx = routineInfo.getParameterCount() - 1;
+
+        return (firstVarargIdx < invocationArgIdx) ? firstVarargIdx : invocationArgIdx;
+    }
+    
 
 	static	public	String	getParameterTypeName( JavaValueNode param )
 		throws StandardException
@@ -642,9 +725,12 @@ abstract class MethodCallNode extends Ja
 		}
 	}
 
-	protected void resolveMethodCall(String javaClassName,
-									 boolean staticMethod) 
-				throws StandardException
+	protected void resolveMethodCall
+        (
+         String javaClassName,
+         boolean staticMethod
+         ) 
+        throws StandardException
 	{
 		// only allow direct method calls through routines and internal SQL.
 		if (routineInfo == null && !internalCall)
@@ -664,63 +750,77 @@ abstract class MethodCallNode extends Ja
 		String[]		primParmTypeNames = null;
 		boolean[]		isParam = getIsParam();
 
-		boolean hasDynamicResultSets = (routineInfo != null) && (count != 0) &&
(count != methodParms.length);
+		boolean hasDynamicResultSets = hasVarargs() ?
+            false :
+            (routineInfo != null) && (count != 0) && (count != methodParms.length);
 
         /*
         ** Find the matching method that is public.
         */
-
-        	int signatureOffset = methodName.indexOf('(');
+        int signatureOffset = methodName.indexOf('(');
         	
-            // support Java signatures by checking if the method name contains a '('
-            if (signatureOffset != -1) {
-               	parmTypeNames = parseValidateSignature(methodName, signatureOffset, hasDynamicResultSets);
-               methodName = methodName.substring(0, signatureOffset);
-               
-               // If the signature is specified then Derby resolves to exactly
-               // that method. Setting this flag to false disables the method
-               // resolution from automatically optionally repeating the last
-               // parameter as needed.
-               hasDynamicResultSets = false;
-              	 
-            }
-            else
-            {
-            	parmTypeNames = getObjectSignature();
-            }
+        // support Java signatures by checking if the method name contains a '('
+        if (signatureOffset != -1) {
+            parmTypeNames = parseValidateSignature(methodName, signatureOffset, hasDynamicResultSets);
+            methodName = methodName.substring(0, signatureOffset);
+            
+            // If the signature is specified then Derby resolves to exactly
+            // that method. Setting this flag to false disables the method
+            // resolution from automatically optionally repeating the last
+            // parameter as needed.
+            hasDynamicResultSets = false;
+        }
+        else
+        {
+            parmTypeNames = getObjectSignature();
+        }
+
+        // the actual type of the trailing Java varargs arg is an array
+        if ( hasVarargs() )
+        {
+            parmTypeNames[ count - 1 ] = parmTypeNames[ count - 1 ] + "[]";
+        }
+
         try
-        {                      	
-                method = classInspector.findPublicMethod(javaClassName,
-                                                    methodName,
-                                                    parmTypeNames,
-                                                    null,
-                                                    isParam,
-                                                    staticMethod,
-                                                    hasDynamicResultSets);
-
-
-                // DB2 LUW does not support Java object types for SMALLINT, INTEGER, BIGINT,
REAL, DOUBLE
-                // and these are the only types that can map to a primitive or an object
type according
-                // to SQL part 13. So we never have a second chance match.
-                // Also if the DDL specified a signature, then no alternate resolution
-                if (signatureOffset == -1 && routineInfo == null) {
-
-                    /* If no match, then retry with combinations of object and
-                     * primitive types.
-                     */
-                    if (method == null)
-                    {
-                        primParmTypeNames = getPrimitiveSignature(false);
-
-                        method = classInspector.findPublicMethod(javaClassName,
-                                                    methodName,
-                                                    parmTypeNames,
-                                                    primParmTypeNames,
-                                                    isParam,
-                                                    staticMethod,
-                                                    hasDynamicResultSets);
-                    }
+        {
+            method = classInspector.findPublicMethod
+                (
+                 javaClassName,
+                 methodName,
+                 parmTypeNames,
+                 null,
+                 isParam,
+                 staticMethod,
+                 hasDynamicResultSets,
+                 hasVarargs()
+                 );
+
+            // DB2 LUW does not support Java object types for SMALLINT, INTEGER, BIGINT,
REAL, DOUBLE
+            // and these are the only types that can map to a primitive or an object type
according
+            // to SQL part 13. So we never have a second chance match.
+            // Also if the DDL specified a signature, then no alternate resolution
+            if (signatureOffset == -1 && routineInfo == null) {
+
+                /* If no match, then retry with combinations of object and
+                 * primitive types.
+                 */
+                if (method == null)
+                {
+                    primParmTypeNames = getPrimitiveSignature(false);
+
+                    method = classInspector.findPublicMethod
+                        (
+                         javaClassName,
+                         methodName,
+                         parmTypeNames,
+                         primParmTypeNames,
+                         isParam,
+                         staticMethod,
+                         hasDynamicResultSets,
+                         hasVarargs()
+                         );
                 }
+            }
         }
         catch (ClassNotFoundException e)
         {

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/NewInvocationNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/NewInvocationNode.java?rev=1420165&r1=1420164&r2=1420165&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/NewInvocationNode.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/NewInvocationNode.java
Tue Dec 11 13:59:13 2012
@@ -417,8 +417,11 @@ public class NewInvocationNode extends M
 
 		try
 		{
-			publicMethod = classInspector.findPublicMethod(javaClassName, methodName,
-											   parmTypeNames, null, isParam, staticMethod, false);
+			publicMethod = classInspector.findPublicMethod
+                (
+                 javaClassName, methodName,
+                 parmTypeNames, null, isParam, staticMethod, false, hasVarargs()
+                 );
 
 			/* If no match, then retry to match any possible combinations of
 			 * object and primitive types.
@@ -426,9 +429,11 @@ public class NewInvocationNode extends M
 			if (publicMethod == null)
 			{
 				String[] primParmTypeNames = getPrimitiveSignature(false);
-				publicMethod = classInspector.findPublicMethod(javaClassName, 
-										methodName, parmTypeNames,
-										primParmTypeNames, isParam, staticMethod, false);
+				publicMethod = classInspector.findPublicMethod
+                    (
+                     javaClassName, methodName, parmTypeNames,
+                     primParmTypeNames, isParam, staticMethod, false, hasVarargs()
+                     );
 			}
 		}
 		catch (ClassNotFoundException e)

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/StaticMethodCallNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/StaticMethodCallNode.java?rev=1420165&r1=1420164&r2=1420165&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/StaticMethodCallNode.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/StaticMethodCallNode.java
Tue Dec 11 13:59:13 2012
@@ -289,7 +289,7 @@ public class StaticMethodCallNode extend
 		verifyClassExist(javaClassName);
 
 		/* Resolve the method call */
-		resolveMethodCall(javaClassName, true);
+		resolveMethodCall( javaClassName, true );
 
 
 		alreadyBound = true;
@@ -461,8 +461,16 @@ public class StaticMethodCallNode extend
 
 			RoutineAliasInfo routineInfo = (RoutineAliasInfo) proc.getAliasInfo();
 			int parameterCount = routineInfo.getParameterCount();
-			if (parameterCount != methodParms.length)
-				continue;
+            boolean hasVarargs = routineInfo.hasVarargs();
+
+            if ( hasVarargs )
+            {
+                // a varargs method can be called with no values supplied
+                // for the trailing varargs argument
+                if ( methodParms.length < (parameterCount - 1) ) { continue; }
+            }
+			else if (parameterCount != methodParms.length)
+            { continue; }
 
 			// pre-form the method signature. If it is a dynamic result set procedure
 			// then we need to add in the ResultSet array
@@ -471,7 +479,7 @@ public class StaticMethodCallNode extend
 
 			int sigParameterCount = parameterCount;
 			if (routineInfo.getMaxDynamicResultSets() > 0)
-				sigParameterCount++;
+            { sigParameterCount++; }
 
 			signature = new JSQLType[sigParameterCount];
 			for (int p = 0; p < parameterCount; p++) {
@@ -519,40 +527,6 @@ public class StaticMethodCallNode extend
 
 				signature[p] = new JSQLType(methoddtd);
 
-				// check parameter is a ? node for INOUT and OUT parameters.
-
-				ValueNode sqlParamNode = null;
-
-				if (methodParms[p] instanceof SQLToJavaValueNode) {
-					SQLToJavaValueNode sql2j = (SQLToJavaValueNode) methodParms[p];
-					sqlParamNode = sql2j.getSQLValueNode();
-				}
-				else
-				{
-				}
-
-				boolean isParameterMarker = true;
-				if ((sqlParamNode == null) || !sqlParamNode.requiresTypeFromContext())
-				{
-					if (parameterMode != JDBC30Translation.PARAMETER_MODE_IN) {
-					 
-						throw StandardException.newException(SQLState.LANG_DB2_PARAMETER_NEEDS_MARKER,
-							RoutineAliasInfo.parameterMode(parameterMode),
-							routineInfo.getParameterNames()[p]);
-					}
-					isParameterMarker = false;
-				}
-				else
-				{
-					if (applicationParameterNumbers == null)
-						applicationParameterNumbers = new int[parameterCount];
-		  			if (sqlParamNode instanceof UnaryOperatorNode) {
-		  				ParameterNode pn = ((UnaryOperatorNode)sqlParamNode).getParameterOperand();
-		  				applicationParameterNumbers[p] = pn.getParameterNumber();
-		  			} else
-						applicationParameterNumbers[p] = ((ParameterNode) sqlParamNode).getParameterNumber();
-				}
-
 				// this is the SQL type of the procedure parameter.
 				DataTypeDescriptor paramdtd = new DataTypeDescriptor(
 					parameterTypeId,
@@ -562,105 +536,42 @@ public class StaticMethodCallNode extend
 					td.getMaximumWidth()
 				);
 
-				boolean needCast = false;
-				if (!isParameterMarker)
-				{
-
-					// can only be an IN parameter.
-					// check that the value can be assigned to the
-					// type of the procedure parameter.
-					if (sqlParamNode instanceof UntypedNullConstantNode)
-					{
-						sqlParamNode.setType(paramdtd);
-					}
-					else
-					{
-
-
-						DataTypeDescriptor dts;
-						TypeId argumentTypeId;
-
-						if (sqlParamNode != null)
-						{
-							// a node from the SQL world
-							argumentTypeId = sqlParamNode.getTypeId();
-							dts = sqlParamNode.getTypeServices();
-						}
-						else
-						{
-							// a node from the Java world
-							dts = DataTypeDescriptor.getSQLDataTypeDescriptor(methodParms[p].getJavaTypeName());
-							if (dts == null)
-							{
-								throw StandardException.newException(SQLState.LANG_NO_CORRESPONDING_S_Q_L_TYPE, 
-									methodParms[p].getJavaTypeName());
-							}
-
-							argumentTypeId = dts.getTypeId();
-						}
-
-						if (! getTypeCompiler(parameterTypeId).storable(argumentTypeId, getClassFactory()))
-								throw StandardException.newException(SQLState.LANG_NOT_STORABLE, 
-									parameterTypeId.getSQLTypeName(),
-									argumentTypeId.getSQLTypeName() );
-
-						// if it's not an exact length match then some cast will be needed.
-						if (!paramdtd.isExactTypeAndLengthMatch(dts))
-							needCast = true;
-					}
-				}
-				else
-				{
-					// any variable length type will need a cast from the
-					// Java world (the ? parameter) to the SQL type. This
-					// ensures values like CHAR(10) are passed into the procedure
-					// correctly as 10 characters long.
-					if (parameterTypeId.variableLength()) {
-
-						if (parameterMode != JDBC30Translation.PARAMETER_MODE_OUT)
-							needCast = true;
-					}
-				}
-				
-
-				if (needCast)
-				{
-					// push a cast node to ensure the
-					// correct type is passed to the method
-					// this gets tacky because before we knew
-					// it was a procedure call we ensured all the
-					// parameter are JavaNodeTypes. Now we need to
-					// push them back to the SQL domain, cast them
-					// and then push them back to the Java domain.
-
-					if (sqlParamNode == null) {
-
-						sqlParamNode = (ValueNode) getNodeFactory().getNode(
-							C_NodeTypes.JAVA_TO_SQL_VALUE_NODE,
-							methodParms[p], 
-							getContextManager());
-					}
-
-					ValueNode castNode = makeCast
+                //
+                // Now coerce the actual method parameter to the declared type
+                // of this routine arg.
+                //
+
+                // if this is the last argument of a varargs routine...
+                if ( hasVarargs && (p == parameterCount-1) )
+                {
+                    //
+                    // The invocation of a varargs routine may have more actual parameters
+                    // than the number of declared routine arguments. All of the trailing
+                    // parameters must be coercible to the type of the last declared argument.
+                    // Furthermore, it may turn out that there isn't a parameter corresponding
to the last
+                    // declared argument of the varargs routine.
+                    //
+                    for ( int idx = p; idx < methodParms.length; idx++ )
+                    {
+                        coerceMethodParameter
+                            (
+                             fromList, subqueryList, aggregateVector,
+                             parameterCount,
+                             paramdtd, parameterTypeId, parameterMode,
+                             idx
+                             );
+                    }
+                }
+                else    // NOT the last argument of a varargs routine
+                {
+                    coerceMethodParameter
                         (
-                         sqlParamNode,
-                         paramdtd,
-                         getNodeFactory(),
-                         getContextManager()
+                         fromList, subqueryList, aggregateVector,
+                         parameterCount,
+                         paramdtd, parameterTypeId, parameterMode,
+                         p
                          );
-
-					methodParms[p] = (JavaValueNode) getNodeFactory().getNode(
-							C_NodeTypes.SQL_TO_JAVA_VALUE_NODE,
-							castNode, 
-							getContextManager());
-
-					methodParms[p] = methodParms[p].bindExpression(fromList, subqueryList, aggregateVector);
-				}
-
-				// only force the type for a ? so that the correct type shows up
-				// in parameter meta data
-				if (isParameterMarker)
-					sqlParamNode.setType(paramdtd);
+                }
 			}
 
 			if (sigParameterCount != parameterCount) {
@@ -681,9 +592,13 @@ public class StaticMethodCallNode extend
 			ad = proc;
 
 			// If a procedure is in the system schema and defined as executing
-			// SQL do we set we are in system code.
-			if (sd.isSystemSchema() && (routineInfo.getReturnType() == null) && routineInfo.getSQLAllowed()
!= RoutineAliasInfo.NO_SQL)
-				isSystemCode = true;
+			// SQL, note that we are in system code.
+			if (
+                sd.isSystemSchema() &&
+                (routineInfo.getReturnType() == null) &&
+                routineInfo.getSQLAllowed() != RoutineAliasInfo.NO_SQL
+                )
+            { isSystemCode = true; }
 
             routineDefiner = sd.getAuthorizationId();
 
@@ -698,6 +613,168 @@ public class StaticMethodCallNode extend
 	}
 
     /**
+     * <p>
+     * Coerce an actual method parameter to the declared type of the corresponding
+     * routine argument.
+     * </p>
+     */
+    private void    coerceMethodParameter
+        (
+         FromList fromList,
+         SubqueryList subqueryList,
+         Vector aggregateVector, 
+         int    parameterCount, // number of declared routine args
+         DataTypeDescriptor paramdtd,   // declared type of routine arg
+         TypeId parameterTypeId,    // declared type id of routine arg
+         int    parameterMode,
+         int    p   // index of actual method parameter in array of parameters
+         )
+        throws StandardException
+    {
+        // check parameter is a ? node for INOUT and OUT parameters.
+
+        ValueNode sqlParamNode = null;
+
+        if (methodParms[p] instanceof SQLToJavaValueNode)
+        {
+            SQLToJavaValueNode sql2j = (SQLToJavaValueNode) methodParms[p];
+            sqlParamNode = sql2j.getSQLValueNode();
+        }
+
+        boolean isParameterMarker = true;
+        if ((sqlParamNode == null) || !sqlParamNode.requiresTypeFromContext())
+        {
+            if (parameterMode != JDBC30Translation.PARAMETER_MODE_IN)
+            {
+                throw StandardException.newException
+                    (
+                     SQLState.LANG_DB2_PARAMETER_NEEDS_MARKER,
+                     RoutineAliasInfo.parameterMode(parameterMode),
+                     routineInfo.getParameterNames()[p]
+                     );
+            }
+            isParameterMarker = false;
+        }
+        else
+        {
+            if (applicationParameterNumbers == null)
+            { applicationParameterNumbers = new int[parameterCount]; }
+            if (sqlParamNode instanceof UnaryOperatorNode)
+            {
+                ParameterNode pn = ((UnaryOperatorNode)sqlParamNode).getParameterOperand();
+                applicationParameterNumbers[p] = pn.getParameterNumber();
+            } else
+            { applicationParameterNumbers[p] = ((ParameterNode) sqlParamNode).getParameterNumber();
}
+        }
+
+        boolean needCast = false;
+        if (!isParameterMarker)
+        {
+            // can only be an IN parameter.
+            // check that the value can be assigned to the
+            // type of the procedure parameter.
+            if (sqlParamNode instanceof UntypedNullConstantNode)
+            {
+                sqlParamNode.setType(paramdtd);
+            }
+            else
+            {
+                DataTypeDescriptor dts;
+                TypeId argumentTypeId;
+                
+                if (sqlParamNode != null)
+                {
+                    // a node from the SQL world
+                    argumentTypeId = sqlParamNode.getTypeId();
+                    dts = sqlParamNode.getTypeServices();
+                }
+                else
+                {
+                    // a node from the Java world
+                    dts = DataTypeDescriptor.getSQLDataTypeDescriptor(methodParms[p].getJavaTypeName());
+                    if (dts == null)
+                    {
+                        throw StandardException.newException
+                            (
+                             SQLState.LANG_NO_CORRESPONDING_S_Q_L_TYPE, 
+                             methodParms[p].getJavaTypeName()
+                             );
+                    }
+
+                    argumentTypeId = dts.getTypeId();
+                }
+
+                if (! getTypeCompiler(parameterTypeId).storable(argumentTypeId, getClassFactory()))
+                {
+                    throw StandardException.newException
+                        (
+                         SQLState.LANG_NOT_STORABLE, 
+                         parameterTypeId.getSQLTypeName(),
+                         argumentTypeId.getSQLTypeName()
+                         );
+                }
+
+                // if it's not an exact length match then some cast will be needed.
+                if (!paramdtd.isExactTypeAndLengthMatch(dts))   { needCast = true; }
+            }
+        }
+        else
+        {
+            // any variable length type will need a cast from the
+            // Java world (the ? parameter) to the SQL type. This
+            // ensures values like CHAR(10) are passed into the procedure
+            // correctly as 10 characters long.
+            if (parameterTypeId.variableLength())
+            {
+                if (parameterMode != JDBC30Translation.PARAMETER_MODE_OUT)
+                { needCast = true; }
+            }
+        }
+
+        if (needCast)
+        {
+            // push a cast node to ensure the
+            // correct type is passed to the method
+            // this gets tacky because before we knew
+            // it was a procedure call we ensured all the
+            // parameter are JavaNodeTypes. Now we need to
+            // push them back to the SQL domain, cast them
+            // and then push them back to the Java domain.
+            
+            if (sqlParamNode == null)
+            {
+                sqlParamNode = (ValueNode) getNodeFactory().getNode
+                    (
+                     C_NodeTypes.JAVA_TO_SQL_VALUE_NODE,
+                     methodParms[p], 
+                     getContextManager()
+                     );
+            }
+
+            ValueNode castNode = makeCast
+                (
+                 sqlParamNode,
+                 paramdtd,
+                 getNodeFactory(),
+                 getContextManager()
+                 );
+
+            methodParms[p] = (JavaValueNode) getNodeFactory().getNode
+                (
+                 C_NodeTypes.SQL_TO_JAVA_VALUE_NODE,
+                 castNode, 
+                 getContextManager()
+                 );
+
+            methodParms[p] = methodParms[p].bindExpression(fromList, subqueryList, aggregateVector);
+        }
+
+        // only force the type for a ? so that the correct type shows up
+        // in parameter meta data
+        if (isParameterMarker)  { sqlParamNode.setType(paramdtd); }
+    }
+
+    /**
      * Wrap a parameter in a CAST node.
      */
     public  static  ValueNode   makeCast
@@ -772,7 +849,7 @@ public class StaticMethodCallNode extend
 			sql2j = (SQLToJavaValueNode) methodParms[parameterNumber];
 		
 		if (routineInfo != null) {
-			parameterMode = routineInfo.getParameterModes()[parameterNumber];
+			parameterMode = routineInfo.getParameterModes()[ getRoutineArgIdx( parameterNumber ) ];
 		} else {
 			// for a static method call the parameter always starts out as a in parameter, but
 			// may be registered as an IN OUT parameter. For a static method argument to be
@@ -1051,9 +1128,11 @@ public class StaticMethodCallNode extend
 
 		}
 
-		// add in the ResultSet arrays.
-		if (routineInfo != null) {
-
+		// add in the ResultSet arrays. note that varargs and dynamic ResultSets
+        // both make claims on the trailing arguments of the method invocation.
+        // a routine may make use of both varargs and dynamic ResultSets.
+		if ( routineInfo != null && !hasVarargs() )
+        {
 			int compiledResultSets = methodParameterTypes.length - methodParms.length;
 
 			if (compiledResultSets != 0) {

Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/VarargsRoutines.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/VarargsRoutines.java?rev=1420165&view=auto
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/VarargsRoutines.java
(added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/VarargsRoutines.java
Tue Dec 11 13:59:13 2012
@@ -0,0 +1,74 @@
+/*
+
+   Derby - Class org.apache.derbyTesting.functionTests.tests.lang.VarargsRoutines
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to you under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+
+package org.apache.derbyTesting.functionTests.tests.lang;
+
+import java.text.MessageFormat;
+
+/**
+ * <p>
+ * Varargs routines used by VarargsTest.
+ * </p>
+ */
+public  class   VarargsRoutines
+{
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // CONSTANTS
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // STATE
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // SQL ROUTINES
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    /** Compute the maximum of a series of ints */
+    public  static  Integer max( Integer... values )
+    {
+        if ( values == null ) { return null; }
+        if ( values.length == 0 ) { return null; }
+
+        int     result = Integer.MIN_VALUE;
+
+        for ( Integer value : values )
+        {
+            if ( value ==  null ) { return null; }
+            result = Math.max( result, value.intValue() );
+        }
+
+        return result;
+    }
+
+    /** Format a message */
+    public  static  String  formatMessage( String message, String... args )
+    {
+        return MessageFormat.format( message, args );
+    }
+
+}

Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/VarargsRoutines.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/VarargsTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/VarargsTest.java?rev=1420165&r1=1420164&r2=1420165&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/VarargsTest.java
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/VarargsTest.java
Tue Dec 11 13:59:13 2012
@@ -186,4 +186,98 @@ public class VarargsTest  extends Genera
               );
     }
 
+    /**
+     * <p>
+     * Simple invocations to verify that varargs routines can be invoked.
+     * </p>
+     */
+    public void test_02_simple() throws Exception
+    {
+        if ( !vmSupportsVarargs() ) { return; }
+        
+        Connection conn = getConnection();
+
+        goodStatement
+            ( conn,
+              "create function maximum( a int ... ) returns int\n" +
+              "language java parameter style derby no sql deterministic\n" +
+              "external name 'org.apache.derbyTesting.functionTests.tests.lang.VarargsRoutines.max'\n"
+              );
+        goodStatement
+            ( conn,
+              "create function formatMessage( message varchar( 32672 ),  args varchar( 32672
) ... ) returns varchar( 32672 )\n" +
+              "language java parameter style derby no sql deterministic\n" +
+              "external name 'org.apache.derbyTesting.functionTests.tests.lang.VarargsRoutines.formatMessage'\n"
+              );
+
+        // 0 args
+        assertResults
+            (
+             conn,
+             "values maximum()",
+             new String[][]
+             {
+                 { null },
+             },
+             false
+             );
+        
+        // a null argument
+        assertResults
+            (
+             conn,
+             "values maximum( null )",
+             new String[][]
+             {
+                 { null },
+             },
+             false
+             );
+        
+        // one non-null argument
+        assertResults
+            (
+             conn,
+             "values maximum( 1 )",
+             new String[][]
+             {
+                 { "1" },
+             },
+             false
+             );
+         
+        // multiple arguments
+        assertResults
+            (
+             conn,
+             "values maximum( 1, 3, 2 )",
+             new String[][]
+             {
+                 { "3" },
+             },
+             false
+             );
+         
+        // verify that arguments are passed in the correct order
+        assertResults
+            (
+             conn,
+             "values formatMessage( 'First {0} then {1} then {2}', 'one', 'two', 'three'
)",
+             new String[][]
+             {
+                 { "First one then two then three" },
+             },
+             false
+             );
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // MINIONS
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    /** Return true if the VM supports vararg methods */
+    private boolean vmSupportsVarargs() { return JDBC.vmSupportsJDBC3(); }
+
 }

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/build.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/build.xml?rev=1420165&r1=1420164&r2=1420165&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/build.xml
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/build.xml
Tue Dec 11 13:59:13 2012
@@ -99,6 +99,7 @@
       <exclude name="${this.dir}/SpillHash.java"/>
       <exclude name="${this.dir}/unaryArithmeticDynamicParameter.java"/>
       <exclude name="${this.dir}/_Suite.java"/>
+      <exclude name="${this.dir}/VarargsRoutines.java"/>
     </javac>
   </target>
   <target name="compilet2" depends="compilet1">
@@ -172,6 +173,7 @@
       <include name="${this.dir}/LongMagnitude.java"/>
       <include name="${this.dir}/ModeAggregate.java"/>
       <include name="${this.dir}/LobMode.java"/>
+      <include name="${this.dir}/VarargsRoutines.java"/>
     </javac>
   </target> 
 



Mime
View raw message