package org.hsqldb;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.ArrayListIdentity;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.HsqlList;
import org.hsqldb.types.ArrayType;
import org.hsqldb.types.RowType;
import org.hsqldb.types.Type;
public class ExpressionArrayAggregate extends Expression {
SortAndSlice sort;
SortAndSlice distinctSort;
String separator = ",";
ArrayType arrayDataType;
Type exprType;
Expression condition = Expression.EXPR_TRUE;
ExpressionArrayAggregate(int type, boolean distinct, Expression e,
SortAndSlice sort, String separator) {
super(type);
this.isDistinctAggregate = distinct;
this.sort = sort;
if (separator != null) {
this.separator = separator;
}
if (type == OpTypes.MEDIAN) {
nodes = new Expression[]{ e };
return;
}
if (sort == null) {
nodes = new Expression[]{ e };
} else {
HsqlArrayList list = sort.getExpressionList();
nodes = new Expression[list.size() + 1];
list.toArray(nodes);
nodes[list.size()] = e;
sort.prepareExtraColumn(1);
}
if (isDistinctAggregate) {
distinctSort = new SortAndSlice();
distinctSort.prepareSingleColumn(nodes.length - 1);
}
}
public boolean isSelfAggregate() {
return true;
}
public String getSQL() {
StringBuilder sb = new StringBuilder(64);
String left = getContextSQL(nodes.length > 0 ? nodes[LEFT]
: null);
switch (opType) {
case OpTypes.ARRAY_AGG :
sb.append(' ').append(Tokens.T_ARRAY_AGG).append('(');
sb.append(left).append(')');
break;
case OpTypes.GROUP_CONCAT :
sb.append(' ').append(Tokens.T_GROUP_CONCAT).append('(');
sb.append(left).append(')');
break;
case OpTypes.MEDIAN :
sb.append(' ').append(Tokens.T_MEDIAN).append('(');
sb.append(left).append(')');
break;
default :
throw Error.runtimeError(ErrorCode.U_S0500,
"ExpressionAggregate");
}
return sb.toString();
}
protected String describe(Session session, int blanks) {
StringBuilder sb = new StringBuilder(64);
sb.append('\n');
for (int i = 0; i < blanks; i++) {
sb.append(' ');
}
switch (opType) {
case OpTypes.ARRAY_AGG :
sb.append(Tokens.T_ARRAY_AGG).append(' ');
break;
case OpTypes.GROUP_CONCAT :
sb.append(Tokens.T_GROUP_CONCAT).append(' ');
break;
case OpTypes.MEDIAN :
sb.append(Tokens.T_MEDIAN).append(' ');
break;
default :
}
if (getLeftNode() != null) {
sb.append(" arg=[");
sb.append(nodes[LEFT].describe(session, blanks + 1));
sb.append(']');
}
return sb.toString();
}
public HsqlList resolveColumnReferences(Session session,
RangeGroup rangeGroup, int rangeCount, RangeGroup[] rangeGroups,
HsqlList unresolvedSet, boolean acceptsSequences) {
HsqlList conditionSet = condition.resolveColumnReferences(session,
rangeGroup, rangeCount, rangeGroups, null, false);
if (conditionSet != null) {
ExpressionColumn.checkColumnsResolved(conditionSet);
}
if (unresolvedSet == null) {
unresolvedSet = new ArrayListIdentity();
}
unresolvedSet.add(this);
if (rangeGroup.getRangeVariables().length > 0) {
this.rangeGroups = rangeGroups;
this.rangeGroup = rangeGroup;
}
return unresolvedSet;
}
public void resolveTypes(Session session, Expression parent) {
nodeDataTypes = new Type[nodes.length];
for (int i = 0; i < nodes.length; i++) {
if (nodes[i] != null) {
nodes[i].resolveTypes(session, this);
if (nodes[i].isUnresolvedParam()) {
throw Error.error(ErrorCode.X_42567);
}
if (nodes[i].dataType == null) {
throw Error.error(ErrorCode.X_42567);
}
nodeDataTypes[i] = nodes[i].dataType;
}
}
exprType = nodes[nodes.length - 1].dataType;
if (exprType.isLobType()) {
throw Error.error(ErrorCode.X_42534);
}
if (exprType.isArrayType()) {
throw Error.error(ErrorCode.X_42534);
}
Type rowDataType = new RowType(nodeDataTypes);
switch (opType) {
case OpTypes.ARRAY_AGG :
arrayDataType =
new ArrayType(rowDataType,
ArrayType.defaultArrayCardinality);
dataType = new ArrayType(exprType,
ArrayType.defaultArrayCardinality);
break;
case OpTypes.GROUP_CONCAT :
arrayDataType =
new ArrayType(rowDataType,
ArrayType.defaultArrayCardinality);
dataType = Type.SQL_VARCHAR_DEFAULT;
break;
case OpTypes.MEDIAN :
arrayDataType =
new ArrayType(nodeDataTypes[0],
ArrayType.defaultArrayCardinality);
dataType = ExpressionAggregate.getType(session,
OpTypes.MEDIAN,
exprType);
if (!exprType.isNumberType()) {
throw Error.error(ErrorCode.X_42563);
}
break;
}
condition.resolveTypes(session, null);
}
boolean equals(Expression other) {
if (other instanceof ExpressionArrayAggregate) {
ExpressionArrayAggregate o = (ExpressionArrayAggregate) other;
return super.equals(other) && opType == other.opType
&& exprSubType == other.exprSubType
&& isDistinctAggregate == o.isDistinctAggregate
&& separator.equals(o.separator)
&& condition.equals(o.condition);
}
return false;
}
public SetFunction updateAggregatingValue(Session session,
SetFunction currValue) {
if (!condition.testCondition(session)) {
return currValue;
}
Object value = null;
Object[] data;
switch (opType) {
case OpTypes.ARRAY_AGG :
data = new Object[nodes.length];
for (int i = 0; i < nodes.length; i++) {
data[i] = nodes[i].getValue(session);
}
value = data;
break;
case OpTypes.GROUP_CONCAT :
data = new Object[nodes.length];
for (int i = 0; i < nodes.length; i++) {
data[i] = nodes[i].getValue(session);
}
if (data[data.length - 1] == null) {
return currValue;
}
value = data;
break;
case OpTypes.MEDIAN :
value = nodes[0].getValue(session);
if (value == null) {
return currValue;
}
break;
}
if (currValue == null) {
currValue = new SetFunctionValueArray();
}
currValue.add(value);
return currValue;
}
public Object getAggregatedValue(Session session, SetFunction currValue) {
if (currValue == null) {
return null;
}
Object[] array = (Object[]) currValue.getValue();
if (isDistinctAggregate) {
arrayDataType.sort(session, array, distinctSort);
int size = arrayDataType.deDuplicate(session, array, distinctSort);
array = (Object[]) ArrayUtil.resizeArrayIfDifferent(array, size);
}
if (sort != null) {
arrayDataType.sort(session, array, sort);
}
switch (opType) {
case OpTypes.ARRAY_AGG : {
Object[] resultArray = new Object[array.length];
for (int i = 0; i < array.length; i++) {
Object[] row = (Object[]) array[i];
resultArray[i] = row[row.length - 1];
}
return resultArray;
}
case OpTypes.GROUP_CONCAT : {
StringBuilder sb = new StringBuilder(16 * array.length);
for (int i = 0; i < array.length; i++) {
if (i > 0) {
sb.append(separator);
}
Object[] row = (Object[]) array[i];
Object value = row[row.length - 1];
String string =
(String) Type.SQL_VARCHAR.convertToType(session,
value, exprType);
sb.append(string);
}
return sb.toString();
}
case OpTypes.MEDIAN : {
SortAndSlice exprSort = new SortAndSlice();
exprSort.prepareSingleColumn(1);
arrayDataType.sort(session, array, exprSort);
boolean even = array.length % 2 == 0;
if (even) {
Object val1 = array[(array.length / 2) - 1];
Object val2 = array[array.length / 2];
Object val3 = dataType.add(session, val1, val2, dataType);
return dataType.divide(session, val3, Integer.valueOf(2));
} else {
return dataType.convertToType(session,
array[array.length / 2],
exprType);
}
}
}
return null;
}
public Expression getCondition() {
return condition;
}
public boolean hasCondition() {
return condition != null && !condition.isTrue();
}
public void setCondition(ExpressionLogical e) {
condition = e;
}
public Expression duplicate() {
ExpressionArrayAggregate e =
(ExpressionArrayAggregate) super.duplicate();
if (condition != null) {
e.condition = condition.duplicate();
}
return e;
}
}