/* Copyright (c) 2001-2010, 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.HsqlNameManager.HsqlName;
import org.hsqldb.HsqlNameManager.SimpleName;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
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
//
boolean isParam;
//
boolean strictReference;
/**
* Creates a OpCodes.COLUMN expression
*/
ExpressionColumn(String schema, String table, String column,
boolean strictReference) {
super(OpTypes.COLUMN);
this.schema = schema;
this.tableName = table;
this.columnName = column;
this.strictReference = strictReference;
}
ExpressionColumn(ColumnSchema column) {
super(OpTypes.COLUMN);
this.column = column;
this.dataType = column.getDataType();
columnName = column.getName().name;
}
ExpressionColumn(RangeVariable rangeVar, int index) {
super(OpTypes.COLUMN);
columnIndex = index;
setAutoAttributesAsColumn(rangeVar, columnIndex);
}
/**
* Creates a temporary OpCodes.COLUMN expression
*/
ExpressionColumn(Expression e, int colIndex, int rangePosition) {
super(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 setAutoAttributesAsColumn(RangeVariable range, int i) {
columnIndex = i;
column = range.getColumn(i);
dataType = column.getDataType();
columnName = range.getColumnAlias(i);
tableName = range.getTableAlias();
rangeVariable = range;
rangeVariable.addColumn(columnIndex);
}
void setAttributesAsColumn(RangeVariable range, int i) {
if (range.variables != null) {
columnIndex = i;
column = range.getColumn(i);
dataType = column.getDataType();
rangeVariable = range;
} else {
columnIndex = i;
column = range.getColumn(i);
dataType = column.getDataType();
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) {
switch (opType) {
case OpTypes.SEQUENCE :
HsqlName name = sequence.getName();
set.add(name);
return;
case OpTypes.MULTICOLUMN :
case OpTypes.DYNAMIC_PARAM :
case OpTypes.ASTERISK :
case OpTypes.SIMPLE_COLUMN :
case OpTypes.COALESCE :
break;
case OpTypes.PARAMETER :
case OpTypes.VARIABLE :
break;
case OpTypes.COLUMN :
set.add(column.getName());
if (column.getName().parent != null) {
set.add(column.getName().parent);
}
return;
}
}
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 : {
boolean resolved = false;
boolean tableQualified = tableName != null;
if (rangeVariable != null) {
return unresolvedSet;
}
for (int i = 0; i < rangeCount; i++) {
RangeVariable rangeVar = rangeVarArray[i];
if (rangeVar == null) {
continue;
}
if (resolved) {
if (resolvesDuplicateColumnReference(rangeVar)) {
if (strictReference) {
String message = getColumnName();
if (alias != null) {
StringBuffer sb =
new StringBuffer(message);
sb.append(' ').append(Tokens.T_AS).append(
' ').append(alias.getStatementName());
message = sb.toString();
}
throw Error.error(ErrorCode.X_42580, message);
}
}
} else {
if (resolveColumnReference(rangeVar)) {
if (tableQualified) {
return unresolvedSet;
}
resolved = true;
continue;
}
}
}
if (resolved) {
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;
}
boolean resolvesDuplicateColumnReference(RangeVariable rangeVar) {
if (tableName == null) {
Expression e = rangeVar.getColumnExpression(columnName);
if (e != null) {
return false;
}
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 {
return true;
}
}
}
if (!rangeVar.resolvesTableName(this)) {
return false;
}
int colIndex = rangeVar.findColumn(columnName);
if (colIndex != -1) {
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];
if (dataType != column.dataType) {
value = dataType.convertToType(session, value,
column.dataType);
}
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,
"ExpressionColumn");
}
}
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 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,
"ExpressionColumn");
}
}
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().getSchemaQualifiedStatementName());
if (alias != null) {
sb.append(" AS ").append(alias.name);
}
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 used in query
*
* @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()) {
StringBuffer sb = new StringBuffer();
Expression e = (Expression) set.get(0);
if (e instanceof ExpressionColumn) {
ExpressionColumn c = (ExpressionColumn) e;
if (c.schema != null) {
sb.append(c.schema + '.');
}
if (c.tableName != null) {
sb.append(c.tableName + '.');
}
throw Error.error(ErrorCode.X_42501,
sb.toString() + c.getColumnName());
} else {
throw Error.error(ErrorCode.X_42501);
}
}
}
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(rangeVariable);
break;
}
}
}
}
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;
}
/**
* SIMPLE_COLUMN expressions can be of different Java types
*/
public boolean equals(Expression other) {
if (other == this) {
return true;
}
if (other == null) {
return false;
}
if (opType != ((Expression) other).opType) {
return false;
}
switch (opType) {
case OpTypes.SIMPLE_COLUMN :
return this.columnIndex == ((Expression) other).columnIndex;
case OpTypes.COALESCE :
return nodes == ((Expression) other).nodes;
case OpTypes.COLUMN :
return column == ((Expression) other).getColumn();
default :
return false;
}
}
void replaceRangeVariables(RangeVariable[] ranges,
RangeVariable[] newRanges) {
for (int i = 0; i < nodes.length; i++) {
nodes[i].replaceRangeVariables(ranges, newRanges);
}
for (int i = 0; i < ranges.length; i++) {
if (rangeVariable == ranges[i]) {
rangeVariable = newRanges[i];
break;
}
}
}
void resetColumnReferences() {
rangeVariable = null;
columnIndex = -1;
}
public boolean isIndexable(RangeVariable range) {
if (opType == OpTypes.COLUMN) {
return rangeVariable == range;
}
return false;
}
public boolean isUnresolvedParam() {
return isParam && dataType == null;
}
boolean isDynamicParam() {
return isParam;
}
}