/* * 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.sql.lang; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.teiid.api.exception.query.QueryResolverException; import org.teiid.core.TeiidRuntimeException; import org.teiid.core.types.DataTypeManager; import org.teiid.core.util.EquivalenceUtil; import org.teiid.core.util.HashCodeUtil; import org.teiid.query.QueryPlugin; import org.teiid.query.metadata.QueryMetadataInterface; import org.teiid.query.resolver.util.ResolverUtil; import org.teiid.query.sql.LanguageObject; import org.teiid.query.sql.LanguageVisitor; import org.teiid.query.sql.symbol.AliasSymbol; import org.teiid.query.sql.symbol.Expression; import org.teiid.query.sql.symbol.Symbol; import org.teiid.query.sql.util.SymbolMap; /** * This object acts as a Set operator on multiple Queries - UNION, * INTERSECT, and EXCEPT can be implemented with this Class */ public class SetQuery extends QueryCommand { public enum Operation { /** Represents UNION of two queries */ UNION, /** Represents intersection of two queries */ INTERSECT, /** Represents set difference of two queries */ EXCEPT } private boolean all = true; private Operation operation; private QueryCommand leftQuery; private QueryCommand rightQuery; private List<Class<?>> projectedTypes = null; //set during resolving private QueryMetadataInterface metadata = null; // set during resolving /** * Construct query with operation type * @param operation Operation as specified like {@link Operation#UNION} */ public SetQuery(Operation operation) { this.operation = operation; } public SetQuery(Operation operation, boolean all, QueryCommand leftQuery, QueryCommand rightQuery) { this.operation = operation; this.all = all; this.leftQuery = leftQuery; this.rightQuery = rightQuery; } public Query getProjectedQuery() { if (leftQuery instanceof SetQuery) { return ((SetQuery)leftQuery).getProjectedQuery(); } return (Query)leftQuery; } /** * Return type of command. * @return TYPE_QUERY */ public int getType() { return Command.TYPE_QUERY; } /** * Set type of operation * @param operation Operation constant as defined in this class */ public void setOperation(Operation operation) { this.operation = operation; } /** * Get operation for this set * @return Operation as defined in this class */ public Operation getOperation() { return this.operation; } public void acceptVisitor(LanguageVisitor visitor) { visitor.visit(this); } /** * Get the ordered list of all elements returned by this query. These elements * may be ElementSymbols or ExpressionSymbols but in all cases each represents a * single column. * @return Ordered list of SingleElementSymbol */ public List getProjectedSymbols() { Query query = getProjectedQuery(); List projectedSymbols = query.getProjectedSymbols(); if (projectedTypes != null) { return getTypedProjectedSymbols(projectedSymbols, projectedTypes, metadata); } return projectedSymbols; } public static List<Expression> getTypedProjectedSymbols(List<? extends Expression> acutal, List<Class<?>> projectedTypes, QueryMetadataInterface metadata) { List<Expression> newProject = new ArrayList<Expression>(); for (int i = 0; i < acutal.size(); i++) { Expression originalSymbol = acutal.get(i); Expression symbol = originalSymbol; Class<?> type = projectedTypes.get(i); if (symbol.getType() != type) { symbol = SymbolMap.getExpression(originalSymbol); try { symbol = ResolverUtil.convertExpression(symbol, DataTypeManager.getDataTypeName(type), metadata); } catch (QueryResolverException err) { throw new TeiidRuntimeException(QueryPlugin.Event.TEIID30447, err); } if (originalSymbol instanceof Symbol) { symbol = new AliasSymbol(Symbol.getShortName(originalSymbol), symbol); } } newProject.add(symbol); } return newProject; } /** * Deep clone this object to produce a new identical query. * @return Deep clone */ public Object clone() { SetQuery copy = new SetQuery(this.operation); this.copyMetadataState(copy); copy.leftQuery = (QueryCommand)this.leftQuery.clone(); copy.rightQuery = (QueryCommand)this.rightQuery.clone(); copy.setAll(this.all); if(this.getOrderBy() != null) { copy.setOrderBy(this.getOrderBy().clone()); } if(this.getLimit() != null) { copy.setLimit( (Limit) this.getLimit().clone() ); } copy.setWith(LanguageObject.Util.deepClone(this.getWith(), WithQueryCommand.class)); if (this.projectedTypes != null) { copy.setProjectedTypes(new ArrayList<Class<?>>(projectedTypes), this.metadata); } return copy; } /** * Compare two queries for equality. * @param obj Other object * @return True if equal */ public boolean equals(Object obj) { // Quick same object test if(this == obj) { return true; } // Quick fail tests if(!(obj instanceof SetQuery)) { return false; } SetQuery other = (SetQuery) obj; return getOperation() == other.getOperation() && EquivalenceUtil.areEqual(this.isAll(), other.isAll()) && EquivalenceUtil.areEqual(this.leftQuery, other.leftQuery) && EquivalenceUtil.areEqual(this.rightQuery, other.rightQuery) && EquivalenceUtil.areEqual(getOrderBy(), other.getOrderBy()) && EquivalenceUtil.areEqual(getLimit(), other.getLimit()) && EquivalenceUtil.areEqual(getWith(), other.getWith()) && sameOptionAndHint(other); } /** * Get hashcode for query. WARNING: This hash code relies on the hash codes of the * Select and Criteria clauses. If the query changes, it's hash code will change and * it can be lost from collections. Hash code is only valid after query has been * completely constructed. * @return Hash code */ public int hashCode() { // For speed, this hash code relies only on the hash codes of its select // and criteria clauses, not on the from, order by, or option clauses int myHash = 0; myHash = HashCodeUtil.hashCode(myHash, this.operation); myHash = HashCodeUtil.hashCode(myHash, getProjectedQuery()); return myHash; } /** * @see org.teiid.query.sql.lang.Command#areResultsCachable() */ public boolean areResultsCachable() { return leftQuery.areResultsCachable() && rightQuery.areResultsCachable(); } /** * @return the left and right queries as a list. This list cannot be modified. */ public List<QueryCommand> getQueryCommands() { return Collections.unmodifiableList(Arrays.asList(leftQuery, rightQuery)); } /** * @param projectedSymbols The projectedSymbols to set. */ public void setProjectedTypes(List<Class<?>> projectedTypes, QueryMetadataInterface metadata) { this.projectedTypes = projectedTypes; this.metadata = metadata; } /** * @return Returns the projectedTypes. */ public List<Class<?>> getProjectedTypes() { return this.projectedTypes; } public boolean isAll() { return this.all; } public void setAll(boolean all) { this.all = all; } public QueryCommand getLeftQuery() { return this.leftQuery; } public void setLeftQuery(QueryCommand leftQuery) { this.leftQuery = leftQuery; } public QueryCommand getRightQuery() { return this.rightQuery; } public void setRightQuery(QueryCommand rightQuery) { this.rightQuery = rightQuery; } }