/*

   Derby - Class org.apache.derby.impl.sql.compile.FromVTI

   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.derby.impl.sql.compile;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import org.apache.derby.catalog.DefaultInfo;
import org.apache.derby.catalog.TypeDescriptor;
import org.apache.derby.catalog.UUID;
import org.apache.derby.catalog.types.RoutineAliasInfo;
import org.apache.derby.shared.common.error.StandardException;
import org.apache.derby.shared.common.reference.ClassName;
import org.apache.derby.shared.common.reference.SQLState;
import org.apache.derby.iapi.services.classfile.VMOpcode;
import org.apache.derby.iapi.services.compiler.LocalField;
import org.apache.derby.iapi.services.compiler.MethodBuilder;
import org.apache.derby.iapi.services.context.ContextManager;
import org.apache.derby.iapi.services.io.FormatableBitSet;
import org.apache.derby.iapi.services.io.FormatableHashtable;
import org.apache.derby.iapi.services.loader.ClassInspector;
import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.sql.compile.CostEstimate;
import org.apache.derby.iapi.sql.compile.Optimizable;
import org.apache.derby.iapi.sql.compile.OptimizablePredicate;
import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;
import org.apache.derby.iapi.sql.compile.Optimizer;
import org.apache.derby.iapi.sql.compile.RowOrdering;
import org.apache.derby.iapi.sql.compile.Visitable;
import org.apache.derby.iapi.sql.compile.Visitor;
import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
import org.apache.derby.iapi.sql.dictionary.ColumnDescriptorList;
import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
import org.apache.derby.iapi.sql.dictionary.DataDictionary;
import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
import org.apache.derby.iapi.transaction.TransactionControl;
import org.apache.derby.iapi.types.DataTypeDescriptor;
import org.apache.derby.iapi.types.DataValueDescriptor;
import org.apache.derby.iapi.util.JBitSet;
import org.apache.derby.vti.DeferModification;
import org.apache.derby.vti.RestrictedVTI;
import org.apache.derby.vti.Restriction;
import org.apache.derby.vti.VTICosting;
import org.apache.derby.vti.VTIEnvironment;

/**
 * A FromVTI represents a VTI in the FROM list of a DML statement.
 *
 */
class FromVTI extends FromTable implements VTIEnvironment
{

	JBitSet				correlationMap;
	JBitSet				dependencyMap;
	MethodCallNode	methodCall;
	TableName			exposedName;
	SubqueryList subqueryList;
	boolean				implementsVTICosting;
	boolean				optimized;
	boolean				materializable;
	boolean				isTarget;
	boolean				isDerbyStyleTableFunction;
	boolean				isRestrictedTableFunction;
	ResultSet			rs;

    private final FormatableHashtable compileTimeConstants =
            new FormatableHashtable();

	// Number of columns returned by the VTI
	protected int numVTICols;

	private PredicateList restrictionList;

	/*
	** We don't know how expensive a virtual table will be.
	** Let's say it has 10000 rows with a cost of 100000.
	*/
	double estimatedCost = VTICosting.defaultEstimatedCost;
	double estimatedRowCount = VTICosting.defaultEstimatedRowCount;
	boolean supportsMultipleInstantiations = true;
	boolean vtiCosted;

	/* Version 1 or 2 VTI*/
	protected boolean			version2;
	private boolean				implementsPushable;
	private PreparedStatement	ps;

    private JavaValueNode[] methodParms;
    
    private boolean controlsDeferral;
    private int resultSetType = ResultSet.TYPE_FORWARD_ONLY;

    private String[] projectedColumnNames; // for RestrictedVTIs
    private Restriction vtiRestriction; // for RestrictedVTIs

    // If this FromVTI is invoked in a subquery which is invoked in an outer FROM list,
    // then arguments to this FromVTI may not reference other tables in that FROM list.
    // See DERBY-5779. Here is an example of a reference we want to forbid:
    //
    // select tt.*
    //     from
    //         sys.systables systabs,
    //         ( select * from table (syscs_diag.space_table( systabs.tablename )) as t2 ) tt
    //     where systabs.tabletype = 'T' and systabs.tableid = tt.tableid;
    //
    private ArrayList<FromList>   outerFromLists = new ArrayList<FromList>();
    
    // for remapping column references in VTI args at code generation time
    private HashMap<Integer,FromTable> argSources = new HashMap<Integer,FromTable>();

    /**
     * Constructor.
	 * @param invocation		The constructor or static method for the VTI
	 * @param correlationName	The correlation name
	 * @param derivedRCL		The derived column list
	 * @param tableProperties	Properties list associated with the table
	 *
	 * @exception StandardException		Thrown on error
	 */
    FromVTI(MethodCallNode invocation,
            String correlationName,
            ResultColumnList derivedRCL,
            Properties tableProperties,
            ContextManager cm) throws StandardException {
        super(correlationName, tableProperties, cm);
        constructorMinion(
                invocation, derivedRCL, makeTableName(null, correlationName));
	}

    /**
     * Constructor.
	 * @param invocation		The constructor or static method for the VTI
	 * @param correlationName	The correlation name
	 * @param derivedRCL		The derived column list
	 * @param tableProperties	Properties list associated with the table
     * @param exposedTableName  The table name (TableName class)
     * @param cm                The context manager
	 */
    FromVTI(MethodCallNode invocation,
            String correlationName,
            ResultColumnList derivedRCL,
            Properties tableProperties,
            TableName exposedTableName,
            ContextManager cm) {

        super(correlationName, tableProperties, cm);
        constructorMinion(invocation, derivedRCL, exposedTableName);
    }

    private void constructorMinion(
            MethodCallNode invocation,
            ResultColumnList derivedRCL,
            TableName exposedTableName) {

        this.methodCall = invocation;
        setResultColumns( derivedRCL );
        this.subqueryList = new SubqueryList(getContextManager());

        // Cache exposed name for this table.
        // The exposed name becomes the qualifier for each column
        // in the expanded list.
        this.exposedName = exposedTableName;
    }

    // Optimizable interface

	/**
	 * @see Optimizable#estimateCost
	 *
	 * @exception StandardException		Thrown on error
	 */
    @Override
	public CostEstimate estimateCost(
				OptimizablePredicateList predList,
				ConglomerateDescriptor cd,
				CostEstimate outerCost,
				Optimizer optimizer,
				RowOrdering rowOrdering)
			throws StandardException
	{
		setCostEstimate( getCostEstimate(optimizer) );

		/* Cost the VTI if it implements VTICosting.
		 * Otherwise we use the defaults.
		 * NOTE: We only cost the VTI once.
		 */
		if (implementsVTICosting && ! vtiCosted)
		{
			try
			{
				VTICosting vtic = getVTICosting();
				estimatedCost = vtic.getEstimatedCostPerInstantiation(this);
				estimatedRowCount = vtic.getEstimatedRowCount(this);
				supportsMultipleInstantiations = vtic.supportsMultipleInstantiations(this);

				if (ps != null) {
					ps.close();
					ps = null;
				}
				if (rs != null) {
					rs.close();
					rs = null;
				}
			}
			catch (SQLException sqle)
			{
				throw StandardException.unexpectedUserException(sqle);
			}
			vtiCosted = true;
		}

		getCostEstimate().setCost(estimatedCost, estimatedRowCount, estimatedRowCount);

		/*
		** Let the join strategy decide whether the cost of the base
		** scan is a single scan, or a scan per outer row.
		** NOTE: The multiplication should only be done against the
		** total row count, not the singleScanRowCount.
		** RESOLVE - If the join strategy does not do materialization,
		** the VTI does not support multiple instantiations and the
		** outer row count is not exactly 1 row, then we need to change
		** the costing formula to take into account the cost of creating
		** the temp conglomerate, writing to it and reading from it
		** outerCost.rowCount() - 1 times.
		*/
		if (getCurrentAccessPath().
				getJoinStrategy().
					multiplyBaseCostByOuterRows())
		{
			getCostEstimate().multiply(outerCost.rowCount(), getCostEstimate());
		}

		if ( ! optimized)
		{
			subqueryList.optimize(optimizer.getDataDictionary(),
                                  getCostEstimate().rowCount());
			subqueryList.modifyAccessPaths();
		}

		optimized = true;

		return getCostEstimate();
	}

	/**
	 * @see Optimizable#legalJoinOrder
	 */
    @Override
	public boolean legalJoinOrder(JBitSet assignedTableMap)
	{
		/* In order to tell if this is a legal join order, we
		 * need to see if the assignedTableMap, ORed with the
		 * outer tables that we are correlated with, contains
		 * our dependency map.
		 */
        JBitSet tempBitSet = assignedTableMap;
		tempBitSet.or(correlationMap);

		/* Have all of our dependencies been satisified? */
		return tempBitSet.contains(dependencyMap);
	}


	/** @see Optimizable#isMaterializable 
	 *
	 */
    @Override
	public boolean isMaterializable()
	{
		return materializable;
	}

	/** @see Optimizable#supportsMultipleInstantiations */
    @Override
	public boolean supportsMultipleInstantiations()
	{
		return supportsMultipleInstantiations;
	}

    /** Return true if this is a user-defined table function */
    public boolean isDerbyStyleTableFunction()
    {
        return isDerbyStyleTableFunction;
    }

	/**
	 * @see ResultSetNode#adjustForSortElimination()
	 */
    @Override
    void adjustForSortElimination()
	{
		/* It's possible that we have an ORDER BY on the columns for this
		 * VTI but that the sort was eliminated during preprocessing (see
		 * esp. SelectNode.preprocess()).  Take as an example the following
		 * query:
		 *
		 *   select distinct * from
		 *      table(syscs_diag.space_table('T1')) X order by 3
		 *
		 * For this query we will merge the ORDER BY and the DISTINCT and
		 * thereby eliminate the sort for the ORDER BY.  As a result we
		 * will end up here--but we don't need to do anything special to
		 * return VTI rows in the correct order, so this method is a no-op.
		 * DERBY-2805.
		 */
	}

	/**
	 * @see Optimizable#modifyAccessPath
	 *
	 * @exception StandardException		Thrown on error
	 */
    @Override
	public Optimizable modifyAccessPath(JBitSet outerTables) throws StandardException
	{
		/* Close the rs if one was instantiated */
		if (rs != null)
		{
			try
			{
				rs.close();
				rs = null;
			}
			catch(Throwable t)
			{
				throw StandardException.unexpectedUserException(t);
			}
		}

		return super.modifyAccessPath(outerTables);
	}

    /**
     * Add a FromList to the collection of FromLists which bindExpressions() checks
     * when vetting VTI arguments which reference columns in other tables.
     * See DERBY-5554 and DERBY-5779.
     */
    public  void    addOuterFromList( FromList fromList )
    {
        outerFromLists.add( fromList );
    }

    @Override
	public boolean pushOptPredicate(OptimizablePredicate optimizablePredicate)
		throws StandardException
	{
		if (!implementsPushable)
			return false;

        // Do not push down join predicates: those referencing more than one table.
        if( ! optimizablePredicate.getReferencedMap().hasSingleBitSet())
            return false;
        
		if (restrictionList == null) {
            restrictionList = new PredicateList(getContextManager());
		}

		restrictionList.addPredicate((Predicate) optimizablePredicate);
		return true;
	}


	/**
	 * Convert this object to a String.  See comments in QueryTreeNode.java
	 * for how this should be done for tree printing.
	 *
	 * @return	This object as a String
	 */

    @Override
	public String toString()
	{
		if (SanityManager.DEBUG)
		{
			return "materializable: " + materializable + "\n" +
			  super.toString();
		}
		else
		{
			return "";
		}
	}

	/**
	 * Prints the sub-nodes of this object.  See QueryTreeNode.java for
	 * how tree printing is supposed to work.
	 *
	 * @param depth		The depth of this node in the tree
	 */

    @Override
    void printSubNodes(int depth)
	{
		if (SanityManager.DEBUG) {
			super.printSubNodes(depth);

			if (methodCall != null)
			{
				printLabel(depth, "methodCall: ");
				methodCall.treePrint(depth + 1);
			}

			if (exposedName != null)
			{
				printLabel(depth, "exposedName: ");
				exposedName.treePrint(depth + 1);
			}

			if (subqueryList != null)
			{
				printLabel(depth, "subqueryList: ");
				subqueryList.treePrint(depth + 1);
			}
		}
	}

	/** 
	 * Return true if this VTI is a constructor. Otherwise, it is a static method.
	 */
    boolean  isConstructor()
	{
		return ( methodCall instanceof NewInvocationNode );
	}

	/** 
	 * Return the constructor or static method invoked from this node
	 */
    final MethodCallNode getMethodCall()
	{
		return methodCall;
	}

	/**
	 * Get the exposed name for this table, which is the name that can
	 * be used to refer to it in the rest of the query.
	 *
	 * @return	The exposed name for this table.
	 */

    @Override
    String getExposedName()
	{
		return correlationName;
	}

    /**
     * @return the table name used for matching with column references.
     *
     */
    public TableName getExposedTableName()
    {
        return exposedName;
    }

	/**
	 * Mark this VTI as the target of a delete or update.
	 */
	void setTarget()
	{
		isTarget = true;
		version2 = true;
	}


	/**
	 * Bind the non VTI tables in this ResultSetNode.  This includes getting their
	 * descriptors from the data dictionary and numbering them.
	 *
	 * @param dataDictionary	The DataDictionary to use for binding
	 * @param fromListParam		FromList to use/append to.
	 *
	 * @return	ResultSetNode
	 *
	 * @exception StandardException		Thrown on error
	 */

    @Override
    ResultSetNode bindNonVTITables(DataDictionary dataDictionary,
							FromList fromListParam) 
					throws StandardException
	{

		/* Assign the tableNumber.  (All other work done in bindVTITables() */
		if (tableNumber == -1)  // allow re-bind, in which case use old number
			tableNumber = getCompilerContext().getNextTableNumber();
		return this;
	}

    /**
     * @return The name of the VTI, mainly for debugging and error messages.
     */
    String getVTIName()
    {
        return methodCall.getJavaClassName();
    } // end of getVTIName

	/**
	 * Bind this VTI that appears in the FROM list.
	 *
	 * @param fromListParam		FromList to use/append to.
	 *
	 * @return	ResultSetNode		The bound FromVTI.
	 *
	 * @exception StandardException		Thrown on error
	 */

    @Override
    ResultSetNode bindVTITables(FromList fromListParam)
							throws StandardException
	{
		ResultColumnList	derivedRCL = getResultColumns();

		LanguageConnectionContext lcc = getLanguageConnectionContext();

		/* NOTE - setting of table number moved to FromList.bindTables()
		 * in order to avoid an ordering problem with join columns in
		 * parameters.
		 */

		/* Bind the constructor or static method - does basic error checking.
		 * Correlated subqueries are not allowed as parameters to
		 * a VTI, so pass an empty FromList.
		 */
        ArrayList<AggregateNode> aggregates = new ArrayList<AggregateNode>();
        methodCall.bindExpression(fromListParam, subqueryList, aggregates);

		// Is the parameter list to the constructor valid for a VTI?
		methodParms = methodCall.getMethodParms();

        RoutineAliasInfo    routineInfo = methodCall.getRoutineInfo();

        if (
            (routineInfo !=null) &&
            routineInfo.getReturnType().isRowMultiSet() &&
            (routineInfo.getParameterStyle() == RoutineAliasInfo.PS_DERBY_JDBC_RESULT_SET)
            )			{
            isDerbyStyleTableFunction = true;
        }

        if ( isDerbyStyleTableFunction )
        {
            Method boundMethod = (Method) methodCall.getResolvedMethod();

            isRestrictedTableFunction = RestrictedVTI.class.isAssignableFrom( boundMethod.getReturnType() );
        }

		/* If we have a valid constructor, does class implement the correct interface? 
		 * If version2 is true, then it must implement PreparedStatement, otherwise
		 * it can implement either PreparedStatement or ResultSet.  (We check for
		 * PreparedStatement first.)
		 */

		if ( isConstructor() )
		{
		    NewInvocationNode   constructor = (NewInvocationNode) methodCall;
                
		    if (!constructor.assignableTo("java.sql.PreparedStatement"))
		    {
			if (version2)
			{
				throw StandardException.newException(SQLState.LANG_DOES_NOT_IMPLEMENT, 
										getVTIName(),
										"java.sql.PreparedStatement");
			}
			else if (! constructor.assignableTo("java.sql.ResultSet"))
			{
				throw StandardException.newException(SQLState.LANG_DOES_NOT_IMPLEMENT, 
										getVTIName(),
										"java.sql.ResultSet");
			}
		    }
		    else
		    {
			    version2 = true;
		    }
        
		    /* If this is a version 2 VTI */
		    if (version2)
		    {
			// Does it support predicates
			implementsPushable = constructor.assignableTo("org.apache.derby.vti.IQualifyable");
		    }
		    // Remember whether or not the VTI implements the VTICosting interface
		    implementsVTICosting = constructor.assignableTo(ClassName.VTICosting);
		}

        if ( isDerbyStyleTableFunction )
        {
            implementsVTICosting = implementsDerbyStyleVTICosting( methodCall.getJavaClassName() );
        }
            

		/* Build the RCL for this VTI.  We instantiate an object in order
		 * to get the ResultSetMetaData.
		 * 
		 * If we have a special trigger vti, then we branch off and get
		 * its rcl from the trigger table that is waiting for us in
		 * the compiler context.
		 */
		UUID triggerTableId;
		if ((isConstructor()) && ((triggerTableId = getSpecialTriggerVTITableName(lcc, methodCall.getJavaClassName())) != null)  )
		{
			TableDescriptor td = getDataDictionary().getTableDescriptor(triggerTableId);
			setResultColumns( genResultColList(td) );

			// costing info
			vtiCosted = true;
			estimatedCost = 50d;
			estimatedRowCount = 5d;
			supportsMultipleInstantiations = true;
		}
		else
		{	
            setResultColumns( new ResultColumnList((getContextManager())) );

			// if this is a Derby-style Table Function, then build the result
			// column list from the RowMultiSetImpl return datatype

			if ( isDerbyStyleTableFunction ) {
			    createResultColumnsForTableFunction( routineInfo.getReturnType() );
			}
			else
			{
            
			    ResultSetMetaData rsmd = getResultSetMetaData();
	
			    /* Wouldn't it be nice if we knew that the class/object would never
			     * return a null ResultSetMetaData.
			     */
			    if (rsmd == null)
			    {
				throw StandardException.newException(SQLState.LANG_NULL_RESULT_SET_META_DATA, 
											getVTIName());
			    }
	
			    // Remember how many columns VTI returns for partial row calculation
			    try
			    {
				numVTICols = rsmd.getColumnCount();
			    }
			    catch (SQLException sqle)
			    {
				numVTICols = 0;
			    }

			    getResultColumns().createListFromResultSetMetaData(rsmd, exposedName, 
														  getVTIName() );
			}
		}
		numVTICols = getResultColumns().size();
	
		/* Propagate the name info from the derived column list */
		if (derivedRCL != null)
		{
            getResultColumns().propagateDCLInfo(derivedRCL, correlationName);
		}

		return this;
	}

	/**
	 * Get the ResultSetMetaData for the class/object.  We first look for 
	 * the optional static method which has the same signature as the constructor.
	 * If it doesn't exist, then we instantiate an object and get the ResultSetMetaData
	 * from that object.
	 *
	 * @return The ResultSetMetaData from the class/object.
	 *
	 * @exception StandardException		Thrown on error
	 */
    ResultSetMetaData getResultSetMetaData()
		throws StandardException
	{
		// Get the actual 
		ResultSetMetaData rsmd = null;

		try
		{	
			if (version2)
			{
				ps = (PreparedStatement) getNewInstance();

				if (ps.getResultSetConcurrency() != ResultSet.CONCUR_UPDATABLE)
				{
					throw StandardException.newException(SQLState.LANG_UPDATABLE_VTI_NON_UPDATABLE_RS, 
														 getVTIName());
				}

				rsmd = ps.getMetaData();

                controlsDeferral = (ps instanceof DeferModification);

                /* See if the result set is known to be insensitive or not.
                 *
                 * Some older VTI implementations do not implement getResultSetType(). UpdatableVTITemplate
                 * does not implement it at all. UpdatableVTITemplate.getResultSetType throws an
                 * exception. In either of these cases make the conservative assumption that the result set is sensitive.
                 */
                try
                {
                    resultSetType = ps.getResultSetType();
                }
                catch( SQLException sqle){}
                catch( java.lang.AbstractMethodError ame){}
                catch( java.lang.NoSuchMethodError nsme){}

				if (!implementsVTICosting) {
					ps.close();
					ps = null;
				}

			}
			else
			{
				rs = (ResultSet) getNewInstance();

				rsmd = rs.getMetaData();

				if (!implementsVTICosting) {
					rs.close();
					rs = null;
				}
			}
		}
		catch(Throwable t)
		{
			throw StandardException.unexpectedUserException(t);
		}

		return rsmd;
	}

    private Object getNewInstance()
        throws StandardException
    {
		NewInvocationNode   constructor = (NewInvocationNode) methodCall;
        Class<?>[]  paramTypeClasses = constructor.getMethodParameterClasses();
        Object[] paramObjects;

		if (paramTypeClasses != null)
		{
			paramObjects = new Object[paramTypeClasses.length];

			for (int index = 0; index < paramTypeClasses.length; index++)
			{
                Class<?> paramClass = paramTypeClasses[index];

				paramObjects[index] = methodParms[index].getConstantValueAsObject();

				// As-per the JDBC spec SMALLINT and TINYINT map to java.lang.Integer
				// as objects. This means if getConstantValueAsObject() has returned an
				// Integer obejct in these cases, whereas Java method calling requires
				// Short or Byte object.
				if ((paramObjects[index] != null) && paramClass.isPrimitive()) {

					if (paramClass.equals(Short.TYPE)) {
						paramObjects[index] =
                            Short.valueOf(
                                ((Integer) paramObjects[index]).shortValue());
					} else if (paramClass.equals(Byte.TYPE)) {
						paramObjects[index] =
                            Byte.valueOf(
                                ((Integer) paramObjects[index]).byteValue());
					}
				}

				// Pass defaults for unknown primitive values
				if (paramObjects[index] == null && 
					paramClass.isPrimitive())
				{
					if (paramClass.equals(Integer.TYPE))
					{
                        paramObjects[index] = Integer.valueOf(0);
					}
					else if (paramClass.equals(Short.TYPE))
					{
                        paramObjects[index] = Short.valueOf((short) 0);
					}
					else if (paramClass.equals(Byte.TYPE))
					{
                        paramObjects[index] = Byte.valueOf((byte)0);
					}
					else if (paramClass.equals(Long.TYPE))
					{
                        paramObjects[index] = Long.valueOf((long) 0);
					}
					else if (paramClass.equals(Float.TYPE))
					{
						paramObjects[index] = (float) 0;
					}
					else if (paramClass.equals(Double.TYPE))
					{
						paramObjects[index] = (double) 0;
					}
					else if (paramClass.equals(Boolean.TYPE))
					{
						paramObjects[index] = Boolean.FALSE;
					}
					else if (paramClass.equals(Character.TYPE))
					{
                        paramObjects[index] =
                                Character.valueOf(Character.MIN_VALUE);
					}
				}
			}
		}
		else
		{
            paramTypeClasses = new Class<?>[0];
			paramObjects = new Object[0];
		}

        try
        {
            ClassInspector classInspector = getClassFactory().getClassInspector();
            String javaClassName = methodCall.getJavaClassName();
            Constructor<?> constr = classInspector.getClass(javaClassName).getConstructor(paramTypeClasses);

            return constr.newInstance(paramObjects);
        }
		catch(Throwable t)
		{
			throw StandardException.unexpectedUserException(t);
		}
    } // end of getNewInstance

    /**
     * Get the DeferModification interface associated with this VTI
     *
     * @return null if the VTI uses the default modification deferral
     */
    public DeferModification getDeferralControl( )
        throws StandardException
    {
        if( ! controlsDeferral)
            return null;
        try
        {
            return (DeferModification) getNewInstance();
        }
		catch(Throwable t)
		{
			throw StandardException.unexpectedUserException(t);
		}
    } // end of getDeferralControl

    /**
     * @return the ResultSet type of the VTI, TYPE_FORWARD_ONLY if the getResultSetType() method
     *         of the VTI class throws an exception.
     */
    public int getResultSetType()
    {
        return resultSetType;
    }

	/**
	 * Bind the expressions in this VTI.  This means 
	 * binding the sub-expressions, as well as figuring out what the return 
	 * type is for each expression.
	 *
	 *
	 * @exception StandardException		Thrown on error
	 */

    @Override
    void bindExpressions(FromList fromListParam)
					throws StandardException
	{
		/* Figure out if the VTIs parameters are QUERY_INVARIANT.  If so,
		 * then the VTI is a candidate for materialization at execution time
		 * if it is the inner table of a join or in a subquery.
		 */
		materializable = methodCall.areParametersQueryInvariant();

		/* NOTE: We need to rebind any ColumnReferences that are parameters and are
		 * from other VTIs that appear after this one in the FROM list.
		 * These CRs will have uninitialized column and table numbers.
		 */
        ArrayList<AggregateNode> aggregates = null;
        for (ColumnReference ref : getNodesFromParameters(ColumnReference.class))
		{
            //
            // Table Function parameters may not reference columns from other tables in the
            // FROM list of the current query block. See DERBY-5579. We also do not allow
            // VTI parameters to refer to other VTIs. We also do not allow even VTIs to
            // reference other elements in the current <joined table>.
            //
            boolean illegalReference = !ref.getCorrelated();

            if ( ref.getCorrelated() ) // the arg refers to a table in an outer query block
            {
                // If the outer table appears in a FROM list alongside a subquery which
                // we're inside, then the reference is undefined and illegal. The following query
                // is an example of this problem. Again, see DERBY-5779.
                //
                // select tt.*
                //     from
                //         sys.systables systabs,
                //         ( select * from table (syscs_diag.space_table( systabs.tablename )) as t2 ) tt
                //     where systabs.tabletype = 'T' and systabs.tableid = tt.tableid;
                //
                for ( int i = 0; i < outerFromLists.size(); i++ )
                {
                    FromTable   fromTable = columnInFromList( outerFromLists.get( i ), ref );

                    if ( fromTable != null )
                    {
                        illegalReference = true;
                        break;
                    }
                }
            }
            else // the arg refers to a table in this query block
            {
                FromTable   fromTable = columnInFromList( fromListParam, ref );
                if ( fromTable != null )
                {
                    // the only legal kind of reference is a VTI argument which
                    // references a non-VTI/tableFunction table in the current query block
                    if ( !isDerbyStyleTableFunction && !(fromTable instanceof FromVTI) )
                    {
                        break;
                    }
                }
            }

            if ( illegalReference )
            {
                throw StandardException.newException
                    ( SQLState.LANG_BAD_TABLE_FUNCTION_PARAM_REF, ref.getSQLColumnName() );
            }

			// Rebind the CR if the tableNumber is uninitialized
			if (ref.getTableNumber() == -1)
			{
				// we need a fake agg list
                if (aggregates == null)
				{
                    aggregates = new ArrayList<AggregateNode>();
				}
				ref.bindExpression(fromListParam,
									subqueryList,
                                    aggregates);
			}
		}
	}

    /**
     * If the referenced column appears in the indicated FROM list, then
     * return the table it appears in.
     */
    private FromTable columnInFromList( FromList fromList, ColumnReference ref )
        throws StandardException
    {
        int referencedTableNumber = ref.getTableNumber();
        
        for ( int i = 0; i < fromList.size(); i++ )
        {
            FromTable   fromTable = (FromTable) fromList.elementAt( i );

            if ( referencedTableNumber == fromTable.getTableNumber() )
            {
                // remember this FromTable so that we can code generate the arg
                // from actual result columns later on.
                argSources.put(Integer.valueOf(fromTable.getTableNumber()),
                               fromTable );

                return fromTable;
            }
        }

        return null;
    }

	/**
	 * Get all of the nodes of the specified class
	 * from the parameters to this VTI.
	 *
	 * @param nodeClass	The Class of interest.
	 *
	 * @return A list containing all of the nodes of interest.
	 *
	 * @exception StandardException		Thrown on error
	 */
    <T extends Visitable> List<T> getNodesFromParameters(Class<T> nodeClass)
		throws StandardException
	{
        CollectNodesVisitor<T> getCRs = new CollectNodesVisitor<T>(nodeClass);
		methodCall.accept(getCRs);
		return getCRs.getList();
	}

	/**
	 * Expand a "*" into a ResultColumnList with all of the
	 * result columns from the subquery.
	 * @exception StandardException		Thrown on error
	 */
    @Override
    ResultColumnList getAllResultColumns(TableName allTableName)
			throws StandardException
	{
        TableName        toCompare;

		if(allTableName != null)
             toCompare = makeTableName(allTableName.getSchemaName(),correlationName);
        else
            toCompare = makeTableName(null,correlationName);

        if ( allTableName != null &&
             ! allTableName.equals(toCompare))
        {
            return null;
        }

        final ContextManager cm = getContextManager();
        final ResultColumnList rcList = new ResultColumnList(cm);

		/* Build a new result column list based off of resultColumns.
		 * NOTE: This method will capture any column renaming due to 
		 * a derived column list.
		 */
        for (ResultColumn rc : getResultColumns())
		{
            if (!rc.isGenerated()) {
                // Build a ResultColumn/ColumnReference pair for the column //
                ResultColumn newRc = new ResultColumn(
                        rc.getName(),
                        new ColumnReference(rc.getName(), exposedName, cm),
                        cm);

                // Build the ResultColumnList to return //
                rcList.addResultColumn(newRc);
            }
		}
		return rcList;
	}

	/**
	 * Try to find a ResultColumn in the table represented by this FromBaseTable
	 * that matches the name in the given ColumnReference.
	 *
	 * @param columnReference	The columnReference whose name we're looking
	 *				for in the given table.
	 *
	 * @return	A ResultColumn whose expression is the ColumnNode
	 *			that matches the ColumnReference.
	 *		Returns null if there is no match.
	 *
	 * @exception StandardException		Thrown on error
	 */

    @Override
    ResultColumn getMatchingColumn(ColumnReference columnReference)
            throws StandardException
	{
		/* We could get called before our RCL is built.  That's okay, we'll
		 * just say that we don't match. 
		 */
		if (getResultColumns() == null)
		{
			return null;
		}

		ResultColumn	resultColumn = null;
		TableName		columnsTableName;

		columnsTableName = columnReference.getQualifiedTableName();

		/*
		** If the column did not specify a name, or the specified name
		** matches the table we're looking at, see whether the column
		** is in this table.
		*/
		if (columnsTableName == null || columnsTableName.equals(exposedName))
		{
			resultColumn = getResultColumns().getResultColumn(columnReference.getColumnName());
			/* Did we find a match? */
			if (resultColumn != null)
			{
				columnReference.setTableNumber(tableNumber);
                columnReference.setColumnNumber(
                    resultColumn.getColumnPosition());
			}
		}

		return resultColumn;
	}

	/**
	 * Preprocess a ResultSetNode - this currently means:
	 *	o  Generating a referenced table map for each ResultSetNode.
	 *  o  Putting the WHERE and HAVING clauses in conjunctive normal form (CNF).
	 *  o  Converting the WHERE and HAVING clauses into PredicateLists and
	 *	   classifying them.
	 *  o  Ensuring that a ProjectRestrictNode is generated on top of every 
	 *     FromBaseTable and generated in place of every FromSubquery.  
	 *  o  Pushing single table predicates down to the new ProjectRestrictNodes.
	 *
	 * @param numTables			The number of tables in the DML Statement
	 * @param gbl				The group by list, if any
	 * @param fromList			The from list, if any
	 *
	 * @return ResultSetNode at top of preprocessed tree.
	 *
	 * @exception StandardException		Thrown on error
	 */

    @Override
    ResultSetNode preprocess(int numTables,
									GroupByList gbl,
									FromList fromList)
								throws StandardException
	{
		methodCall.preprocess(
            numTables,
            new FromList(
                getOptimizerFactory().doJoinOrderOptimization(),
                getContextManager()),
            new SubqueryList(getContextManager()),
            new PredicateList(getContextManager()));

		/* Generate the referenced table map */
		setReferencedTableMap( new JBitSet(numTables) );
		getReferencedTableMap().set(tableNumber);

		/* Create the dependency map.  This FromVTI depends on any
		 * tables which are referenced by the method call.  Note,
		 * though, that such tables should NOT appear in this node's
		 * referencedTableMap, since that field is really meant to
		 * hold the table numbers for any FromTables which appear
		 * AT OR UNDER the subtree whose root is this FromVTI.  That
		 * said, the tables referenced by methodCall do not appear
		 * "under" this FromVTI--on the contrary, they must appear
		 * "above" this FromVTI within the query tree in order to
		 * be referenced by the methodCall.  So methodCall table
		 * references do _not_ belong in this.referencedTableMap.
		 * (DERBY-3288)
		 */
		dependencyMap = new JBitSet(numTables);
		methodCall.categorize(dependencyMap, false);

		// Make sure this FromVTI does not "depend" on itself.
		dependencyMap.clear(tableNumber);

		// Get a JBitSet of the outer tables represented in the parameter list
		correlationMap = new JBitSet(numTables);
		methodCall.getCorrelationTables(correlationMap);

		return genProjectRestrict(numTables);
	}

	/** 
	 * Put a ProjectRestrictNode on top of each FromTable in the FromList.
	 * ColumnReferences must continue to point to the same ResultColumn, so
	 * that ResultColumn must percolate up to the new PRN.  However,
	 * that ResultColumn will point to a new expression, a VirtualColumnNode, 
	 * which points to the FromTable and the ResultColumn that is the source for
	 * the ColumnReference.  
	 * (The new PRN will have the original of the ResultColumnList and
	 * the ResultColumns from that list.  The FromTable will get shallow copies
	 * of the ResultColumnList and its ResultColumns.  ResultColumn.expression
	 * will remain at the FromTable, with the PRN getting a new 
	 * VirtualColumnNode for each ResultColumn.expression.)
	 * We then project out the non-referenced columns.  If there are no referenced
	 * columns, then the PRN's ResultColumnList will consist of a single ResultColumn
	 * whose expression is 1.
	 *
	 * @param numTables			Number of tables in the DML Statement
	 *
	 * @return The generated ProjectRestrictNode atop the original FromTable.
	 *
	 * @exception StandardException		Thrown on error
	 */

    @Override
	protected ResultSetNode genProjectRestrict(int numTables)
				throws StandardException
	{
		ResultColumnList	prRCList;

		/* We get a shallow copy of the ResultColumnList and its 
		 * ResultColumns.  (Copy maintains ResultColumn.expression for now.)
		 */
		prRCList = getResultColumns();
		setResultColumns( getResultColumns().copyListAndObjects() );

		/* Replace ResultColumn.expression with new VirtualColumnNodes
		 * in the ProjectRestrictNode's ResultColumnList.  (VirtualColumnNodes include
		 * pointers to source ResultSetNode, this, and source ResultColumn.)
		 * NOTE: We don't want to mark the underlying RCs as referenced, otherwise
		 * we won't be able to project out any of them.
		 */
		prRCList.genVirtualColumnNodes(this, getResultColumns(), false);

		/* Project out any unreferenced columns.  If there are no referenced 
		 * columns, generate and bind a single ResultColumn whose expression is 1.
		 */
		prRCList.doProjection();

		/* Finally, we create the new ProjectRestrictNode */
        return new ProjectRestrictNode(
								this,
								prRCList,
								null,	/* Restriction */
								null,   /* Restriction as PredicateList */
								null,	/* Project subquery list */
								null,	/* Restrict subquery list */
								tableProperties,
								getContextManager()	 );
	}

	/**
	 * Return whether or not to materialize this ResultSet tree.
	 *
	 * @return Whether or not to materialize this ResultSet tree.
	 *			would return valid results.
	 *
	 * @exception StandardException		Thrown on error
	 */
    @Override
    boolean performMaterialization(JBitSet outerTables)
		throws StandardException
	{
		/* We need to materialize the VTI iff:
		 *	o  It is an inner table.
		 *	o  The VTI can be materialized.
		 *	o  The VTI cannot be instantiated multiple times.
		 *	o  The join strategy does not do materialization.
		 * RESOLVE - We don't have to materialize if all of the
		 * outer tables are 1 row tables.
		 */
		return (outerTables.getFirstSetBit() != -1 &&	
				! outerTables.hasSingleBitSet() && // Not the outer table
				(! getTrulyTheBestAccessPath().
					getJoinStrategy().
						doesMaterialization()) &&		// Join strategy does not do materialization
					isMaterializable() &&					// VTI can be materialized
				! supportsMultipleInstantiations		// VTI does not support multiple instantiations
				);
	}

    /**
     * Compute the projection and restriction to be pushed to the external
     * table function if it is a RestrictedVTI. This method is called by the
     * parent ProjectRestrictNode at code generation time. See DERBY-4357.
     *
     * @param parentPredicates The full list of predicates to be applied by the parent ProjectRestrictNode
     */
    void computeProjectionAndRestriction( PredicateList parentPredicates )
        throws StandardException
    {
        // nothing to do if this is a not a restricted table function
        if ( !isRestrictedTableFunction ) { return; }

        computeRestriction( parentPredicates, computeProjection( ) );
    }
    /**
     * Fills in the array of projected column names suitable for handing to
     * RestrictedVTI.initScan(). Returns a map of the exposed column names
     * to the actual names of columns in the table function. This is useful
     * because the predicate refers to the exposed column names.
     */
    private HashMap<String,String> computeProjection( ) throws StandardException
    {
        HashMap<String,String>  nameMap = new HashMap<String,String>();

        ResultColumnList allVTIColumns = getResultColumns();
        int              totalColumnCount = allVTIColumns.size();

        projectedColumnNames = new String[ totalColumnCount ];

        for ( int i = 0; i < totalColumnCount; i++ )
        {
            ResultColumn column = allVTIColumns.getResultColumn( i + 1 );
            String       exposedNam = column.getName();

            if ( column.isReferenced() )
            {
                String       baseName = column.getBaseColumnNode().getColumnName();
                
                projectedColumnNames[ i ] = baseName;

                nameMap.put( exposedNam, baseName );
            }
        }

        return nameMap;
    }
    /**
     * Fills in the restriction to be handed to a RestrictedVTI at run-time.
     *
     * @param parentPredicates The full list of predicates to be applied by the parent ProjectRestrictNode
     * @param columnNameMap Mapping between the exposed column names used in the predicates and the actual column names declared for the table function at CREATE FUNCTION time.
     */
    private void computeRestriction( PredicateList parentPredicates, HashMap<String,String> columnNameMap )
        throws StandardException
    {
        if ( parentPredicates == null )  { return; }

        // walk the list, looking for qualifiers, that is, WHERE clause
        // fragments (conjuncts)  which can be pushed into the table function
        for (Predicate pp : parentPredicates)
        {
            if ( canBePushedDown( pp ) )
            {
                // A Predicate has a top level AND node
                Restriction newRestriction =
                        makeRestriction( pp.getAndNode(), columnNameMap );

                // If newRestriction is null, then we are confused. Don't push
                // the restriction into the table function
                if ( newRestriction == null )
                {
                    vtiRestriction = null;
                    return;
                }

                // If we get here, then we still understand the restriction
                // we're compiling.

                if ( vtiRestriction == null ) { vtiRestriction = newRestriction; }
                else { vtiRestriction = new Restriction.AND( vtiRestriction, newRestriction ); }
            }
        }
    }
    /** Return true if the predicate can be pushed into a RestrictedVTI */
    private boolean canBePushedDown( Predicate predicate ) throws StandardException
    {
        JBitSet referencedSet = predicate.getReferencedSet();

        // we want this to be a qualifier on only this FROM table */
        return
            (
             predicate.isQualifier() &&
             (referencedSet != null) &&
             (referencedSet.hasSingleBitSet() ) &&
             (referencedSet.get( getTableNumber() ) )
             );
    }
    /**
     * Turn a compile-time WHERE clause fragment into a run-time
     * Restriction. Returns null if the clause could not be understood.
     *
     * @param clause The clause which should be turned into a Restriction.
     * @param columnNameMap Mapping between the exposed column names used in the predicates and the actual column names declared for the table function at CREATE FUNCTION time.
     */
    private Restriction makeRestriction(
            ValueNode clause,
            HashMap<String, String> columnNameMap )
        throws StandardException
    {
        if ( clause instanceof AndNode )
        {
            AndNode andOperator = (AndNode) clause;

            // strip off trailing vacuous TRUE constant if present
            if ( andOperator.getRightOperand().isBooleanTrue() )
            { return makeRestriction( andOperator.getLeftOperand(), columnNameMap ); }
            
            Restriction leftRestriction = makeRestriction( andOperator.getLeftOperand(), columnNameMap );
            Restriction rightRestriction = makeRestriction( andOperator.getRightOperand(), columnNameMap );

            if ( (leftRestriction == null) || (rightRestriction == null) ) { return null; }

            return new Restriction.AND( leftRestriction, rightRestriction );
        }
        else if ( clause instanceof OrNode )
        {
            OrNode orOperator = (OrNode) clause;
            
            // strip off trailing vacuous FALSE constant if present
            if ( orOperator.getRightOperand().isBooleanFalse() )
            { return makeRestriction( orOperator.getLeftOperand(), columnNameMap ); }
            
            Restriction leftRestriction = makeRestriction( orOperator.getLeftOperand(), columnNameMap );
            Restriction rightRestriction = makeRestriction( orOperator.getRightOperand(), columnNameMap );

            if ( (leftRestriction == null) || (rightRestriction == null) ) { return null; }

            return new Restriction.OR( leftRestriction, rightRestriction );
        }
        else if ( clause instanceof BinaryRelationalOperatorNode )
        { return makeLeafRestriction( (BinaryRelationalOperatorNode) clause, columnNameMap ); }
        else if ( clause instanceof IsNullNode )
        { return makeIsNullRestriction( (IsNullNode) clause, columnNameMap ); }
        else { return iAmConfused( clause ); }
    }
    /**
     * Makes a Restriction out of a comparison between a constant and a column
     * in the VTI.
     *
     * @param clause The clause which should be turned into a Restriction.
     * @param columnNameMap Mapping between the exposed column names used in the predicates and the actual column names declared for the table function at CREATE FUNCTION time.
     */
    private Restriction makeLeafRestriction(
            BinaryRelationalOperatorNode clause,
            HashMap<String, String> columnNameMap) throws StandardException
    {
        int rawOperator = clause.getOperator();
        ColumnReference rawColumn;
        ValueNode rawValue;

        if ( clause.getLeftOperand() instanceof ColumnReference )
        {
            rawColumn = (ColumnReference) clause.getLeftOperand();
            rawValue = clause.getRightOperand();
        }
        else if ( clause.getRightOperand() instanceof ColumnReference )
        {
            rawColumn = (ColumnReference) clause.getRightOperand();
            rawValue = clause.getLeftOperand();
            rawOperator = flipOperator( rawOperator );
        }
        else { return iAmConfused( clause ); }

        int comparisonOperator = mapOperator( rawOperator );
        if ( comparisonOperator < 0 ) { return iAmConfused( clause ); }

        String columnName = columnNameMap.get( rawColumn.getColumnName() );
        Object constantOperand = squeezeConstantValue( rawValue );
        if ( (columnName == null) || (constantOperand == null) ) { return iAmConfused( clause ); }

        return new Restriction.ColumnQualifier( columnName, comparisonOperator, constantOperand );
    }
    /**
     * Makes an IS NULL comparison of a column
     * in the VTI.
     *
     * @param clause The IS NULL (or IS NOT NULL) node
     * @param columnNameMap Mapping between the exposed column names used in the predicates and the actual column names declared for the table function at CREATE FUNCTION time.
     */
    private Restriction makeIsNullRestriction(
            IsNullNode clause,
            HashMap<String, String> columnNameMap) throws StandardException
    {
        ColumnReference rawColumn = (ColumnReference) clause.getOperand();

        int comparisonOperator = mapOperator( clause.getOperator() );
        if ( comparisonOperator < 0 ) { return iAmConfused( clause ); }
        if (
            (comparisonOperator != Restriction.ColumnQualifier.ORDER_OP_ISNULL) &&
            (comparisonOperator != Restriction.ColumnQualifier.ORDER_OP_ISNOTNULL)
            ) { return iAmConfused( clause ); }

        String columnName = columnNameMap.get( rawColumn.getColumnName() );
        if ( columnName == null ) { return iAmConfused( clause ); }

        return new Restriction.ColumnQualifier( columnName, comparisonOperator, null );
    }
    /** This is a handy place to put instrumentation for tracing trees which we don't understand */
    private Restriction iAmConfused( ValueNode clause ) throws StandardException
    {
        return null;
    }
    /** Flip the sense of a comparison */
    private int flipOperator( int rawOperator ) throws StandardException
    {
        switch( rawOperator )
        {
        case RelationalOperator.EQUALS_RELOP:         return RelationalOperator.EQUALS_RELOP;
        case RelationalOperator.GREATER_EQUALS_RELOP: return RelationalOperator.LESS_EQUALS_RELOP;
        case RelationalOperator.GREATER_THAN_RELOP:   return RelationalOperator.LESS_THAN_RELOP;
        case RelationalOperator.LESS_EQUALS_RELOP:    return RelationalOperator.GREATER_EQUALS_RELOP;
        case RelationalOperator.LESS_THAN_RELOP:      return RelationalOperator.GREATER_THAN_RELOP;
        case RelationalOperator.NOT_EQUALS_RELOP:     return RelationalOperator.NOT_EQUALS_RELOP;

        case RelationalOperator.IS_NOT_NULL_RELOP:
        case RelationalOperator.IS_NULL_RELOP:
        default:
            if ( SanityManager.DEBUG )
            {
                SanityManager.THROWASSERT( "Unrecognized relational operator: " + rawOperator );
            }
        }

        return -1;
    }
    /** Map internal operator constants to user-visible ones */
    private int mapOperator( int rawOperator ) throws StandardException
    {
        switch( rawOperator )
        {
        case RelationalOperator.EQUALS_RELOP:         return Restriction.ColumnQualifier.ORDER_OP_EQUALS;
        case RelationalOperator.GREATER_EQUALS_RELOP: return Restriction.ColumnQualifier.ORDER_OP_GREATEROREQUALS;
        case RelationalOperator.GREATER_THAN_RELOP:   return Restriction.ColumnQualifier.ORDER_OP_GREATERTHAN;
        case RelationalOperator.LESS_EQUALS_RELOP:    return Restriction.ColumnQualifier.ORDER_OP_LESSOREQUALS;
        case RelationalOperator.LESS_THAN_RELOP:      return Restriction.ColumnQualifier.ORDER_OP_LESSTHAN;
        case RelationalOperator.IS_NULL_RELOP:        return Restriction.ColumnQualifier.ORDER_OP_ISNULL;
        case RelationalOperator.IS_NOT_NULL_RELOP:    return Restriction.ColumnQualifier.ORDER_OP_ISNOTNULL;
        case RelationalOperator.NOT_EQUALS_RELOP:     return Restriction.ColumnQualifier.ORDER_OP_NOT_EQUALS;

        default:
            if ( SanityManager.DEBUG )
            {
                SanityManager.THROWASSERT( "Unrecognized relational operator: " + rawOperator );
            }
        }

        return -1;
    }
    /**
     * Get the constant or parameter reference out of a comparand. Return null
     * if we are confused. A parameter reference is wrapped in an integer array
     * to distinguish it from a constant integer.
     */
    private Object squeezeConstantValue( ValueNode valueNode ) throws StandardException
    {
        if ( valueNode instanceof ParameterNode )
        {
            return new int[] { ((ParameterNode) valueNode).getParameterNumber() };
        }
        else if ( valueNode instanceof ConstantNode )
        {
            return ((ConstantNode) valueNode).getValue().getObject();
        }
        else
        {
            return iAmConfused( valueNode );
        }
    }
    
	/**
	 * Generation on a FromVTI creates a wrapper around
	 * the user's java.sql.ResultSet
	 *
	 * @param acb	The ActivationClassBuilder for the class being built
	 * @param mb The MethodBuilder for the execute() method to be built
	 *
	 * @exception StandardException		Thrown on error
	 */
    @Override
    void generate(ActivationClassBuilder acb, MethodBuilder mb)
							throws StandardException
	{
        // If necessary, compute the projection to be pushed into the table
        // function
        if ( isRestrictedTableFunction && ( projectedColumnNames == null) ) { computeProjection(); }
        
		/* NOTE: We need to remap any CRs within the parameters
		 * so that we get their values from the right source
		 * row.  For example, if a CR is a join column, we need
		 * to get the value from the source table and not the
		 * join row since the join row hasn't been filled in yet.
		 */
		RemapCRsVisitor rcrv = new RemapCRsVisitor(true);
		methodCall.accept(rcrv);
        remapBaseTableColumns();

		/* Get the next ResultSet #, so that we can number this ResultSetNode, its
		 * ResultColumnList and ResultSet.
		 */
		assignResultSetNumber();

		acb.pushGetResultSetFactoryExpression(mb);
		int nargs = getScanArguments(acb, mb);
		mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getVTIResultSet",ClassName.NoPutResultSet, nargs);
	}

    /**
     * <p>
     * Remap the column references in vti arguments.
     * Point those column references at the result columns for the base table. This
     * prevents us from code-generating the args from references to unfilled columns in
     * higher join nodes. See DERBY-5554.
     * </p>
     */
    private void remapBaseTableColumns() throws StandardException
    {
        for (ColumnReference ref : getNodesFromParameters(ColumnReference.class))
		{
            FromTable fromTable = argSources.get(
                Integer.valueOf(ref.getTableNumber()));

            if ( fromTable != null )
            {
                ResultColumnList    rcl = fromTable.getResultColumns();

                if ( rcl != null )
                {
                    ResultColumn    newRC = rcl.getResultColumn( ref.getColumnName() );

                    if ( newRC != null ) { ref.setSource( newRC ); }
                }
            }
        }
    }

	private int getScanArguments(ActivationClassBuilder acb,
										  MethodBuilder mb)
		throws StandardException
	{
		int				rclSize = getResultColumns().size();
		FormatableBitSet			referencedCols = new FormatableBitSet(rclSize);
		int				erdNumber = -1;
		int				numSet = 0;

		// Get our final cost estimate.
		setCostEstimate( getFinalCostEstimate() );

		for (int index = 0; index < rclSize; index++)
		{
            ResultColumn rc = getResultColumns().elementAt(index);
			if (rc.isReferenced())
			{
				referencedCols.set(index);
				numSet++;
			}
		}

		// Only add referencedCols if not all columns are accessed
		if (numSet != numVTICols)
		{
			erdNumber = acb.addItem(referencedCols);
		}

        // compileTimeConstants cannot be null, even if there are no
        // constants, since VTIResultSet.setSharedState() may want to
        // add constants to it during execution.
        if (SanityManager.DEBUG) {
            SanityManager.ASSERT(compileTimeConstants != null,
                                 "compileTimeConstants is null");
        }
		int ctcNumber = acb.addItem(compileTimeConstants);

		acb.pushThisAsActivation(mb); // arg 1

        // Get a row builder to allocate scan rows of the right shape and size.
        mb.push(acb.addItem(getResultColumns().buildRowTemplate())); // arg 2

		// For a Version 2 VTI we never maintain the java.sql.PreparedStatement
		// from compile time to execute time. This would rquire the PreparedStatement
		// to be shareable across multiple connections, which is not the model for
		// java.sql.PreparedStatement.

		// For a Version 2 VTI we do pass onto the ResultSet the re-useability
		// of the java.sql.PreparedStatement at runtime. The java.sql.PreparedStatement
		// is re-uesable if
		//
		// o  No ? or ColumnReferences in parameters

		boolean reuseablePs = version2 &&
			(getNodesFromParameters(ParameterNode.class).isEmpty()) &&
			(getNodesFromParameters(ColumnReference.class).isEmpty());



		mb.push(getResultSetNumber()); // arg 3

		// The generated method for the constructor
		generateConstructor(acb, mb, reuseablePs); // arg 4

		// Pass in the class name
		mb.push(methodCall.getJavaClassName()); // arg 5

		if (restrictionList != null) {
			restrictionList.generateQualifiers(acb, mb, this, true);
		}
		else
			mb.pushNull(ClassName.Qualifier + "[][]");

		// Pass in the erdNumber for the referenced column FormatableBitSet
		mb.push(erdNumber); // arg 6

		// Whether or not this is a version 2 VTI
		mb.push(version2);

		mb.push(reuseablePs);

		mb.push(ctcNumber);

		// Whether or not this is a target VTI
		mb.push(isTarget);

		// isolation level of the scan (if specified)
		mb.push(getCompilerContext().getScanIsolationLevel());

		// estimated row count
		mb.push(getCostEstimate().rowCount());

		// estimated cost
		mb.push(getCostEstimate().getEstimatedCost());

		// Whether or not this is a Derby-style Table Function
		mb.push(isDerbyStyleTableFunction);

		// Push the return type
        int rtNum = -1;
        if ( isDerbyStyleTableFunction  )
        {
            rtNum = acb.addItem(methodCall.getRoutineInfo().getReturnType());
        }
        mb.push(rtNum);

        // push the projection and restriction for RestrictedVTIs
        mb.push( storeObjectInPS( acb, projectedColumnNames ) );
        mb.push( storeObjectInPS( acb, vtiRestriction ) );        

        // push the schema name and unqualified name of the table function
        TableName   fullName = methodCall.getFullName();
		mb.push( (fullName == null) ? "" : fullName.getSchemaName() );
		mb.push( (fullName == null) ? "" : fullName.getTableName() );

		return 20;
	}
    /** Store an object in the prepared statement.  Returns -1 if the object is
     * null. Otherwise returns the object's retrieval handle.
     */
    private int storeObjectInPS( ActivationClassBuilder acb, Object obj ) throws StandardException
    {
        if ( obj == null ) { return -1; }
        else { return acb.addItem( obj ); }
    }

	private void generateConstructor(ActivationClassBuilder acb,
										   MethodBuilder mb, boolean reuseablePs)
		throws StandardException
	{
        
        String vtiType = version2 ?
                "java.sql.PreparedStatement" : "java.sql.ResultSet";
		// this sets up the method and the static field.
		// generates:
		// 	java.sql.ResultSet userExprFun { }
		MethodBuilder userExprFun = acb.newGeneratedFun(
                vtiType, Modifier.PUBLIC);
		userExprFun.addThrownException("java.lang.Exception");


		// If it's a re-useable PreparedStatement then hold onto it.
		LocalField psHolder = reuseablePs ? acb.newFieldDeclaration(Modifier.PRIVATE, "java.sql.PreparedStatement") : null;

		if (reuseablePs) {

			userExprFun.getField(psHolder);
			userExprFun.conditionalIfNull();
		}

		methodCall.generateExpression(acb, userExprFun);
        userExprFun.upCast(vtiType);

		if (reuseablePs) {

			userExprFun.putField(psHolder);

			userExprFun.startElseCode();

			userExprFun.getField(psHolder);

			userExprFun.completeConditional();
		}

		userExprFun.methodReturn();



		// methodCall knows it is returning its value;

		/* generates:
		 *    return <newInvocation.generate(acb)>;
		 */
		// we are done modifying userExprFun, complete it.
		userExprFun.complete();

   		// constructor is used in the final result set as an access of the new static
		// field holding a reference to this new method.
		// generates:
		//	ActivationClass.userExprFun
		// which is the static field that "points" to the userExprFun
		// that evaluates the where clause.
		acb.pushMethodReference(mb, userExprFun);


		// now add in code to close the reusable PreparedStatement when
		// the activation is closed.
		if (reuseablePs) {
			MethodBuilder closeActivationMethod = acb.getCloseActivationMethod();

			closeActivationMethod.getField(psHolder);
			closeActivationMethod.conditionalIfNull();
			  // do nothing
			  closeActivationMethod.push(0); // work around for no support for real if statements
			closeActivationMethod.startElseCode(); 
			  closeActivationMethod.getField(psHolder);
			  closeActivationMethod.callMethod(VMOpcode.INVOKEINTERFACE, "java.sql.Statement",
				  "close", "void", 0);
			  closeActivationMethod.push(0);

			closeActivationMethod.completeConditional();
			closeActivationMethod.endStatement();
		}

	}

	/**
	 * Search to see if a query references the specifed table name.
	 *
	 * @param name		Table name (String) to search for.
	 * @param baseTable	Whether or not name is for a base table
	 *
	 * @return	true if found, else false
	 *
	 * @exception StandardException		Thrown on error
	 */
    @Override
    boolean referencesTarget(String name, boolean baseTable)
		throws StandardException
	{
		return (! baseTable) && name.equals(methodCall.getJavaClassName());
	}

	/**
	 * Accept the visitor for all visitable children of this node.
	 * 
	 * @param v the visitor
	 *
	 * @exception StandardException on error
	 */
    @Override
	void acceptChildren(Visitor v)
		throws StandardException
	{
		super.acceptChildren(v);

		if (methodCall != null)
		{
			methodCall = (MethodCallNode) methodCall.accept(v);
		}

        if (exposedName != null) {
            exposedName = (TableName) exposedName.accept(v);
        }
	}

	/**
	 * Check and see if we have a special trigger VTI.
	 * If it cannot be bound (because we aren't actually 
	 * compiling or executing a trigger), then throw 
	 * an exception.
	 * 
	 * @return null if not a special trigger vti, or the table
	 * id if it is
	 */
	private UUID getSpecialTriggerVTITableName(LanguageConnectionContext lcc, String className)
		throws StandardException
	{
		if (className.equals(ClassName.TriggerNewTransitionRows) ||
		    className.equals(ClassName.TriggerOldTransitionRows))
		{
			// if there isn't an active trigger being compiled, error
			if (lcc.getTriggerTable() != null)
			{
				return lcc.getTriggerTable().getUUID();
			}
			else if (lcc.getTriggerExecutionContext() != null)
			{
				return lcc.getTriggerExecutionContext().getTargetTableId();
			}
			else
			{
				throw StandardException.newException(SQLState.LANG_CANNOT_BIND_TRIGGER_V_T_I, className);
			}
		}
		return (UUID)null;
	}

	private ResultColumnList genResultColList(TableDescriptor td)
			throws StandardException
	{
		/* Add all of the columns in the table */
        ResultColumnList rcList = new ResultColumnList((getContextManager()));
		ColumnDescriptorList cdl = td.getColumnDescriptorList();
		int					 cdlSize = cdl.size();

		for (int index = 0; index < cdlSize; index++)
		{
			/* Build a ResultColumn/BaseColumnNode pair for the column */
            ColumnDescriptor colDesc = cdl.elementAt(index);

            ValueNode valueNode = new BaseColumnNode(colDesc.getColumnName(),
									  		exposedName,
											colDesc.getType(),
											getContextManager());
            ResultColumn resultColumn =
                    new ResultColumn(colDesc, valueNode, getContextManager());
			/* Build the ResultColumnList to return */
			rcList.addResultColumn(resultColumn);
		}

		return rcList;
	}
	
    @Override
    boolean needsSpecialRCLBinding()
	{
		return true;
	}

	boolean isUpdatableCursor() throws StandardException {
		return true;
	}

	/*
	** VTIEnvironment
	*/
    final public boolean isCompileTime() {
		return true;
	}

	public String getOriginalSQL() {
		return getCompilerContext().getParser().getSQLtext();
	}
	
    final public int getStatementIsolationLevel() {
		return TransactionControl.jdbcIsolationLevel( getCompilerContext().getScanIsolationLevel() );
	}

	public void setSharedState(String key, java.io.Serializable value) {

		if (key == null)
			return;

		compileTimeConstants.put(key, value);
	}

	public Object getSharedState(String key) {
        if (key == null) {
			return null;
        }

		return compileTimeConstants.get(key);
	}

    /**
     * Add result columns for a Derby-style Table Function
     */
    private void    createResultColumnsForTableFunction
        (TypeDescriptor td)
        throws StandardException
    {
        String[] columnNames = td.getRowColumnNames();
        TypeDescriptor[] types = td.getRowTypes();
        for ( int i = 0; i < columnNames.length; i++ )
        {
            String          columnName = columnNames[ i ];
            DataTypeDescriptor  dtd = DataTypeDescriptor.getType(types[i]);
            ResultColumn    rc = getResultColumns().addColumn
                ( exposedName, columnName, dtd );

            //
            // Stuff a column descriptor into the ResultColumn. We do this so that
            // getColumnPosition() will return the column position within the
            // table function's shape. Later on, projection may remove columns
            // from the ResultColumnList. We don't want getColumnPosition() to say
            // that the column position is the index into the abbreviated ResultColumnList.
            // See DERBY-6040.
            //
            ColumnDescriptor    coldesc = new ColumnDescriptor
                (
                 columnName, i+1, dtd,
                 (DataValueDescriptor) null, (DefaultInfo) null, (UUID) null, (UUID) null,
                 0L, 0L, 0L, false
                 );
            rc.setColumnDescriptor( null, coldesc );
        }
    }

    /**
     * Return true if this Derby Style Table Function implements the VTICosting
     * interface. The class must satisfy the following conditions:
     *
     * <ul>
     * <li>Implements VTICosting</li>
     * <li>Has a public, no-arg constructor</li>
     * </ul>
     */
    private boolean implementsDerbyStyleVTICosting( String className )
        throws StandardException
    {
        Constructor<?>     constructor = null;
        Class<?>           vtiClass = lookupClass( className );
        Class<?>           vtiCostingClass = lookupClass( VTICosting.class.getName() );

        try {
            if ( !vtiCostingClass.isAssignableFrom( vtiClass ) ) { return false; }
        }
        catch (Throwable t)
        {
            throw StandardException.unexpectedUserException( t );
        }

        try {
            constructor = vtiClass.getConstructor( new Class<?>[] {} );
        }
        catch (Throwable t)
        {
            throw StandardException.newException
                ( SQLState.LANG_NO_COSTING_CONSTRUCTOR, t, className );
        }
        
        if ( Modifier.isPublic( constructor.getModifiers() ) ) { return true; }

        // Bad class. It thinks it implements VTICosting, but it doesn't
        // have a public no-arg constructor
        throw StandardException.newException
            ( SQLState.LANG_NO_COSTING_CONSTRUCTOR, className );
    }

    /**
     * Get the VTICosting implementation for this optimizable VTI.
     */
    private VTICosting  getVTICosting()
        throws StandardException
    {
        if ( !isDerbyStyleTableFunction ) { return (version2) ? (VTICosting) ps : (VTICosting) rs; }
        
        String              className = methodCall.getJavaClassName();
        Class<?>               vtiClass = lookupClass( className );
        
        try {
            Constructor<?> constructor =
                    vtiClass.getConstructor( new Class<?>[] {} );
            VTICosting          result =
                    (VTICosting) constructor.newInstance( (Object[])null );

            return result;
        }
        catch (Throwable t)
        {
            throw StandardException.unexpectedUserException( t );
        }
    }

    /**
     * Lookup the class that holds the VTI.
     */
    private Class<?> lookupClass( String className )
        throws StandardException
    {
        try {
            return getClassFactory().getClassInspector().getClass( className );
        }
        catch (ClassNotFoundException t)
        {
            throw StandardException.unexpectedUserException( t );
        }
    }

}
