/*

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

   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 org.apache.derby.shared.common.error.StandardException;
import org.apache.derby.shared.common.reference.SQLState;
import org.apache.derby.iapi.services.context.ContextManager;
import org.apache.derby.iapi.services.loader.GeneratedClass;
import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.sql.ResultDescription;
import org.apache.derby.iapi.sql.compile.Visitor;
import org.apache.derby.iapi.sql.dictionary.DataDictionary;
import org.apache.derby.iapi.sql.dictionary.SPSDescriptor;
import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
import org.apache.derby.iapi.sql.execute.ConstantAction;
import org.apache.derby.iapi.sql.execute.ExecPreparedStatement;
import org.apache.derby.iapi.types.DataTypeDescriptor;
import org.apache.derby.iapi.util.ByteArray;


/**
 * A ExecSPSNode is the root of a QueryTree 
 * that represents an EXECUTE STATEMENT
 * statement.  It is a tad abnormal.  During a
 * bind, it locates and retrieves the SPSDescriptor
 * for the particular statement.  At generate time,
 * it generates the prepared statement for the 
 * stored prepared statement and returns it (i.e.
 * it effectively replaces itself with the appropriate
 * prepared statement).
 *
 */

class ExecSPSNode extends StatementNode
{
	private TableName			name;
	private SPSDescriptor		spsd;
	private ExecPreparedStatement ps;

	/**
     * Constructor for a ExecSPSNode
	 *
	 * @param newObjectName		The name of the table to be created
     * @param cm                The context manager
	 *
	 * @exception StandardException		Thrown on error
	 */
    ExecSPSNode(TableName newObjectName,
                ContextManager cm) {
        super(cm);
        this.name = newObjectName;
	}

	/**
	 * Bind this ExecSPSNode.  This means doing any static error
	 * checking that can be done before actually creating the table.
	 * For example, verifying that the ResultColumnList does not
	 * contain any duplicate column names.
	 *
	 * @exception StandardException		Thrown on error
	 */
    @Override
	public void bindStatement() throws StandardException
	{
		/*
		** Grab the compiler context each time we bind just
		** to make sure we have the write one (even though
		** we are caching it).
		*/
		DataDictionary dd = getDataDictionary();

		String schemaName = name.getSchemaName();
		SchemaDescriptor sd = getSchemaDescriptor(name.getSchemaName());
		if (schemaName == null)
			name.setSchemaName(sd.getSchemaName());

		if (sd.getUUID() != null)
			spsd = dd.getSPSDescriptor(name.getTableName(), sd);

		if (spsd == null)
		{
			throw StandardException.newException(SQLState.LANG_OBJECT_NOT_FOUND, "STATEMENT", name);
		}

        if (spsd.getType() == SPSDescriptor.SPS_TYPE_TRIGGER)
		{
			throw StandardException.newException(SQLState.LANG_TRIGGER_SPS_CANNOT_BE_EXECED, name);
		}
		

		/*
		** This execute statement is dependent on the
		** stored prepared statement.  If for any reason
		** the underlying statement is invalidated by
		** the time we get to execution, the 'execute statement'
		** will get invalidated when the underlying statement
		** is invalidated.
		*/
		getCompilerContext().createDependency(spsd);

	}

	/**
	 * SPSes are atomic if its underlying statement is
	 * atomic.
	 *
	 * @return true if the statement is atomic
	 */	
    @Override
	public boolean isAtomic()
	{

		if (SanityManager.DEBUG)
		{
			SanityManager.ASSERT(ps != null, 
				"statement expected to be bound before calling isAtomic()");
		}

		return ps.isAtomic();
	}

	/**
	 * Do code generation for this statement.  Overrides
	 * the normal generation path in StatementNode.
	 *
	 * @param	ignored - ignored (he he)
	 *
	 * @return		A GeneratedClass for this statement
	 *
	 * @exception StandardException		Thrown on error
	 */
    @Override
	public GeneratedClass generate(ByteArray ignored) throws StandardException
	{
		//Bug 4821 - commiting the nested transaction will release any bind time locks
		//This way we won't get lock time out errors while trying to update sysstatement
		//table during stale sps recompilation later in the getPreparedstatement() call.
		if (spsd.isValid() == false) {
			getLanguageConnectionContext().commitNestedTransaction();
			getLanguageConnectionContext().beginNestedTransaction(true);
		}  

		/*
		** The following does a prepare on the underlying
		** statement if necessary.  The returned statement
		** is valid and its class is loaded up.
		*/
		ps = spsd.getPreparedStatement();


		/*
		** Set the saved constants from the prepared statement.
		** Put them in the compilation context -- this is where
		** they are expected.
		*/
		getCompilerContext().setSavedObjects(ps.getSavedObjects());
		getCompilerContext().setCursorInfo(ps.getCursorInfo());
		GeneratedClass gc = ps.getActivationClass();
		
		return gc;
	}
		
	/**
	 * Make the result description.  Really, we are just
	 * copying it from the stored prepared statement.
	 *
	 * @return	the description
	 */
    @Override
	public ResultDescription makeResultDescription()
	{
		return ps.getResultDescription();
	}

	/**
	 * Get information about this cursor.  For sps,
	 * this is info saved off of the original query
	 * tree (the one for the underlying query).
	 *
	 * @return	the cursor info
	 */
    @Override
	public Object getCursorInfo()
	{
		return ps.getCursorInfo();
	}

	/**
	 * Return a description of the ? parameters for the statement
	 * represented by this query tree.  Just return the params
	 * stored with the prepared statement.
	 *
	 * @return	An array of DataTypeDescriptors describing the
	 *		? parameters for this statement.  It returns null
	 *		if there are no parameters.
	 *
	 * @exception StandardException on error
	 */
    @Override
	public DataTypeDescriptor[]	getParameterTypes() throws StandardException
	{
		return spsd.getParams();
	}


	/**
	 * Create the Constant information that will drive the guts of Execution.
	 * This is assumed to be the first action on this node.
	 *
	 */
    @Override
    public ConstantAction makeConstantAction()
	{
		return ps.getConstantAction();
	}

	/**
	 * We need a savepoint if we will do transactional work.
	 * We'll ask the underlying statement if it needs
	 * a savepoint and pass that back.  We have to do this
	 * after generation because getting the PS now might
	 * cause us to basically do DDL (for a stmt recompilation)
	 * which is explicitly banned during binding.  So the
	 * caller can only call this after generate() has retrieved
	 * the target PS.  
	 *
	 * @return boolean	always true.
	 */
    @Override
	public boolean needsSavepoint()
	{
		if (SanityManager.DEBUG)
		{
			SanityManager.ASSERT(ps != null, 
				"statement expected to be bound before calling needsSavepoint()");
		}

		return ps.needsSavepoint();
	}

	/** @see StatementNode#executeStatementName */
    @Override
	public String executeStatementName()
	{
		return name.getTableName();
	}

	/** @see StatementNode#executeSchemaName */
    @Override
	public String executeSchemaName()
	{
		return name.getSchemaName();
	}

	/**
	 * Get the name of the SPS that is used
	 * to execute this statement.  Only relevant
	 * for an ExecSPSNode -- otherwise, returns null.
	 *
	 * @return the name of the underlying sps
	 */
    @Override
	public String getSPSName()
	{
		return spsd.getQualifiedName();
	}
		
	/*
	 * Shouldn't be called
	 */
	int activationKind()
	{
		if (SanityManager.DEBUG)
		{
			SanityManager.THROWASSERT("activationKind not expected "+
				"to be called for a stored prepared statement");
		}
	   return StatementNode.NEED_PARAM_ACTIVATION;
	}
	/////////////////////////////////////////////////////////////////////
	//
	// PRIVATE
	//
	/////////////////////////////////////////////////////////////////////

		
	/////////////////////////////////////////////////////////////////////
	//
	// MISC
	//
	/////////////////////////////////////////////////////////////////////
    String statementToString()
	{
		return "EXECUTE STATEMENT";
	}

    @Override
    void acceptChildren(Visitor v) throws StandardException {
        super.acceptChildren(v);

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