/*

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

   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.iapi.services.compiler.MethodBuilder;
import org.apache.derby.iapi.services.context.ContextManager;
import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.types.DataTypeDescriptor;

/**
 * A VirtualColumnNode represents a virtual column reference to a column in
 * a row returned by an underlying ResultSetNode. The underlying column could
 * be in a base table,  view (which could expand into a complex
 * expression), subquery in the FROM clause, temp table, expression result, etc.  
 * By the time we get to code generation, all VirtualColumnNodes should stand only 
 * for references to columns in a base table within a FromBaseTable.
 *
 */

class VirtualColumnNode extends ValueNode
{
	/* A VirtualColumnNode contains a pointer to the immediate child result
	 * that is materializing the virtual column and the ResultColumn
	 * that represents that materialization.
	 */
	private ResultSetNode	sourceResultSet;
	private ResultColumn	sourceColumn;

	/* columnId is redundant since a ResultColumn also has one, but
	 * we need it here for generate()
	 */
	int columnId;

	private boolean correlated = false;


	/**
     * Constructor for a VirtualColumnNode.
	 *
	 * @param sourceResultSet	The ResultSetNode where the value is originating
	 * @param sourceColumn		The ResultColumn where the value is originating
	 * @param columnId			The columnId within the current Row
     * @param cm                The context manager
	 */
    VirtualColumnNode(ResultSetNode sourceResultSet,
                      ResultColumn sourceColumn,
                      int columnId,
                      ContextManager cm) throws StandardException
	{
        super(cm);
        this.sourceResultSet = sourceResultSet;
        this.sourceColumn = sourceColumn;
        this.columnId = columnId;
        setType(sourceColumn.getTypeServices());
	}


	/**
	 * 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);

			printLabel(depth, "sourceColumn: ");
		    sourceColumn.treePrint(depth + 1);
            printLabel(depth, "sourceResultSet: ");
            sourceResultSet.treePrint(depth + 1);
		}
	}

	/**
	 * Return the ResultSetNode that is the source of this VirtualColumnNode.
	 *
	 * @return ResultSetNode	
	 */
    ResultSetNode getSourceResultSet()
	{
		return sourceResultSet;
	}

	/**
	 * Return the ResultColumn that is the source of this VirtualColumnNode.
	 *
	 * @return ResultSetNode	
	 */
    ResultColumn getSourceColumn()
	{
		return sourceColumn;
	}

	/**
	 * Get the name of the table the ResultColumn is in, if any.  This will be null
	 * if the user did not supply a name (for example, select a from t).
	 * The method will return B for this example, select b.a from t as b
	 * The method will return T for this example, select t.a from t
	 *
	 * @return	A String containing the name of the table the Column
	 *		is in. If the column is not in a table (i.e. is a
	 * 		derived column), it returns NULL.
	 */
    @Override
    String getTableName()
	{
		return sourceColumn.getTableName();
	}

	/**
	 * Get the name of the schema the ResultColumn's table is in, if any.
	 * The return value will be null if the user did not supply a schema name
	 * (for example, select t.a from t).
	 * Another example for null return value (for example, select b.a from t as b).
	 * But for following query select app.t.a from t, this will return APP
	 *
	 * @return	A String containing the name of the schema for the Column's table.
	 *		If the column is not in a schema (i.e. derived column), it returns NULL.
	 */
    @Override
    String getSchemaName() throws StandardException
	{
		return sourceColumn.getSchemaName();
	}

	/**
	 * Return whether or not the ResultColumn is wirtable by a positioned update.
	 *
	 * @return TRUE, if the column is a base column of a table and is 
	 * writable by a positioned update.
	 */
    @Override
	public boolean updatableByCursor()
	{
		return sourceColumn.updatableByCursor();
	}

	/**
	 * Return the ResultColumn that is the source of this VirtualColumnNode.
	 *
	 * @return ResultSetNode	
	 */
    @Override
    ResultColumn getSourceResultColumn()
	{
		return sourceColumn;
	}

	/**
	 * Mark this VCN as a reference to a correlated column.
	 * (It's source resultSet is an outer ResultSet.
	 */
	void setCorrelated()
	{
		correlated = true;
	}

	/**
	 * Return whether or not this VCN is a correlated reference.
	 * 
	 * @return Whether or not this VCN is a correlated reference.
	 */
	boolean getCorrelated()
	{
		return correlated;
	}

	/**
	 * Return whether or not this expression tree is cloneable.
	 *
	 * @return boolean	Whether or not this expression tree is cloneable.
	 */
    @Override
    boolean isCloneable()
	{
		return true;
	}

	/**
	 * ColumnNode's are against the current row in the system.
	 * This lets us generate
	 * a faster get that simply returns the column from the
	 * current row, rather than getting the value out and
	 * returning that, only to have the caller (in the situations
	 * needed) stuffing it back into a new column holder object.
	 * We will assume the general generate() path is for getting
	 * the value out, and use generateColumn() when we want to
	 * keep the column wrapped.
	 *
	 * @exception StandardException		Thrown on error
	 */
    @Override
    void generateExpression(ExpressionClassBuilder acb, MethodBuilder mb)
									throws StandardException
	{
		int sourceResultSetNumber = sourceColumn.getResultSetNumber();

		/* If the source is marked as redundant, then continue down
		 * the RC/VirtualColumnNode chain.
		 */
		if (sourceColumn.isRedundant())
		{
			sourceColumn.getExpression().generateExpression(acb, mb);
			return;
		}

		if (SanityManager.DEBUG)
		SanityManager.ASSERT(sourceResultSetNumber >= 0,
			"sourceResultSetNumber expected to be >= 0 for virtual column "+sourceColumn.getName());

		/* The ColumnReference is from an immediately underlying ResultSet.
		 * The Row for that ResultSet is Activation.row[sourceResultSetNumber], 
		 * where sourceResultSetNumber is the resultSetNumber for that ResultSet.
		 *
		 * The generated java is the expression:
		 *	(<Datatype interface>) this.row[sourceResultSetNumber].
		 *												getColumn(#columnId);
		 * where <Datatype interface> is the appropriate interface for the
		 * column from the Datatype protocol.
		 */
		acb.pushColumnReference(mb, 
									sourceResultSetNumber,
	    							sourceColumn.getVirtualColumnId());

		mb.cast(sourceColumn.getTypeCompiler().interfaceName());
	}

	/**
	 * Return the variant type for the underlying expression.
	 * The variant type can be:
	 *		VARIANT				- variant within a scan
	 *							  (method calls and non-static field access)
	 *		SCAN_INVARIANT		- invariant within a scan
	 *							  (column references from outer tables)
	 *		QUERY_INVARIANT		- invariant within the life of a query
	 *							  (constant expressions)
	 *
	 * @return	The variant type for the underlying expression.
	 * @exception StandardException	thrown on error
	 */
    @Override
	protected int getOrderableVariantType() throws StandardException
	{
		/*
		** Delegate to the source column
		*/
		return sourceColumn.getOrderableVariantType();
	}

	/**
	 * Get the DataTypeServices from this Node.
	 *
	 * @return	The DataTypeServices from this Node.  This
	 *		may be null if the node isn't bound yet.
	 */
    @Override
    final public DataTypeDescriptor getTypeServices()
	{
        return sourceColumn.getTypeServices();
    }
    
    @Override
    final public void setType(DataTypeDescriptor dtd) throws StandardException
    {
        sourceColumn.setType(dtd);
    }
    
    boolean isEquivalent(ValueNode o) throws StandardException
    {
        if (isSameNodeKind(o)) {
    		VirtualColumnNode other = (VirtualColumnNode)o;
    		return sourceColumn.isEquivalent(other.sourceColumn);
    	}

    	return false;
    }
}
