
/*

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

   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.sql.SQLWarning;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.apache.derby.catalog.UUID;
import org.apache.derby.shared.common.error.ExceptionSeverity;
import org.apache.derby.shared.common.error.StandardException;
import org.apache.derby.iapi.services.compiler.JavaFactory;
import org.apache.derby.iapi.services.context.ContextImpl;
import org.apache.derby.iapi.services.context.ContextManager;
import org.apache.derby.iapi.services.io.FormatableBitSet;
import org.apache.derby.iapi.services.loader.ClassFactory;
import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.sql.compile.CompilerContext;
import org.apache.derby.iapi.sql.compile.OptimizerFactory;
import org.apache.derby.iapi.sql.compile.Parser;
import org.apache.derby.iapi.sql.compile.Visitable;
import org.apache.derby.iapi.sql.compile.VisitableFilter;
import org.apache.derby.iapi.sql.compile.TypeCompilerFactory;
import org.apache.derby.iapi.sql.conn.Authorizer;
import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
import org.apache.derby.iapi.sql.conn.LanguageConnectionFactory;
import org.apache.derby.iapi.sql.depend.DependencyManager;
import org.apache.derby.iapi.sql.depend.Dependent;
import org.apache.derby.iapi.sql.depend.Provider;
import org.apache.derby.iapi.sql.depend.ProviderList;
import org.apache.derby.iapi.sql.dictionary.AliasDescriptor;
import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
import org.apache.derby.iapi.sql.dictionary.PermDescriptor;
import org.apache.derby.iapi.sql.dictionary.PrivilegedSQLObject;
import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
import org.apache.derby.iapi.sql.dictionary.SequenceDescriptor;
import org.apache.derby.iapi.sql.dictionary.StatementColumnPermission;
import org.apache.derby.iapi.sql.dictionary.StatementGenericPermission;
import org.apache.derby.iapi.sql.dictionary.StatementPermission;
import org.apache.derby.iapi.sql.dictionary.StatementRolePermission;
import org.apache.derby.iapi.sql.dictionary.StatementRoutinePermission;
import org.apache.derby.iapi.sql.dictionary.StatementSchemaPermission;
import org.apache.derby.iapi.sql.dictionary.StatementTablePermission;
import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
import org.apache.derby.iapi.store.access.SortCostController;
import org.apache.derby.iapi.store.access.StoreCostController;
import org.apache.derby.iapi.transaction.TransactionControl;
import org.apache.derby.iapi.types.DataTypeDescriptor;

/**
 *
 * CompilerContextImpl, implementation of CompilerContext.
 * CompilerContext and hence CompilerContextImpl objects are private to a LanguageConnectionContext.
 *
 */
public class CompilerContextImpl extends ContextImpl
	implements CompilerContext {

    private static  final   int SCOPE_CELL = 0;
    
	//
	// Context interface       
	//

	/**
		@exception StandardException thrown by makeInvalid() call
	 */
	public void cleanupOnError(Throwable error) throws StandardException {

		setInUse(false);
		resetContext();

		if (error instanceof StandardException) {

			StandardException se = (StandardException) error;
			// if something went wrong with the compile,
			// we need to mark the statement invalid.
			// REVISIT: do we want instead to remove it,
			// so the cache doesn't get full of garbage input
			// that won't even parse?
            
            int severity = se.getSeverity();

			if (severity < ExceptionSeverity.SYSTEM_SEVERITY) 
			{
				if (currentDependent != null)
				{
					currentDependent.makeInvalid(DependencyManager.COMPILE_FAILED,
												 lcc);
				}
				closeStoreCostControllers();
				closeSortCostControllers();
			}
			// anything system or worse, or non-DB errors,
			// will cause the whole system to shut down.
            
            if (severity >= ExceptionSeverity.SESSION_SEVERITY)
                popMe();
		}

	}

	/**
	  *	Reset compiler context (as for instance, when we recycle a context for
	  *	use by another compilation.
	  */
	public	void	resetContext()
	{
		nextColumnNumber = 1;
		nextTableNumber = 0;
		nextSubqueryNumber = 0;
		resetNextResultSetNumber();
		nextEquivalenceClass = -1;
		compilationSchema = null;
		parameterList = null;
		parameterDescriptors = null;
		scanIsolationLevel = TransactionControl.UNSPECIFIED_ISOLATION_LEVEL;
		warnings = null;
		savedObjects = null;
		reliability = CompilerContext.SQL_LEGAL;
		returnParameterFlag = false;
		initRequiredPriv();
		defaultSchemaStack = null;
        referencedSequences = null;
        privilegeCheckFilters =  null;
        namedScopes = null;
        skippingTypePrivileges = false;
	}

	//
	// CompilerContext interface
	//
	// we might want these to refuse to return
	// anything if they are in-use -- would require
	// the interface provide a 'done' call, and
	// we would mark them in-use whenever a get happened.
	public Parser getParser() {
		return parser;
	}

	/**
      * Get the OptimizerFactory for this context
	  *
      * @return The OptimizerFactory for this context.
	  */
    public  OptimizerFactory    getOptimizerFactory()
    {   return lcf.getOptimizerFactory(); }


	public int getNextColumnNumber()
	{
		return nextColumnNumber++;
	}

	public int getNextTableNumber()
	{
		return nextTableNumber++;
	}

	public int getNumTables()
	{
		return nextTableNumber;
	}

	/**
	 * Get the current next subquery number from this CompilerContext.
	 *
	 * @return int	The next subquery number for the current statement.
	 *
	 */

	public int getNextSubqueryNumber()
	{
		return nextSubqueryNumber++;
	}

	/**
	 * Get the number of subquerys in the current statement from this CompilerContext.
	 *
	 * @return int	The number of subquerys in the current statement.
	 *
	 */

	public int getNumSubquerys()
	{
		return nextSubqueryNumber;
	}

	public int getNextResultSetNumber()
	{
		return nextResultSetNumber++;
	}

	public void resetNextResultSetNumber()
	{
		nextResultSetNumber = 0;
	}

	public int getNumResultSets()
	{
		return nextResultSetNumber;
	}

	public String getUniqueClassName()
	{
		// REMIND: should get a new UUID if we roll over...
		if (SanityManager.DEBUG)
		{
    		SanityManager.ASSERT(nextClassName <= Long.MAX_VALUE);
    	}
		return classPrefix.concat(Long.toHexString(nextClassName++));
	}

	/**
	 * Get the next equivalence class for equijoin clauses.
	 *
	 * @return The next equivalence class for equijoin clauses.
	 */
	public int getNextEquivalenceClass()
	{
		return ++nextEquivalenceClass;
	}

	public ClassFactory getClassFactory()
	{
		return lcf.getClassFactory();
	}

	public JavaFactory getJavaFactory()
	{
		return lcf.getJavaFactory();
	}

	public void setCurrentDependent(Dependent d) {
		currentDependent = d;
	}

	/**
	 * Get the current auxiliary provider list from this CompilerContext.
	 *
	 * @return	The current AuxiliaryProviderList.
	 *
	 */

	public ProviderList getCurrentAuxiliaryProviderList()
	{
		return currentAPL;
	}

	/**
	 * Set the current auxiliary provider list for this CompilerContext.
	 *
	 * @param apl	The new current AuxiliaryProviderList.
	 */

	public void setCurrentAuxiliaryProviderList(ProviderList apl)
	{
		currentAPL = apl;
	}

	public void createDependency(Provider p) throws StandardException {
		if (SanityManager.DEBUG)
		SanityManager.ASSERT(currentDependent != null,
				"no current dependent for compilation");

		if (dm == null)
			dm = lcc.getDataDictionary().getDependencyManager();
		dm.addDependency(currentDependent, p, getContextManager());
		addProviderToAuxiliaryList(p);
	}

	/**
	 * Add a dependency between two objects.
	 *
	 * @param d	The Dependent object.
	 * @param p	The Provider of the dependency.
	 * @exception StandardException thrown on failure.
	 *
	 */
	public	void createDependency(Dependent d, Provider p) throws StandardException
	{
		if (dm == null)
			dm = lcc.getDataDictionary().getDependencyManager();

		dm.addDependency(d, p, getContextManager());
		addProviderToAuxiliaryList(p);
	}

	/**
	 * Add a Provider to the current AuxiliaryProviderList, if one exists.
	 *
	 * @param p		The Provider to add.
	 */
	private void addProviderToAuxiliaryList(Provider p)
	{
		if (currentAPL != null)
		{
			currentAPL.addProvider(p);
		}
	}

	public int addSavedObject(Object obj) {
        if (savedObjects == null) {
            savedObjects = new ArrayList<Object>();
        }

		savedObjects.add(obj);
		return savedObjects.size()-1;
	}

	public Object[] getSavedObjects() {
		if (savedObjects == null) return null;

		Object[] retVal = savedObjects.toArray();
		savedObjects = null; // erase to start over
		return retVal;
	}

	/** @see CompilerContext#setSavedObjects */
    public void setSavedObjects(List<Object> objs)
	{
        Iterator<Object> it = objs.iterator();
        while (it.hasNext()) {
            addSavedObject(it.next());
        }
	}

	/** @see CompilerContext#setCursorInfo */
	public void setCursorInfo(Object cursorInfo)
	{
		this.cursorInfo = cursorInfo;
	}

	/** @see CompilerContext#getCursorInfo */
	public Object getCursorInfo()
	{
		return cursorInfo;
	}

	
	/** @see CompilerContext#firstOnStack */
	public void firstOnStack()
	{
		firstOnStack = true;
	}

	/** @see CompilerContext#isFirstOnStack */
	public boolean isFirstOnStack()
	{
		return firstOnStack;
	}

	/**
	 * Set the in use state for the compiler context.
	 *
	 * @param inUse	 The new inUse state for the compiler context.
	 */
	public void setInUse(boolean inUse)
	{
		this.inUse = inUse;

		/*
		** Close the StoreCostControllers associated with this CompilerContext
		** when the context is no longer in use.
		*/
		if ( ! inUse)
		{
			closeStoreCostControllers();
			closeSortCostControllers();
		}
	}

	/**
	 * Return the in use state for the compiler context.
	 *
	 * @return boolean	The in use state for the compiler context.
	 */
	public boolean getInUse()
	{
		return inUse;
	}

	/**
	 * Sets which kind of query fragments are NOT allowed. Basically,
	 * these are fragments which return unstable results. CHECK CONSTRAINTS
	 * and CREATE PUBLICATION want to forbid certain kinds of fragments.
	 *
	 * @param reliability	bitmask of types of query fragments to be forbidden
	 *						see the reliability bitmasks in CompilerContext.java
	 *
	 */
	public void	setReliability(int reliability) { this.reliability = reliability; }

	/**
	 * Return the reliability requirements of this clause. See setReliability()
	 * for a definition of clause reliability.
	 *
	 * @return a bitmask of which types of query fragments are to be forbidden
	 */
	public int getReliability() { return reliability; }

	/**
	 * @see CompilerContext#getStoreCostController
	 *
	 * @exception StandardException		Thrown on error
	 */
	public StoreCostController getStoreCostController(long conglomerateNumber)
			throws StandardException
	{
        Long conglomNum = conglomerateNumber;

        // Try to find the given conglomerate number among the already
        // opened conglomerates.
        StoreCostController retval =
                storeCostControllers.get(conglomNum);

        if (retval == null) {
            // Not found, so get a StoreCostController from the store.
            retval = lcc.getTransactionCompile()
                        .openStoreCost(conglomerateNumber);
            storeCostControllers.put(conglomNum, retval);
        }

		return retval;
	}

	/**
	 *
	 */
	private void closeStoreCostControllers()
	{
        Iterator<StoreCostController> it = storeCostControllers.values().iterator();
        while (it.hasNext())
		{
            StoreCostController scc = it.next();
			try {
				scc.close();
			} catch (StandardException se) {
			}
		}

		storeCostControllers.clear();
	}

	/**
	 * @see CompilerContext#getSortCostController
	 *
	 * @exception StandardException		Thrown on error
	 */
	public SortCostController getSortCostController() throws StandardException
	{
		/*
		** Re-use a single SortCostController for each compilation
		*/
		if (sortCostController == null)
		{
			/*
			** Get a StoreCostController from the store.
			*/

			sortCostController =
				lcc.getTransactionCompile().openSortCostController();
		}

		return sortCostController;
	}

	/**
	 *
	 * @exception StandardException		Thrown on error
	 */
	private void closeSortCostControllers()
	{
		if (sortCostController != null)
		{
			sortCostController.close();
			sortCostController = null;	
		}
	}

	/**
	 * Get the compilation schema descriptor for this compilation context.
	   Will be null if no default schema lookups have occured. Ie.
	   the statement is independent of the current schema.
	 * 
	 * @return the compilation schema descirptor
	 */
	public SchemaDescriptor getCompilationSchema()
	{
		return compilationSchema;
	}

	/**
	 * Set the compilation schema descriptor for this compilation context.
	 *
	 * @param newDefault	the compilation schema
	 * 
	 * @return the previous compilation schema descirptor
	 */
	public SchemaDescriptor setCompilationSchema(SchemaDescriptor newDefault)
	{
		SchemaDescriptor tmpSchema = compilationSchema;
		compilationSchema = newDefault;
		return tmpSchema;
	}

	/**
	 * @see CompilerContext#pushCompilationSchema
	 */
	public void pushCompilationSchema(SchemaDescriptor sd)
	{
		if (defaultSchemaStack == null) {
			defaultSchemaStack = new ArrayList<SchemaDescriptor>(2);
		}

		defaultSchemaStack.add(defaultSchemaStack.size(),
							   getCompilationSchema());
		setCompilationSchema(sd);
	}

	/**
	 * @see CompilerContext#popCompilationSchema
	 */
	public void popCompilationSchema()
	{
		SchemaDescriptor sd =
			defaultSchemaStack.remove(
				defaultSchemaStack.size() - 1);
		setCompilationSchema(sd);
	}

	/**
	 * @see CompilerContext#setParameterList
	 */
    public void setParameterList(List<ParameterNode> parameterList)
	{
		this.parameterList = parameterList;

		/* Don't create param descriptors array if there are no params */
		int numberOfParameters = (parameterList == null) ? 0 : parameterList.size();

		if (numberOfParameters > 0)
		{
			parameterDescriptors = new DataTypeDescriptor[numberOfParameters];
		}
	}

	/**
	 * @see CompilerContext#getParameterList
	 */
    public List<ParameterNode> getParameterList()
	{
		return parameterList;
	}

	/**
	 * @see CompilerContext#setReturnParameterFlag
	 */
	public void setReturnParameterFlag()
	{
		returnParameterFlag = true;
	}

	/**
	 * @see CompilerContext#getReturnParameterFlag
	 */
	public boolean getReturnParameterFlag()
	{
		return returnParameterFlag;
	}

	/**
     * Get an array of type descriptors for all the ? parameters.
	 */
    DataTypeDescriptor[] getParameterTypes()
	{
		return parameterDescriptors;
	}

	/**
	 * @see CompilerContext#setScanIsolationLevel
	 */
	public void setScanIsolationLevel(int isolationLevel)
	{
		scanIsolationLevel = isolationLevel;
	}

	/**
	 * @see CompilerContext#getScanIsolationLevel
	 */
	public int getScanIsolationLevel()
	{
		return scanIsolationLevel;
	}

	/**
	 * @see CompilerContext#getTypeCompilerFactory
	 */
	public TypeCompilerFactory getTypeCompilerFactory()
	{
		return typeCompilerFactory;
	}


	/**
		Add a compile time warning.
	*/
	public void addWarning(SQLWarning warning) {
		if (warnings == null)
			warnings = warning;
		else
			warnings.setNextWarning(warning);
	}

	/**
		Get the chain of compile time warnings.
	*/
	public SQLWarning getWarnings() {
		return warnings;
	}

    /*
     * class interface
     */

    /**
     * This constructor creates and saves the parser using the language
     * connection factory when the context is created (when the first statement
     * comes in, likely).
     */
    @SuppressWarnings("LeakingThisInConstructor")
	public CompilerContextImpl(ContextManager cm,
			LanguageConnectionContext lcc,
		TypeCompilerFactory typeCompilerFactory )
	{
		super(cm, CompilerContext.CONTEXT_ID);

		this.lcc = lcc;
		lcf = lcc.getLanguageConnectionFactory();
		this.parser = lcf.newParser(this);
		this.typeCompilerFactory = typeCompilerFactory;

		// the prefix for classes in this connection
		classPrefix = "ac"+lcf.getUUIDFactory().createUUID().toString().replace('-','x');

		initRequiredPriv();
	}

	private void initRequiredPriv()
	{
		currPrivType = Authorizer.NULL_PRIV;
		privTypeStack.clear();
		requiredColumnPrivileges = null;
		requiredTablePrivileges = null;
		requiredSchemaPrivileges = null;
		requiredRoutinePrivileges = null;
		requiredUsagePrivileges = null;
		requiredRolePrivileges = null;

        if( lcc.usesSqlAuthorization())
		{
			requiredColumnPrivileges = new HashMap<StatementTablePermission,StatementColumnPermission>();
			requiredTablePrivileges = new HashMap<StatementTablePermission,StatementTablePermission>();
			requiredSchemaPrivileges = new HashMap<StatementSchemaPermission,StatementSchemaPermission>();
			requiredRoutinePrivileges = new HashMap<UUID,Integer>();
			requiredUsagePrivileges = new HashMap<UUID,String>();
			requiredRolePrivileges = new HashMap<StatementRolePermission,StatementRolePermission>();
		}
	} // end of initRequiredPriv

	/**
	 * Sets the current privilege type context. Column and table nodes do not know
	 * how they are being used. Higher level nodes in the query tree do not know what
	 * is being referenced.
	 * Keeping the context allows the two to come together.
	 *
	 * @param privType One of the privilege types in org.apache.derby.iapi.sql.conn.Authorizer.
	 */
	public void pushCurrentPrivType( int privType)
	{
		privTypeStack.add(currPrivType);
		currPrivType = privType;
	}

	public void popCurrentPrivType( )
	{
        Integer top = privTypeStack.remove(privTypeStack.size() - 1);
        currPrivType = top.intValue();
	}

	/**
	 * Add a column privilege to the list of used column privileges.
	 *
	 * @param column The column whose privileges we're interested in.
	 */
	public void addRequiredColumnPriv( ColumnDescriptor column)
	{
		if( requiredColumnPrivileges == null // Using old style authorization
			|| currPrivType == Authorizer.NULL_PRIV
			|| currPrivType == Authorizer.DELETE_PRIV // Table privilege only
			|| currPrivType == Authorizer.INSERT_PRIV // Table privilege only
			|| currPrivType == Authorizer.TRIGGER_PRIV // Table privilege only
			|| currPrivType == Authorizer.EXECUTE_PRIV
			|| column == null)
        {
			return;
        }

		/*
		* Note that to look up the privileges for this column,
		* we need to know what table the column is in. However,
		* not all ColumnDescriptor objects are associated with
		* a table object. Sometimes a ColumnDescriptor
		* describes a column but doesn't specify the table. An
		* example of this occurs in the set-clause of the
		* UPDATE statement in SQL, where we may have a
		* ColumnDescriptor which describes the expression that
		* is being used in the UPDATE statement to provide the
		* new value that will be computed by the UPDATE. In such a
		* case, there is no column privilege to be added, so we
		* just take an early return. DERBY-1583 has more details.
		*/
		TableDescriptor td = column.getTableDescriptor();
		if (td == null)
			return;

		if (td.getTableType() ==
				TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE) {
			return; // no priv needed, it is per session anyway
		}

		UUID tableUUID = td.getUUID();

		//DERBY-4191
		if( currPrivType == Authorizer.MIN_SELECT_PRIV){
            // If we are here for MIN_SELECT_PRIV requirement, then first
            // check if there is already a SELECT privilege requirement on any
            // of the columns in the table, or on the table itself. If yes,
            // then we do not need to add MIN_SELECT_PRIV requirement for the
            // table because that requirement is already getting satisfied with
            // the already existing SELECT privilege requirement.
			StatementTablePermission key = new StatementTablePermission( 
					tableUUID, Authorizer.SELECT_PRIV);
            if (requiredColumnPrivileges.containsKey(key) ||
                    requiredTablePrivileges.containsKey(key)) {
				return;
            }
		}
		if( currPrivType == Authorizer.SELECT_PRIV){
			//If we are here for SELECT_PRIV requirement, then first check
			//if there is already any MIN_SELECT_PRIV privilege required
			//on this table. If yes, then that requirement will be fulfilled
			//by the SELECT_PRIV requirement we are adding now. Because of
			//that, remove the MIN_SELECT_PRIV privilege requirement
			StatementTablePermission key = new StatementTablePermission( 
					tableUUID, Authorizer.MIN_SELECT_PRIV);
            requiredColumnPrivileges.remove(key);
		}
		
		StatementTablePermission key = new StatementTablePermission( tableUUID, currPrivType);
		StatementColumnPermission tableColumnPrivileges
		  = requiredColumnPrivileges.get( key);
		if( tableColumnPrivileges == null)
		{
			tableColumnPrivileges = new StatementColumnPermission( tableUUID,
																   currPrivType,
																   new FormatableBitSet( td.getNumberOfColumns()));
			requiredColumnPrivileges.put(key, tableColumnPrivileges);
		}
		tableColumnPrivileges.getColumns().set(column.getPosition() - 1);
	} // end of addRequiredColumnPriv

	/**
	 * Add a table or view privilege to the list of used table privileges.
	 *
	 * @see CompilerContext#addRequiredRoutinePriv
	 */
	public void addRequiredTablePriv( TableDescriptor table)
	{
		if( requiredTablePrivileges == null || table == null)
			return;

		if (table.getTableType() ==
				TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE) {
			return; // no priv needed, it is per session anyway
		}

		if( currPrivType == Authorizer.SELECT_PRIV){
			//DERBY-4191
			//Check if there is any MIN_SELECT_PRIV select privilege required
			//on this table. If yes, then that requirement will be fulfilled
			//by the SELECT_PRIV requirement we are adding now. Because of
			//that, remove the MIN_SELECT_PRIV privilege requirement
			StatementTablePermission key = new StatementTablePermission( 
					table.getUUID(), Authorizer.MIN_SELECT_PRIV);
            requiredColumnPrivileges.remove(key);
		}

		StatementTablePermission key = new StatementTablePermission( table.getUUID(), currPrivType);
		requiredTablePrivileges.put(key, key);
	}

	/**
	 * Add a routine execute privilege to the list of used routine privileges.
	 *
	 * @see CompilerContext#addRequiredRoutinePriv
	 */
	public void addRequiredRoutinePriv( AliasDescriptor routine)
	{
		// routine == null for built in routines
		if( requiredRoutinePrivileges == null || routine == null)
			return;

		// Ignore SYSFUN routines for permission scheme
		if (routine.getSchemaUUID().toString().equals(SchemaDescriptor.SYSFUN_SCHEMA_UUID))
			return;

 		if (requiredRoutinePrivileges.get(routine.getUUID()) == null)
			requiredRoutinePrivileges.put(routine.getUUID(), 1);
	}

	/**
	 * @see CompilerContext#addRequiredUsagePriv
	 */
	public void addRequiredUsagePriv( PrivilegedSQLObject usableObject )
    {
		if( requiredUsagePrivileges == null || usableObject == null) { return; }

        UUID objectID = usableObject.getUUID();
        String objectType = usableObject.getObjectTypeName();

 		if (requiredUsagePrivileges.get( objectID ) == null)
        { requiredUsagePrivileges.put( objectID, objectType ); }
    }
    
	/**
	 * Add a required schema privilege to the list privileges.
	 *
	 * @see CompilerContext#addRequiredSchemaPriv
	 */
	public void addRequiredSchemaPriv(String schemaName, String aid, int privType)
	{
		if( requiredSchemaPrivileges == null || schemaName == null)
			return;

		StatementSchemaPermission key = new 
				StatementSchemaPermission(schemaName, aid, privType);

		requiredSchemaPrivileges.put(key, key);
	}


	/**
	 * Add a required role privilege to the list privileges.
	 *
	 * @see CompilerContext#addRequiredRolePriv
	 */
	public void addRequiredRolePriv(String roleName, int privType)
	{
		if( requiredRolePrivileges == null)
			return;

		StatementRolePermission key = new
			StatementRolePermission(roleName, privType);

		requiredRolePrivileges.put(key, key);
	}


	/**
	 * @return The list of required privileges.
	 */
	public List<StatementPermission> getRequiredPermissionsList()
	{
		int size = 0;
		if( requiredRoutinePrivileges != null)
        { size += requiredRoutinePrivileges.size(); }
		if( requiredUsagePrivileges != null)
        { size += requiredUsagePrivileges.size(); }
		if( requiredTablePrivileges != null)
        { size += requiredTablePrivileges.size(); }
		if( requiredSchemaPrivileges != null)
        { size += requiredSchemaPrivileges.size(); }
		if( requiredColumnPrivileges != null)
        { size += requiredColumnPrivileges.size(); }
		if( requiredRolePrivileges != null)
        { size += requiredRolePrivileges.size(); }
		
		ArrayList<StatementPermission> list = new ArrayList<StatementPermission>( size);
		if( requiredRoutinePrivileges != null)
		{
			for( Iterator<UUID> itr = requiredRoutinePrivileges.keySet().iterator(); itr.hasNext();)
			{
				UUID routineUUID = itr.next();
				
				list.add( new StatementRoutinePermission( routineUUID));
			}
		}
		if( requiredUsagePrivileges != null)
		{
			for( Iterator<UUID> itr = requiredUsagePrivileges.keySet().iterator(); itr.hasNext();)
			{
				UUID objectID = itr.next();
				
                list.add(new StatementGenericPermission(
                        objectID,
                        requiredUsagePrivileges.get(objectID),
                        PermDescriptor.USAGE_PRIV));
			}
		}
		if( requiredTablePrivileges != null)
		{
			for( Iterator<StatementTablePermission> itr = requiredTablePrivileges.values().iterator(); itr.hasNext();)
			{
				list.add( itr.next());
			}
		}
		if( requiredSchemaPrivileges != null)
		{
			for( Iterator<StatementSchemaPermission> itr = requiredSchemaPrivileges.values().iterator(); itr.hasNext();)
			{
				list.add( itr.next());
			}
		}
		if( requiredColumnPrivileges != null)
		{
			for( Iterator<StatementColumnPermission> itr = requiredColumnPrivileges.values().iterator(); itr.hasNext();)
			{
				list.add( itr.next());
			}
		}
		if( requiredRolePrivileges != null)
		{
			for( Iterator<StatementRolePermission> itr = requiredRolePrivileges.values().iterator();
				 itr.hasNext();)
			{
				list.add( itr.next());
			}
		}
		return list;
	} // end of getRequiredPermissionsList

	public void addReferencedSequence( SequenceDescriptor sd )
    {
        if ( referencedSequences == null ) { referencedSequences = new HashMap<UUID,SequenceDescriptor>(); }

        referencedSequences.put( sd.getUUID(), sd );
    }

	/**
	 * Report whether the given sequence has been referenced already.
	 */
    public boolean isReferenced( SequenceDescriptor sd )
    {
        if ( referencedSequences == null ) { return false; }

        return referencedSequences.containsKey( sd.getUUID() );
    }

    public  void    addPrivilegeFilter( VisitableFilter vf )
    {
        if ( privilegeCheckFilters == null ) { privilegeCheckFilters = new ArrayList<VisitableFilter>(); }
        
        privilegeCheckFilters.add( vf );
    }

    public  void    removePrivilegeFilter( VisitableFilter vf )
    {
        if ( (vf != null) && (privilegeCheckFilters != null) )
        {
            privilegeCheckFilters.remove( vf );
        }
    }

    public  boolean passesPrivilegeFilters( Visitable visitable )
        throws StandardException
    {
        // if there are no filters, then all QueryTreeNodes pass.
        if ( privilegeCheckFilters == null ) { return true; }

        for ( VisitableFilter filter : privilegeCheckFilters )
        {
            if ( !filter.accept( visitable ) ) { return false; }
        }

        return true;
    }
    
    public  void    beginScope( String scopeName )
    {
        if ( namedScopes == null ) { namedScopes = new HashMap<String,int[]>(); }
        
        int[]   scopeDepth = namedScopes.get( scopeName );
        if ( scopeDepth == null )
        {
            scopeDepth = new int[ SCOPE_CELL + 1 ];
            namedScopes.put( scopeName, scopeDepth );
        }

        scopeDepth[ SCOPE_CELL ]++;
    }
    
    public  void    endScope( String scopeName )
    {
        if ( namedScopes == null ) { return; }

        int[]   scopeDepth = namedScopes.get( scopeName );
        if ( scopeDepth == null )   { return; }

        scopeDepth[ SCOPE_CELL ]--;

        if ( scopeDepth[ SCOPE_CELL ] <= 0 ) { namedScopes.remove( scopeName ); }
    }

    public  int     scopeDepth( String scopeName )
    {
        if ( namedScopes == null ) { return 0; }

        int[]   scopeDepth = namedScopes.get( scopeName );
        if ( scopeDepth == null )   { return 0; }
        else { return scopeDepth[ SCOPE_CELL ]; }
    }

    /** Set whether we should skip adding USAGE privileges for user-defined types */
    public  boolean    skipTypePrivileges( boolean skip )
    {
        boolean oldValue = skippingTypePrivileges;
        
        skippingTypePrivileges = skip;

        return oldValue;
    }

    /** Return whether we are skipping USAGE privileges for user-defined types */
    public  boolean skippingTypePrivileges()
    {
        return skippingTypePrivileges;
    }

	/*
	** Context state must be reset in resetContext()
	*/

	private final Parser 		parser;
	private final LanguageConnectionContext lcc;
	private final LanguageConnectionFactory lcf;
	private TypeCompilerFactory	typeCompilerFactory;
	private Dependent			currentDependent;
	private DependencyManager	dm;
	private boolean				firstOnStack;
	private boolean				inUse;
	private int					reliability = CompilerContext.SQL_LEGAL;
	private	int					nextColumnNumber = 1;
	private int					nextTableNumber;
	private int					nextSubqueryNumber;
	private int					nextResultSetNumber;
	private int					scanIsolationLevel;
	private int					nextEquivalenceClass = -1;
	private long				nextClassName;
	private List<Object>				savedObjects;
	private String				classPrefix;
	private SchemaDescriptor	compilationSchema;
    private ArrayList<VisitableFilter> privilegeCheckFilters;
    private HashMap<String,int[]> namedScopes;
    private boolean             skippingTypePrivileges;

	/**
	 * Saved execution time default schema, if we need to change it
	 * temporarily.
	 */
	private ArrayList<SchemaDescriptor>        	defaultSchemaStack;

	private ProviderList		currentAPL;
	private boolean returnParameterFlag;

    /**
     * Map that contains all store cost controllers opened in this compiler
     * context. Conglomerate id (long) is the key.
     */
    private final HashMap<Long,StoreCostController> storeCostControllers = new HashMap<Long,StoreCostController>();

	private SortCostController	sortCostController;

    private List<ParameterNode> parameterList;

	/* Type descriptors for the ? parameters */
	private DataTypeDescriptor[]	parameterDescriptors;

	private Object				cursorInfo;

	private SQLWarning warnings;

	private final ArrayList<Integer> privTypeStack = new ArrayList<Integer>();
	private int currPrivType = Authorizer.NULL_PRIV;
	private HashMap<StatementTablePermission,StatementColumnPermission> requiredColumnPrivileges;
	private HashMap<StatementTablePermission,StatementTablePermission> requiredTablePrivileges;
	private HashMap<StatementSchemaPermission,StatementSchemaPermission> requiredSchemaPrivileges;
	private HashMap<UUID,Integer> requiredRoutinePrivileges;
	private HashMap<UUID,String> requiredUsagePrivileges;
	private HashMap<StatementRolePermission,StatementRolePermission> requiredRolePrivileges;
    private HashMap<UUID,SequenceDescriptor> referencedSequences;
    
} // end of class CompilerContextImpl
