/*
* Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. Crate licenses
* this file to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* However, if you have executed another commercial license agreement
* with Crate these terms will supersede the license and you may use the
* software solely pursuant to the terms of the relevant commercial agreement.
*/
package io.crate.planner.node.dql;
import io.crate.analyze.WhereClause;
import io.crate.analyze.symbol.DefaultTraversalSymbolVisitor;
import io.crate.analyze.symbol.Field;
import io.crate.analyze.symbol.Symbol;
import io.crate.analyze.symbol.format.SymbolFormatter;
import io.crate.metadata.ColumnIdent;
import io.crate.metadata.Reference;
import io.crate.metadata.doc.DocTableInfo;
import java.util.List;
public class GroupByConsumer {
private static final GroupByValidator GROUP_BY_VALIDATOR = new GroupByValidator();
public static boolean groupedByClusteredColumnOrPrimaryKeys(DocTableInfo tableInfo,
WhereClause whereClause,
List<Symbol> groupBySymbols) {
if (groupBySymbols.size() > 1) {
return groupedByPrimaryKeys(tableInfo.primaryKey(), groupBySymbols);
}
/**
* if the table has more than one partition there are multiple shards which might even be on different nodes
* so one shard doesn't contain all "clustered by" values
* -> need to use a distributed group by.
*/
if (tableInfo.isPartitioned() && whereClause.partitions().size() != 1) {
return false;
}
// this also handles the case if there is only one primary key.
// as clustered by column == pk column in that case
Symbol groupByKey = groupBySymbols.get(0);
return (groupByKey instanceof Reference
&& ((Reference) groupByKey).ident().columnIdent()
.equals(tableInfo.clusteredBy()));
}
private static boolean groupedByPrimaryKeys(List<ColumnIdent> primaryKeys, List<Symbol> groupBy) {
if (groupBy.size() != primaryKeys.size()) {
return false;
}
for (int i = 0, groupBySize = groupBy.size(); i < groupBySize; i++) {
Symbol groupBySymbol = groupBy.get(i);
if (groupBySymbol instanceof Reference) {
ColumnIdent columnIdent = ((Reference) groupBySymbol).ident().columnIdent();
ColumnIdent pkIdent = primaryKeys.get(i);
if (!pkIdent.equals(columnIdent)) {
return false;
}
} else {
return false;
}
}
return true;
}
public static void validateGroupBySymbols(List<Symbol> groupBySymbols) {
for (Symbol symbol : groupBySymbols) {
GROUP_BY_VALIDATOR.process(symbol, null);
}
}
private static class GroupByValidator extends DefaultTraversalSymbolVisitor<Void, Void> {
@Override
public Void visitReference(Reference symbol, Void context) {
if (symbol.indexType() == Reference.IndexType.ANALYZED) {
throw new IllegalArgumentException(
SymbolFormatter.format("Cannot GROUP BY '%s': grouping on analyzed/fulltext columns is not possible", symbol));
} else if (symbol.indexType() == Reference.IndexType.NO) {
throw new IllegalArgumentException(
SymbolFormatter.format("Cannot GROUP BY '%s': grouping on non-indexed columns is not possible", symbol));
}
return null;
}
@Override
public Void visitField(Field field, Void context) {
throw new UnsupportedOperationException("Field must have been resolved to Reference already");
}
}
}