/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package com.akiban.sql.parser;
import com.akiban.sql.StandardException;
import com.akiban.sql.types.DataTypeDescriptor;
/**
* A ResultColumn represents a result column in a SELECT, INSERT, or UPDATE
* statement. In a SELECT statement, the result column just represents an
* expression in a row being returned to the client. For INSERT and UPDATE
* statements, the result column represents an column in a stored table.
* So, a ResultColumn has to be bound differently depending on the type of
* statement it appears in.
* <P>
* The type of the ResultColumn can differ from its underlying expression,
* for example in certain joins the ResultColumn can be nullable even if
* its underlying column is not. In an INSERT or UPDATE the ResultColumn
* will represent the type of the column in the table, the type of
* the underlying expresion will be the type of the source of the
* value to be insert or updated. The method columnTypeAndLengthMatch()
* can be used to detect when normalization is required between
* the expression and the tyoe of ResultColumn. This class does
* not implement any type normalization (conversion), this is
* typically handled by a NormalizeResultSetNode.
*
*/
public class ResultColumn extends ValueNode
{
/* name and exposedName should point to the same string, unless there is a
* derived column list, in which case name will point to the underlying name
* and exposedName will point to the name from the derived column list.
*/
private String name;
private String exposedName;
private String tableName;
private ValueNode expression;
private boolean defaultColumn;
// tells us if this ResultColumn represents an autoincrement column in a
// base table.
private boolean autoincrement;
private ColumnReference reference; // used to verify quals at bind time, if given.
/* virtualColumnId is the ResultColumn's position (1-based) within the ResultSet */
private int virtualColumnId;
private boolean isNameGenerated;
/**
* Different types of initializer parameters indicate different
* types of initialization. Parameters may be:
*
* <ul>
* <li>arg1 The name of the column, if any.</li>
* <li>arg2 The expression this result column represents</li>
* </ul>
*
* <p>
* - OR -
* </p>
*
* <ul>
* <li>arg1 a column reference node</li>
* <li>arg2 The expression this result column represents</li>
* </ul>
*
* <p>
* - OR -
* </p>
*
* <ul>
* <li>arg1 The column descriptor.</li>
* <li>arg2 The expression this result column represents</li>
* </ul>
*
* <p>
* - OR -
* </p>
*
* <ul>
* <li>dtd The type of the column</li>
* <li>expression The expression this result column represents</li>
* </ul>
*/
public void init(Object arg1, Object arg2) throws StandardException {
// RESOLVE: This is something of a hack - it is not obvious that
// the first argument being null means it should be treated as
// a String.
if ((arg1 instanceof String) || (arg1 == null)) {
this.name = (String)arg1;
this.exposedName = this.name;
setExpression((ValueNode)arg2);
}
else if (arg1 instanceof ColumnReference) {
ColumnReference ref = (ColumnReference)arg1;
this.name = ref.getColumnName();
this.exposedName = ref.getColumnName();
/*
when we bind, we'll want to make sure
the reference has the right table name.
*/
this.reference = ref;
setExpression((ValueNode)arg2);
}
else {
setType((DataTypeDescriptor)arg1);
setExpression((ValueNode)arg2);
if (arg2 instanceof ColumnReference) {
reference = (ColumnReference)arg2;
}
}
/* this result column represents a <default> keyword in an insert or
* update statement
*/
if (expression != null &&
expression.isInstanceOf(NodeTypes.DEFAULT_NODE))
defaultColumn = true;
}
/**
* Fill this node with a deep copy of the given node.
*/
public void copyFrom(QueryTreeNode node) throws StandardException {
super.copyFrom(node);
ResultColumn other = (ResultColumn)node;
this.name = other.name;
this.exposedName = other.exposedName;
this.tableName = other.tableName;
this.expression = (ValueNode)getNodeFactory().copyNode(other.expression,
getParserContext());
this.defaultColumn = other.defaultColumn;
this.autoincrement = other.autoincrement;
this.reference = (ColumnReference)getNodeFactory().copyNode(other.reference,
getParserContext());
this.virtualColumnId = other.virtualColumnId;
this.isNameGenerated = other.isNameGenerated;
}
/**
* Returns TRUE if the ResultColumn is standing in for a DEFAULT keyword in
* an insert/update statement.
*/
public boolean isDefaultColumn() {
return defaultColumn;
}
public void setDefaultColumn(boolean value) {
defaultColumn = value;
}
/**
* Return TRUE if this result column matches the provided column name.
*
* This function is used by ORDER BY column resolution. For the
* ORDER BY clause, Derby will prefer to match on the column's
* alias (exposedName), but will also successfully match on the
* underlying column name. Thus the following statements are
* treated equally:
* select name from person order by name;
* select name as person_name from person order by name;
* select name as person_name from person order by person_name;
* See DERBY-2351 for more discussion.
*/
boolean columnNameMatches(String columnName) {
return columnName.equals(exposedName) ||
columnName.equals(name) ||
columnName.equals(getSourceColumnName());
}
/**
* Returns the underlying source column name, if this ResultColumn
* is a simple direct reference to a table column, or NULL otherwise.
*/
String getSourceColumnName() {
if (expression instanceof ColumnReference)
return ((ColumnReference)expression).getColumnName();
return null;
}
/**
* The following methods implement the ResultColumnDescriptor
* interface. See the Language Module Interface for details.
*/
public String getName() {
return exposedName;
}
public String getSchemaName() throws StandardException {
if (expression != null)
return expression.getSchemaName();
else
return null;
}
public String getTableName() {
if (tableName != null) {
return tableName;
}
else if (expression != null)
return expression.getTableName();
else
return null;
}
public int getColumnPosition() {
return virtualColumnId;
}
/**
* Set the expression in this ResultColumn. This is useful in those
* cases where you don't know the expression in advance, like for
* INSERT statements with column lists, where the column list and
* SELECT or VALUES clause are parsed separately, and then have to
* be hooked up.
*
* @param expression The expression to be set in this ResultColumn
*/
public void setExpression(ValueNode expression) {
this.expression = expression;
}
/**
* Get the expression in this ResultColumn.
*
* @return ValueNode this.expression
*/
public ValueNode getExpression() {
return expression;
}
/**
* Set the expression to a null node of the
* correct type.
*
* @exception StandardException Thrown on error
*/
void setExpressionToNullNode() throws StandardException {
setExpression(getNullNode(getType()));
}
/**
* Set the name in this ResultColumn. This is useful when you don't
* know the name at the time you create the ResultColumn, for example,
* in an insert-select statement, where you want the names of the
* result columns to match the table being inserted into, not the
* table they came from.
*
* @param name The name to set in this ResultColumn
*/
public void setName(String name) {
if (this.name == null) {
this.name = name;
}
else {
assert (reference == null || name.equals(reference.getColumnName())) :
"don't change name from reference name";
}
this.exposedName = name;
}
/**
* Is the name for this ResultColumn generated?
*/
public boolean isNameGenerated() {
return isNameGenerated;
}
/**
* Set that this result column name is generated.
*/
public void setNameGenerated(boolean value) {
isNameGenerated = value;
}
/**
* Adjust the virtualColumnId for this ResultColumn by the specified amount
*
* @param adjust The adjustment for the virtualColumnId
*/
public void adjustVirtualColumnId(int adjust) {
virtualColumnId += adjust;
}
/**
* Set the virtualColumnId for this ResultColumn
*
* @param id The virtualColumnId for this ResultColumn
*/
public void setVirtualColumnId(int id) {
virtualColumnId = id;
}
/**
* Get the virtualColumnId for this ResultColumn
*
* @return virtualColumnId for this ResultColumn
*/
public int getVirtualColumnId() {
return virtualColumnId;
}
/**
* 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
*/
public String toString() {
return "exposedName: " + exposedName + "\n" +
"name: " + name + "\n" +
"tableName: " + tableName + "\n" +
"isDefaultColumn: " + defaultColumn + "\n" +
super.toString();
}
/**
* 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
*/
public void printSubNodes(int depth) {
super.printSubNodes(depth);
if (expression != null) {
printLabel(depth, "expression: ");
expression.treePrint(depth + 1);
}
if (reference != null) {
printLabel(depth, "reference: ");
reference.treePrint(depth + 1);
}
}
/**
* Accept the visitor for all visitable children of this node.
*
* @param v the visitor
*
* @exception StandardException on error
*/
void acceptChildren(Visitor v) throws StandardException {
super.acceptChildren(v);
if (expression != null) {
setExpression((ValueNode)expression.accept(v));
}
if (reference != null) {
reference = (ColumnReference)reference.accept(v);
}
}
public TableName getTableNameObject() {
return null;
}
/* Get the wrapped reference if any */
public ColumnReference getReference() {
return reference;
}
public boolean isEquivalent(ValueNode o) throws StandardException {
if (o.getNodeType() == getNodeType()) {
ResultColumn other = (ResultColumn)o;
if (expression != null) {
return expression.isEquivalent(other.expression);
}
}
return false;
}
}