/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with VoltDB. If not, see <http://www.gnu.org/licenses/>.
*/
package org.voltdb.expressions;
import java.util.ArrayList;
import java.util.List;
import org.voltdb.types.ExpressionType;
import org.voltdb.types.SortDirectionType;
/**
* Objects of this class represent windowed expressions in the Volt
* AST. We currently implement only the RANK function, and only the
* slow path. This class for the slow path is just a container with
* three pieces.
* <ol>
* <li>A sequence of partition by expressions.</li>
* <li>A sequence of pairs of order by expressions and sort directions.</li>
* <li> An aggregate operation.
* </ol>
* The aggregate operation is sorted in the AbstractExpression base class.
*
* There will be a fast path which will need an index and a single
* table, as well as perhaps some other metadata. But we currently just
* implement the slow path.
*/
public class WindowFunctionExpression extends AbstractExpression {
public enum Members {
PARTITION_BY_EXPRESSIONS
}
public static SortDirectionType DEFAULT_ORDER_BY_DIRECTION = SortDirectionType.ASC;
private List<AbstractExpression> m_partitionByExpressions = new ArrayList<>();
private List<AbstractExpression> m_orderByExpressions = new ArrayList<>();
private List<SortDirectionType> m_orderByDirections = new ArrayList<>();
// This object is not in the display list. It's squirreled away in the ParsedSelectStatment. But
// the display list has a TVE which references the column which holds the values this aggregate
// expression will compute. This field holds this TVE.
private TupleValueExpression m_displayListExpression;
private int m_xmlID = -1;
private boolean m_isDistinct = false;
public WindowFunctionExpression() {
//
// This is needed for serialization
//
super();
}
public WindowFunctionExpression(
ExpressionType operationType, // RANK, MAX, etc.
List<AbstractExpression> partitionbyExprs,
List<AbstractExpression> orderbyExprs,
List<SortDirectionType> orderByDirections,
List<AbstractExpression> aggArguments,
int id)
{
super(operationType);
m_partitionByExpressions.addAll(partitionbyExprs);
m_orderByExpressions.addAll(orderbyExprs);
m_orderByDirections.addAll(orderByDirections);
if (m_args == null) {
m_args = new ArrayList<>();
}
m_args.addAll(aggArguments);
finalizeValueTypes();
m_xmlID = id;
}
public int getOrderbySize() {
return m_orderByExpressions.size();
}
public int getPartitionbySize() {
return m_partitionByExpressions.size();
}
public List<AbstractExpression> getPartitionByExpressions() {
return m_partitionByExpressions;
}
public List<AbstractExpression> getOrderByExpressions() {
return m_orderByExpressions;
}
public List<SortDirectionType> getOrderByDirections() {
return m_orderByDirections;
}
public List<AbstractExpression> getAggregateArguments() {
return m_args;
}
public boolean hasSubqueryArgs() {
if (m_args != null) {
for (AbstractExpression arg : m_args) {
if (arg.hasSubquerySubexpression()) {
return true;
}
}
}
return false;
}
@Override
public boolean equals(Object obj) {
if (super.equals(obj) && obj instanceof WindowFunctionExpression) {
WindowFunctionExpression oWindow = (WindowFunctionExpression)obj;
if (m_orderByExpressions.equals(oWindow.getOrderByExpressions())
&& m_orderByDirections.equals(oWindow.getOrderByDirections())
&& m_partitionByExpressions.equals(oWindow.getPartitionByExpressions())) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
int hash = super.hashCode();
hash += m_orderByDirections.hashCode();
hash += m_orderByExpressions.hashCode();
hash += m_partitionByExpressions.hashCode();
hash += m_args.hashCode();
return hash;
}
@Override
public void finalizeValueTypes() {
// This is a kind of logical aggregate
// function, even if it is not a subclass
// of AggregateExpression.
AggregateExpression.finalizeAggregateValueTypes(this);
}
@Override
public String explain(String impliedTableName) {
StringBuilder sb = new StringBuilder();
sb.append("WINDOW expression").append(" expression");
return sb.toString();
}
/*
* Functions to find subexpressions by class. We need to search the
* partition by and order by lists.
*/
@Override
public <aeClass> List<aeClass> findAllSubexpressionsOfClass(Class< ? extends AbstractExpression> aeClass) {
List<aeClass> list = super.findAllSubexpressionsOfClass(aeClass);
for (AbstractExpression pbexpr : m_partitionByExpressions) {
list.addAll(pbexpr.findAllSubexpressionsOfClass(aeClass));
}
for (AbstractExpression sortExpr : m_orderByExpressions) {
list.addAll(sortExpr.findAllSubexpressionsOfClass(aeClass));
}
for (AbstractExpression aggExpr : m_args) {
list.addAll(aggExpr.findAllSubexpressionsOfClass(aeClass));
}
return list;
}
@Override
public boolean hasAnySubexpressionOfClass(Class< ? extends AbstractExpression> aeClass) {
if (super.hasAnySubexpressionOfClass(aeClass)) {
return true;
}
for (AbstractExpression pbexpr : m_partitionByExpressions) {
if (pbexpr.hasAnySubexpressionOfClass(aeClass)) {
return true;
}
}
for (AbstractExpression sortExpr : m_orderByExpressions) {
if (sortExpr.hasAnySubexpressionOfClass(aeClass)) {
return true;
}
}
for (AbstractExpression aggExpr : m_args) {
if (aggExpr.hasAnySubexpressionOfClass(aeClass)) {
return true;
}
}
return false;
}
/**
* Return the index of the given partition by expression in the
* order by list. This is used when trying to rationalize partition by
* and order by expressions.
*
* @param partitionByExpression
* @return
*/
public int getSortIndexOfOrderByExpression(AbstractExpression partitionByExpression) {
for (int idx = 0; idx < m_orderByExpressions.size(); ++idx) {
if (m_orderByExpressions.get(idx).equals(partitionByExpression)) {
return idx;
}
}
return -1;
}
public final TupleValueExpression getDisplayListExpression() {
return m_displayListExpression;
}
public final void setDisplayListExpression(TupleValueExpression displayListExpression) {
m_displayListExpression = displayListExpression;
}
/**
* When a VoltXMLElement is translated to an expression, we remember the
* ID. We may see it again in the order by expression. This gets the ID number.
* @return
*/
public final int getXMLID() {
return m_xmlID;
}
public boolean getIsDistinct() {
// TODO Auto-generated method stub
return m_isDistinct;
}
}