/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.query.optimizer.relational;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.teiid.core.types.DataTypeManagerService;
import org.teiid.designer.query.sql.lang.ISetQuery.Operation;
import org.teiid.query.sql.lang.CompareCriteria;
import org.teiid.query.sql.lang.Criteria;
import org.teiid.query.sql.lang.Query;
import org.teiid.query.sql.lang.QueryCommand;
import org.teiid.query.sql.lang.SetCriteria;
import org.teiid.query.sql.lang.SetQuery;
import org.teiid.query.sql.symbol.Constant;
import org.teiid.query.sql.symbol.ElementSymbol;
import org.teiid.query.sql.symbol.Expression;
import org.teiid.query.sql.util.SymbolMap;
/**
* TODO: support recursive detection of partitions
*
* Extracts a map of partitioning information from a union
*/
public class PartitionAnalyzer {
public static Map<ElementSymbol, List<Set<Constant>>> extractPartionInfo(SetQuery setQuery, List<ElementSymbol> projectedSymbols) {
List<Query> queries = new LinkedList<Query>();
if (!extractQueries(setQuery, queries)) {
return Collections.emptyMap();
}
Map<ElementSymbol, List<Set<Constant>>> partitions = new LinkedHashMap<ElementSymbol, List<Set<Constant>>>();
boolean first = true;
for (Query query : queries) {
Map<ElementSymbol, Set<Constant>> info = extractPartitionInfo(query, projectedSymbols);
partitions.keySet().retainAll(info.keySet());
if (first) {
first = false;
for (Map.Entry<ElementSymbol, Set<Constant>> entry : info.entrySet()) {
ArrayList<Set<Constant>> values = new ArrayList<Set<Constant>>(queries.size());
partitions.put(entry.getKey(), values);
values.add(entry.getValue());
}
continue;
}
Set<ElementSymbol> keys = partitions.keySet();
for (Iterator<ElementSymbol> iter = keys.iterator(); iter.hasNext();) {
ElementSymbol elementSymbol = iter.next();
List<Set<Constant>> values = partitions.get(elementSymbol);
Set<Constant> value = info.get(elementSymbol);
for (Set<Constant> set : values) {
if (!Collections.disjoint(set, value)) {
iter.remove();
continue;
}
}
values.add(value);
}
}
return partitions;
}
public static boolean extractQueries(QueryCommand queryCommand, List<Query> result) {
if (queryCommand instanceof SetQuery) {
SetQuery sq = (SetQuery)queryCommand;
if (sq.isAll() && sq.getOperation() == Operation.UNION && sq.getOrderBy() == null && sq.getLimit() == null && sq.getWith() == null) {
if (!extractQueries(sq.getLeftQuery(), result)) {
return false;
}
if (!extractQueries(sq.getRightQuery(), result)) {
return false;
}
return true;
}
return false;
}
result.add((Query)queryCommand);
return true;
}
private static Map<ElementSymbol, Set<Constant>> extractPartitionInfo(Query query, List<ElementSymbol> projectedSymbols) {
List<Expression> projected = query.getSelect().getProjectedSymbols();
List<Criteria> crits = Criteria.separateCriteriaByAnd(query.getCriteria());
Map<Expression, Set<Constant>> inMap = new HashMap<Expression, Set<Constant>>();
for (Criteria criteria : crits) {
if (criteria instanceof CompareCriteria) {
CompareCriteria cc = (CompareCriteria)criteria;
if (cc.getOperator() != CompareCriteria.EQ) {
continue;
}
if (cc.getLeftExpression() instanceof Constant) {
inMap.put(cc.getRightExpression(), new HashSet<Constant>(Arrays.asList((Constant)cc.getLeftExpression())));
} else if (cc.getRightExpression() instanceof Constant) {
inMap.put(cc.getLeftExpression(), new HashSet<Constant>(Arrays.asList((Constant)cc.getRightExpression())));
}
continue;
}
if (!(criteria instanceof SetCriteria)) {
continue;
}
SetCriteria sc = (SetCriteria)criteria;
HashSet<Constant> values = new HashSet<Constant>();
boolean allConstants = true;
for (Expression exp : (Collection<Expression>)sc.getValues()) {
if (exp instanceof Constant) {
values.add((Constant)exp);
} else {
allConstants = false;
break;
}
}
if (allConstants) {
inMap.put(sc.getExpression(), values);
}
}
Map<ElementSymbol, Set<Constant>> result = new HashMap<ElementSymbol, Set<Constant>>();
for (int i = 0; i < projected.size(); i++) {
Expression ex = SymbolMap.getExpression(projected.get(i));
if (DataTypeManagerService.getInstance(ex.getTeiidVersion()).isNonComparable(ex.getType())) {
continue;
}
if (ex instanceof Constant) {
result.put(projectedSymbols.get(i), Collections.singleton((Constant)ex));
} else {
Set<Constant> values = inMap.get(ex);
if (values != null) {
result.put(projectedSymbols.get(i), values);
}
}
}
return result;
}
}