/* Copyright (c) 2001-2009, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb;
import org.hsqldb.HSQLInterface.HSQLParseException;
import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.HsqlNameManager.SimpleName;
import org.hsqldb.lib.ArrayListIdentity;
import org.hsqldb.lib.HsqlList;
import org.hsqldb.lib.OrderedHashSet;
import org.hsqldb.lib.Set;
import org.hsqldb.types.Type;
/**
* Implementation of column, variable, parameter, etc. access operations.
*
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 1.9.0
* @since 1.9.0
*/
public class ExpressionColumn extends Expression {
public final static ExpressionColumn[] emptyArray =
new ExpressionColumn[]{};
//
ColumnSchema column;
String schema;
String tableName;
String columnName;
RangeVariable rangeVariable;
//
NumberSequence sequence;
boolean isWritable; // = false; true if column of writable table
/**
* Creates a OpCodes.COLUMN expression
*/
ExpressionColumn(String schema, String table, String column) {
super(OpTypes.COLUMN);
this.schema = schema;
tableName = table;
columnName = column;
}
ExpressionColumn(ColumnSchema column) {
super(OpTypes.COLUMN);
columnName = column.getName().name;
}
ExpressionColumn(RangeVariable rangeVar, ColumnSchema column) {
super(OpTypes.COLUMN);
columnIndex = rangeVar.rangeTable.findColumn(column.getName().name);
setAttributesAsColumn(rangeVar, columnIndex);
}
ExpressionColumn(RangeVariable rangeVar, ColumnSchema column, int index) {
super(OpTypes.COLUMN);
columnIndex = index;
setAttributesAsColumn(rangeVar, columnIndex);
}
/**
* Creates a temporary OpCodes.COLUMN expression
*/
ExpressionColumn(Expression e, int colIndex, int rangePosition) {
this(OpTypes.SIMPLE_COLUMN);
dataType = e.dataType;
columnIndex = colIndex;
alias = e.alias;
this.rangePosition = rangePosition;
}
ExpressionColumn() {
super(OpTypes.ASTERISK);
}
ExpressionColumn(int type) {
super(type);
if (type == OpTypes.DYNAMIC_PARAM) {
isParam = true;
}
}
ExpressionColumn(Expression[] nodes, String name) {
super(OpTypes.COALESCE);
this.nodes = nodes;
this.columnName = name;
}
/**
* Creates an OpCodes.ASTERISK expression
*/
ExpressionColumn(String schema, String table) {
super(OpTypes.MULTICOLUMN);
this.schema = schema;
tableName = table;
}
/**
* Creates a OpCodes.SEQUENCE expression
*/
ExpressionColumn(NumberSequence sequence) {
super(OpTypes.SEQUENCE);
this.sequence = sequence;
dataType = sequence.getDataType();
}
void setAttributesAsColumn(RangeVariable range, int i) {
if (range.variables != null) {
columnIndex = i;
column = range.getColumn(i);
dataType = column.getDataType();
rangeVariable = range;
} else {
Table t = range.getTable();
columnIndex = i;
column = range.getColumn(i);
dataType = column.getDataType();
columnName = column.getName().name;
tableName = t.getName().name;
schema = t.getSchemaName().name;
if (alias == null && range.hasColumnAlias()) {
alias = range.getColumnAliasName(i);
}
rangeVariable = range;
rangeVariable.addColumn(columnIndex);
}
}
void setAttributesAsColumn(ColumnSchema column, boolean isWritable) {
this.column = column;
dataType = column.getDataType();
this.isWritable = isWritable;
}
SimpleName getSimpleName() {
if (alias != null) {
return alias;
}
if (column != null) {
return column.getName();
}
if (opType == OpTypes.COALESCE) {
return nodes[LEFT].getSimpleName();
}
return null;
}
String getAlias() {
if (alias != null) {
return alias.name;
}
if (opType == OpTypes.COLUMN) {
return columnName;
}
if (opType == OpTypes.COALESCE) {
return columnName;
}
return "";
}
public String getBaseColumnName() {
if (opType == OpTypes.COLUMN && rangeVariable != null) {
return rangeVariable.getTable().getColumn(
columnIndex).getName().name;
}
return null;
}
public HsqlName getBaseColumnHsqlName() {
return column.getName();
}
void collectObjectNames(Set set) {
if (opType == OpTypes.SEQUENCE) {
HsqlName name = ((NumberSequence) valueData).getName();
set.add(name);
return;
}
set.add(column.getName());
if (column.getName().parent != null) {
set.add(column.getName().parent);
}
}
String getColumnName() {
if (opType == OpTypes.COLUMN && column != null) {
return column.getName().name;
}
return getAlias();
}
ColumnSchema getColumn() {
return column;
}
String getSchemaName() {
return schema;
}
RangeVariable getRangeVariable() {
return rangeVariable;
}
public HsqlList resolveColumnReferences(RangeVariable[] rangeVarArray,
int rangeCount, HsqlList unresolvedSet, boolean acceptsSequences) {
switch (opType) {
case OpTypes.SEQUENCE :
if (!acceptsSequences) {
throw Error.error(ErrorCode.X_42598);
}
break;
case OpTypes.MULTICOLUMN :
case OpTypes.DYNAMIC_PARAM :
case OpTypes.ASTERISK :
case OpTypes.SIMPLE_COLUMN :
case OpTypes.COALESCE :
break;
case OpTypes.PARAMETER :
case OpTypes.VARIABLE :
case OpTypes.COLUMN :
if (rangeVariable != null) {
return unresolvedSet;
}
for (int i = 0; i < rangeCount; i++) {
RangeVariable rangeVar = rangeVarArray[i];
if (rangeVar == null) {
continue;
}
if (resolveColumnReference(rangeVar)) {
return unresolvedSet;
}
}
if (unresolvedSet == null) {
unresolvedSet = new ArrayListIdentity();
}
unresolvedSet.add(this);
}
return unresolvedSet;
}
public boolean resolveColumnReference(RangeVariable rangeVar) {
if (tableName == null) {
Expression e = rangeVar.getColumnExpression(columnName);
if (e != null) {
opType = e.opType;
nodes = e.nodes;
dataType = e.dataType;
return true;
}
if (rangeVar.variables != null) {
int colIndex = rangeVar.findColumn(columnName);
if (colIndex == -1) {
return false;
}
ColumnSchema column = rangeVar.getColumn(colIndex);
if (column.getParameterMode()
== SchemaObject.ParameterModes.PARAM_OUT) {
return false;
} else {
opType = rangeVar.isVariable ? OpTypes.VARIABLE
: OpTypes.PARAMETER;
setAttributesAsColumn(rangeVar, colIndex);
return true;
}
}
}
if (!rangeVar.resolvesTableName(this)) {
return false;
}
int colIndex = rangeVar.findColumn(columnName);
if (colIndex != -1) {
setAttributesAsColumn(rangeVar, colIndex);
return true;
}
return false;
}
public void resolveTypes(Session session, Expression parent) {
switch (opType) {
case OpTypes.DEFAULT :
if (parent != null && parent.opType != OpTypes.ROW) {
throw Error.error(ErrorCode.X_42544);
}
break;
case OpTypes.COALESCE : {
Type type = null;
for (int i = 0; i < nodes.length; i++) {
type = Type.getAggregateType(nodes[i].dataType, type);
}
dataType = type;
break;
}
}
}
public Object getValue(Session session) {
switch (opType) {
case OpTypes.DEFAULT :
return null;
case OpTypes.VARIABLE : {
return session.sessionContext.routineVariables[columnIndex];
}
case OpTypes.PARAMETER : {
return session.sessionContext.routineArguments[columnIndex];
}
case OpTypes.COLUMN : {
Object[] data =
(Object[]) session.sessionContext
.rangeIterators[rangeVariable.rangePosition]
.getCurrent();
Object value = data[columnIndex];
Type colType = column.getDataType();
if (!dataType.equals(colType)) {
value = dataType.convertToType(session, value, colType);
}
return value;
}
case OpTypes.SIMPLE_COLUMN : {
Object[] data =
(Object[]) session.sessionContext
.rangeIterators[rangePosition].getCurrent();
return data[columnIndex];
}
case OpTypes.COALESCE : {
Object value = null;
for (int i = 0; i < nodes.length; i++) {
value = nodes[i].getValue(session, dataType);
if (value != null) {
return value;
}
}
return value;
}
case OpTypes.DYNAMIC_PARAM : {
return session.sessionContext.dynamicArguments[parameterIndex];
}
case OpTypes.SEQUENCE : {
return session.sessionData.getSequenceValue(sequence);
}
case OpTypes.ASTERISK :
case OpTypes.MULTICOLUMN :
default :
throw Error.runtimeError(ErrorCode.U_S0500, "Expression");
}
}
public String getSQL() {
switch (opType) {
case OpTypes.DEFAULT :
return Tokens.T_DEFAULT;
case OpTypes.DYNAMIC_PARAM :
return Tokens.T_QUESTION;
case OpTypes.ASTERISK :
return "*";
case OpTypes.COALESCE :
// return columnName;
return alias.getStatementName();
case OpTypes.VARIABLE :
case OpTypes.PARAMETER :
case OpTypes.COLUMN : {
if (column == null) {
if (alias != null) {
return alias.getStatementName();
} else {
return columnName;
}
}
if (rangeVariable.tableAlias == null) {
return column.getName().getSchemaQualifiedStatementName();
} else {
StringBuffer sb = new StringBuffer();
sb.append(rangeVariable.tableAlias.getStatementName());
sb.append('.');
sb.append(column.getName().statementName);
return sb.toString();
}
}
case OpTypes.MULTICOLUMN : {
if (nodes.length == 0) {
return "*";
}
StringBuffer sb = new StringBuffer();
for (int i = 0; i < nodes.length; i++) {
Expression e = nodes[i];
if (i > 0) {
sb.append(',');
}
String s = e.getSQL();
sb.append(s);
}
return sb.toString();
}
default :
throw Error.runtimeError(ErrorCode.U_S0500, "Expression");
}
}
protected String describe(Session session, int blanks) {
StringBuffer sb = new StringBuffer(64);
sb.append('\n');
for (int i = 0; i < blanks; i++) {
sb.append(' ');
}
switch (opType) {
case OpTypes.DEFAULT :
sb.append(Tokens.T_DEFAULT);
break;
case OpTypes.ASTERISK :
sb.append("OpTypes.ASTERISK ");
break;
case OpTypes.VARIABLE :
sb.append("VARIABLE: ");
sb.append(column.getName().name);
break;
case OpTypes.PARAMETER :
sb.append(Tokens.T_PARAMETER).append(": ");
sb.append(column.getName().name);
break;
case OpTypes.COALESCE :
sb.append(Tokens.T_COLUMN).append(": ");
sb.append(columnName);
if (alias != null) {
sb.append(" AS ").append(alias.name);
}
break;
case OpTypes.COLUMN :
sb.append(Tokens.T_COLUMN).append(": ");
sb.append(column.getName().name);
if (alias != null) {
sb.append(" AS ").append(alias.name);
}
sb.append(' ').append(Tokens.T_TABLE).append(": ").append(
tableName);
break;
case OpTypes.DYNAMIC_PARAM :
sb.append("DYNAMIC PARAM: ");
sb.append(", TYPE = ").append(dataType.getNameString());
break;
case OpTypes.SEQUENCE :
sb.append(Tokens.T_SEQUENCE).append(": ");
sb.append(sequence.getName().name);
break;
case OpTypes.MULTICOLUMN :
// shouldn't get here
}
return sb.toString();
}
/**
* Returns the table name for a column expression as a string
*
* @return table name
*/
String getTableName() {
if (opType == OpTypes.MULTICOLUMN) {
return tableName;
}
if (opType == OpTypes.COLUMN) {
if (rangeVariable == null) {
return tableName;
} else {
return rangeVariable.getTable().getName().name;
}
}
return "";
}
static void checkColumnsResolved(HsqlList set) {
if (set != null && !set.isEmpty()) {
ExpressionColumn e = (ExpressionColumn) set.get(0);
StringBuffer sb = new StringBuffer();
if (e.schema != null) {
sb.append(e.schema + '.');
}
if (e.tableName != null) {
sb.append(e.tableName + '.');
}
throw Error.error(ErrorCode.X_42501,
sb.toString() + e.getColumnName());
}
}
public OrderedHashSet getUnkeyedColumns(OrderedHashSet unresolvedSet) {
for (int i = 0; i < nodes.length; i++) {
if (nodes[i] == null) {
continue;
}
unresolvedSet = nodes[i].getUnkeyedColumns(unresolvedSet);
}
if (opType == OpTypes.COLUMN
&& !rangeVariable.hasKeyedColumnInGroupBy) {
if (unresolvedSet == null) {
unresolvedSet = new OrderedHashSet();
}
unresolvedSet.add(this);
}
return unresolvedSet;
}
/**
* collects all range variables in expression tree
*/
void collectRangeVariables(RangeVariable[] rangeVariables, Set set) {
for (int i = 0; i < nodes.length; i++) {
if (nodes[i] != null) {
nodes[i].collectRangeVariables(rangeVariables, set);
}
}
if (rangeVariable != null) {
for (int i = 0; i < rangeVariables.length; i++) {
if (rangeVariables[i] == rangeVariable) {
set.add(rangeVariables[i]);
}
}
}
}
Expression replaceAliasInOrderBy(Expression[] columns, int length) {
for (int i = 0; i < nodes.length; i++) {
if (nodes[i] == null) {
continue;
}
nodes[i] = nodes[i].replaceAliasInOrderBy(columns, length);
}
switch (opType) {
case OpTypes.COALESCE :
case OpTypes.COLUMN : {
for (int i = 0; i < length; i++) {
SimpleName aliasName = columns[i].alias;
String alias = aliasName == null ? null
: aliasName.name;
if (schema == null && tableName == null
&& columnName.equals(alias)) {
return columns[i];
}
}
for (int i = 0; i < length; i++) {
if (columns[i] instanceof ExpressionColumn) {
if (this.equals(columns[i])) {
return columns[i];
}
if (tableName == null && schema == null
&& columnName
.equals(((ExpressionColumn) columns[i])
.columnName)) {
return columns[i];
}
}
}
}
default :
}
return this;
}
Expression replaceColumnReferences(RangeVariable range,
Expression[] list) {
if (opType == OpTypes.COLUMN && rangeVariable == range) {
return list[columnIndex];
}
for (int i = 0; i < nodes.length; i++) {
if (nodes[i] == null) {
continue;
}
nodes[i] = nodes[i].replaceColumnReferences(range, list);
}
return this;
}
int findMatchingRangeVariableIndex(RangeVariable[] rangeVarArray) {
for (int i = 0; i < rangeVarArray.length; i++) {
RangeVariable rangeVar = rangeVarArray[i];
if (rangeVar.resolvesTableName(this)) {
return i;
}
}
return -1;
}
/**
* return true if given RangeVariable is used in expression tree
*/
boolean hasReference(RangeVariable range) {
if (range == rangeVariable) {
return true;
}
for (int i = 0; i < nodes.length; i++) {
if (nodes[i] != null) {
if (nodes[i].hasReference(range)) {
return true;
}
}
}
return false;
}
public boolean equals(Expression other) {
if (other == this) {
return true;
}
if (other == null) {
return false;
}
if (opType != other.opType) {
return false;
}
switch (opType) {
case OpTypes.SIMPLE_COLUMN :
return this.columnIndex == other.columnIndex;
case OpTypes.COALESCE :
return nodes == other.nodes;
case OpTypes.COLUMN :
return column == other.getColumn();
default :
return false;
}
}
/*************** VOLTDB *********************/
/**
* VoltDB added method to get a non-catalog-dependent
* representation of this HSQLDB object.
* @param session The current Session object may be needed to resolve
* some names.
* @param indent A string of whitespace to be prepended to every line
* in the resulting XML.
* @return XML, correctly indented, representing this object.
*/
String voltGetXML(Session session, String indent) throws HSQLParseException
{
StringBuffer sb = new StringBuffer();
//
// We want to keep track of which expressions are the same in the XML output
//
String include = "id=\"" + this.getUniqueId() + "\" ";
if (opType == OpTypes.ASTERISK) {
sb.append(indent).append("<asterisk/>");
}
else if (isParam) {
sb.append(indent).append("<value ").append(include);
sb.append("type=\"").append(Types.getTypeName(dataType.typeCode)).append("\" ");
sb.append("isparam=\"true\" ");
sb.append("/>");
}
else {
sb.append(indent).append("<columnref ").append(include);
if (tableName != null)
sb.append("table=\"").append(tableName).append("\" ");
sb.append("column=\"").append(columnName).append("\" ");
sb.append("alias=\"").append((this.alias != null) && (getAlias().length() > 0) ? getAlias() : columnName).append("\" ");
sb.append("/>");
}
return sb.toString();
}
}