/* 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_voltpatches;
import java.util.List;
import org.hsqldb_voltpatches.HSQLInterface.HSQLParseException;
import org.hsqldb_voltpatches.lib.HsqlList;
import org.hsqldb_voltpatches.types.Type;
/**
* Implementation of RANK operations
*
* @author Xin Jia
*/
public class ExpressionWindowed extends Expression {
private List<Expression> m_partitionByList;
private SortAndSlice m_sortAndSlice;
private boolean m_isDistinctAggregate;
ExpressionWindowed(int tokenT,
Expression aggExprs[],
boolean isDistinct,
SortAndSlice sortAndSlice,
List<Expression> partitionByList) {
super(ParserBase.getWindowedExpressionType(tokenT));
nodes = aggExprs;
m_isDistinctAggregate = isDistinct;
m_partitionByList = partitionByList;
m_sortAndSlice = sortAndSlice;
validateWindowedSyntax();
}
/**
* Validate that this is a collection of values.
*/
private void validateWindowedSyntax() {
// Check that the aggregate is one of the supported ones, and
// that the number of aggregate parameters is right.
switch (opType) {
case OpTypes.WINDOWED_RANK:
case OpTypes.WINDOWED_DENSE_RANK:
if (nodes.length != 0) {
throw Error.error("Windowed Aggregate " + OpTypes.aggregateName(opType) + " expects no arguments.", "", 0);
}
break;
case OpTypes.WINDOWED_COUNT:
case OpTypes.WINDOWED_MIN:
case OpTypes.WINDOWED_MAX:
case OpTypes.WINDOWED_SUM:
break;
default:
throw Error.error("Unsupported window function " + OpTypes.aggregateName(opType), "", 0);
}
}
@Override
public Object getValue(Session session) {
return 0;
}
/**
* Returns the data type
*/
@Override
Type getDataType() {
switch (opType) {
case OpTypes.WINDOWED_RANK:
case OpTypes.WINDOWED_DENSE_RANK:
case OpTypes.WINDOWED_COUNT:
return Type.SQL_BIGINT;
case OpTypes.WINDOWED_MAX:
case OpTypes.WINDOWED_MIN:
case OpTypes.WINDOWED_SUM:
return dataType;
default:
throw Error.error("Unsupported windowed function " + OpTypes.aggregateName(opType), "", 0);
}
}
@Override
public HsqlList resolveColumnReferences(RangeVariable[] rangeVarArray,
int rangeCount, HsqlList unresolvedSet, boolean acceptsSequences) {
HsqlList localSet = null;
// Resolve the aggregate expression. For the RANK-like aggregates
// this is a no-op, because nodes is empty.
for (Expression e : nodes) {
localSet = e.resolveColumnReferences(RangeVariable.emptyArray, localSet);
}
for (Expression e : m_partitionByList) {
localSet = e.resolveColumnReferences(
RangeVariable.emptyArray, localSet);
}
if (m_sortAndSlice != null) {
for (int i = 0; i < m_sortAndSlice.exprList.size(); i++) {
Expression e = (Expression) m_sortAndSlice.exprList.get(i);
assert(e instanceof ExpressionOrderBy);
ExpressionOrderBy expr = (ExpressionOrderBy)e;
localSet = expr.resolveColumnReferences(
RangeVariable.emptyArray, localSet);
}
}
if (localSet != null) {
isCorrelated = true;
for (int i = 0; i < localSet.size(); i++) {
Expression e = (Expression) localSet.get(i);
unresolvedSet = e.resolveColumnReferences(rangeVarArray,
unresolvedSet);
}
unresolvedSet = Expression.resolveColumnSet(rangeVarArray,
localSet, unresolvedSet);
}
return unresolvedSet;
}
@Override
public void resolveTypes(Session session, Expression parent) {
for (Expression expr : nodes) {
expr.resolveTypes(session, parent);
}
for (Expression expr : m_partitionByList) {
expr.resolveTypes(session, parent);
}
if (m_sortAndSlice != null) {
for (int i = 0; i < m_sortAndSlice.exprList.size(); i++) {
Expression e = (Expression) m_sortAndSlice.exprList.get(i);
e.resolveTypes(session, parent);
}
}
dataType = Type.SQL_BIGINT;
}
@Override
public String getSQL() {
StringBuffer sb = new StringBuffer();
sb.append(OpTypes.aggregateName(opType)).append("(");
for (Expression e : nodes) {
sb.append(e.getSQL());
}
sb.append(") ")
.append(Tokens.T_OVER + " (");
if (m_partitionByList.size() > 0) {
sb.append(Tokens.T_PARTITION + ' ' + Tokens.T_BY + ' ');
String sep = "";
for (int idx = 0; idx < m_partitionByList.size(); idx += 1) {
Expression expr = m_partitionByList.get(idx);
sb.append(sep)
.append(expr.getSQL());
sep = ", ";
}
}
if (m_sortAndSlice != null && m_sortAndSlice.getOrderLength() > 0) {
sb.append(Tokens.T_ORDER + ' ' + Tokens.T_BY + ' ');
for (int idx = 0; idx < m_sortAndSlice.getOrderLength(); idx += 1) {
Expression obExpr = (Expression) m_sortAndSlice.exprList.get(idx);
assert(obExpr instanceof ExpressionOrderBy);
ExpressionOrderBy obOrderByExpression = (ExpressionOrderBy)obExpr;
sb.append(obExpr.getSQL())
.append(' ')
.append(obOrderByExpression.isDescending() ? Tokens.T_DESC : Tokens.T_ASC);
}
}
sb.append(")");
return sb.toString();
}
@Override
protected String describe(Session session, int blanks) {
return getSQL();
}
/**
* Create a VoltXMLElement for a windowed aggregate expression. The
* children are parts of the expression. For example, consider the
* expression <code>MAX(A+B) OVER (PARTITION BY E1, E2 ORDER BY E3 ASC)</code>.
* There will be these children.
* <ul>
* <li>A child named "winspec" with the windowed specification. This
* will have two children.
* <ul>
* <li>One will be named "partitionbyList", and will contain the
* expressions E1 and E2.</li>
* <li>The other will contain a list of expressions and sort orders
* for the order by list, <E3, ASC>.</li>
* </ul>
* </li>
* <li>All other children are the arguments to the aggregate. This
* would be <code>A+B</code> in the expression above. Note that there are no
* arguments to the rank functions, so this will be empty for the rank functions.
* </ul>
*
* @param exp
* @param context
* @return
* @throws HSQLParseException
*/
public VoltXMLElement voltAnnotateWindowedAggregateXML(VoltXMLElement exp, SimpleColumnContext context)
throws HSQLParseException {
VoltXMLElement winspec = new VoltXMLElement("winspec");
exp.children.add(winspec);
if (m_partitionByList.size() > 0) {
VoltXMLElement pxe = new VoltXMLElement("partitionbyList");
winspec.children.add(pxe);
for (Expression e : m_partitionByList) {
pxe.children.add(e.voltGetXML(context, null));
}
}
VoltXMLElement rxe = new VoltXMLElement("orderbyList");
winspec.children.add(rxe);
if (m_sortAndSlice != null) {
for (int i = 0; i < m_sortAndSlice.exprList.size(); i++) {
Expression e = (Expression) m_sortAndSlice.exprList.get(i);
assert(e instanceof ExpressionOrderBy);
ExpressionOrderBy expr = (ExpressionOrderBy)e;
VoltXMLElement orderby = expr.voltGetXML(context, null);
boolean isDescending = expr.isDescending();
orderby.attributes.put("descending", isDescending ? "true": "false");
rxe.children.add(orderby);
}
}
return exp;
}
public boolean isDistinct() {
return m_isDistinctAggregate;
}
}