/* 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 org.apache.commons.lang3.builder.HashCodeBuilder;
import org.voltdb.VoltType;
import org.voltdb.types.ExpressionType;
public class AggregateExpression extends AbstractExpression {
/** True if this aggregate requires distinct: e.g. count(distinct A) */
private boolean m_distinct = false;
public AggregateExpression(ExpressionType type) {
super(type);
}
public AggregateExpression() {
//
// This is needed for serialization
//
super();
}
public void setDistinct() { m_distinct = true; }
public boolean isDistinct() { return m_distinct; }
@Override
public boolean equals(Object obj) {
if(super.equals(obj) == false) return false;
if (obj instanceof AggregateExpression == false) return false;
AggregateExpression expr = (AggregateExpression) obj;
if (m_distinct != expr.isDistinct()) return false;
return true;
}
@Override
public int hashCode() {
// based on implementation of equals
int result = super.hashCode();
result += new HashCodeBuilder(17, 31).append(m_distinct).toHashCode();
return result;
}
private static final String FLOAT_AGG_ERR_MSG = "Aggregate functions of floating point columns may not be deterministic. We suggest converting to DECIMAL.";
@Override
public void finalizeValueTypes()
{
finalizeAggregateValueTypes(this);
}
public static void finalizeAggregateValueTypes(AbstractExpression expr)
{
expr.finalizeChildValueTypes();
ExpressionType type = expr.getExpressionType();
AbstractExpression aggArg;
switch (type) {
case AGGREGATE_COUNT:
case AGGREGATE_WINDOWED_RANK:
case AGGREGATE_WINDOWED_DENSE_RANK:
case AGGREGATE_WINDOWED_COUNT:
case AGGREGATE_COUNT_STAR:
case AGGREGATE_APPROX_COUNT_DISTINCT:
case AGGREGATE_HYPERLOGLOGS_TO_CARD:
//
// Always an integer
//
expr.m_valueType = VoltType.BIGINT;
expr.m_valueSize = expr.m_valueType.getLengthInBytesForFixedTypes();
break;
case AGGREGATE_VALS_TO_HYPERLOGLOG:
expr.m_valueType = VoltType.VARBINARY;
expr.m_valueSize = 65537;
break;
case AGGREGATE_AVG:
case AGGREGATE_MAX:
case AGGREGATE_MIN:
case AGGREGATE_WINDOWED_MIN:
case AGGREGATE_WINDOWED_MAX:
//
// It's always whatever the base type is
//
aggArg = expr.getFirstArgument();
assert(aggArg != null);
expr.m_valueType = aggArg.getValueType();
expr.m_valueSize = aggArg.getValueSize();
// Of these aggregate functions, only AVG is
// non-deterministic on floating point types.
if (expr.m_valueType == VoltType.FLOAT && type == ExpressionType.AGGREGATE_AVG) {
expr.updateContentDeterminismMessage(FLOAT_AGG_ERR_MSG);
}
break;
case AGGREGATE_WINDOWED_SUM:
case AGGREGATE_SUM:
aggArg = expr.getFirstArgument();
assert(aggArg != null);
if (aggArg.getValueType() == VoltType.TINYINT ||
aggArg.getValueType() == VoltType.SMALLINT ||
aggArg.getValueType() == VoltType.INTEGER) {
expr.m_valueType = VoltType.BIGINT;
expr.m_valueSize = expr.m_valueType.getLengthInBytesForFixedTypes();
} else {
expr.m_valueType = aggArg.getValueType();
expr.m_valueSize = aggArg.getValueSize();
}
if (expr.m_valueType == VoltType.FLOAT) {
expr.updateContentDeterminismMessage(FLOAT_AGG_ERR_MSG);
}
break;
default:
throw new RuntimeException("ERROR: Invalid Expression type '" + type + "' for Expression '" + expr + "'");
}
}
@Override
public String explain(String impliedTableName) {
ExpressionType type = getExpressionType();
if (type == ExpressionType.AGGREGATE_COUNT_STAR) {
return "COUNT(*)";
}
return type.symbol() + ( m_distinct ? " DISTINCT(" : "(" ) +
m_left.explain(impliedTableName) + ")";
}
}