/*
* 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.Collection;
import java.util.Collections;
import java.util.List;
import org.teiid.core.types.DataTypeManager;
import org.teiid.core.util.EquivalenceUtil;
import org.teiid.core.util.HashCodeUtil;
import org.teiid.query.sql.LanguageObject;
import org.teiid.query.sql.LanguageVisitor;
import org.teiid.query.sql.symbol.ElementSymbol;
import org.teiid.query.sql.symbol.Expression;
import org.teiid.query.sql.visitor.AggregateSymbolCollectorVisitor;
/**
* A representation of a data query. A query consists of various parts,
* referred to as clauses. The following list the types of clauses
* that a query can hold, and their purpose: <p>
* <pre>
* CLAUSE PURPOSE
* ========= ==============================================
* Select Defines the variables data to be retrieved for
* From Defines the groups to retrieve data from
* Criteria Defines constraints on data retrieval ("where")
* GroupBy Defines how rows being returned should be grouped
* Having Defines how groups should be filtered, also a criteria
* OrderBy Defines how the results should be sorted
* Option Defines any extra options on the query
* </pre>
*/
public class Query extends QueryCommand implements FilteredCommand {
/** The select clause. */
private Select select;
/** The from clause. */
private From from;
/** The criteria specifying constraints on what data will be retrieved. */
private Criteria criteria;
/** The group by specifying how to group rows. */
private GroupBy groupBy;
/** The having specifying which group rows will be returned. */
private Criteria having;
/** XML flag */
private boolean isXML;
/** The into clause. */
private Into into;
/** xml projected symbols */
private List<Expression> selectList;
//currently set by parser, but can be derived
private boolean isRowConstructor;
// =========================================================================
// C O N S T R U C T O R S
// =========================================================================
/**
* Constructs a default instance of this class.
*/
public Query() {
super();
}
public static Query rowConstructor(List<Expression> select) {
Query query = new Query();
query.setSelect(new Select());
query.getSelect().addSymbols(select);
query.setRowConstructor(true);
return query;
}
/**
* Constructs an instance of this class given the specified clauses
* @param select SELECT clause
* @param from FROM clause
* @param criteria WHERE clause
* @param orderBy ORDER BY clause
* @param option OPTION clause
*/
public Query( Select select, From from, Criteria criteria, OrderBy orderBy, Option option ) {
super();
setSelect( select );
setFrom( from );
setCriteria( criteria );
setOrderBy( orderBy );
setOption( option );
}
/**
* Constructs an instance of this class given all the clauses
* @param select SELECT clause
* @param from FROM clause
* @param criteria WHERE clause
* @param groupBy GROUP BY clause
* @param having HAVING clause
* @param orderBy ORDER BY clause
* @param option OPTION clause
*/
public Query( Select select, From from, Criteria criteria, GroupBy groupBy, Criteria having, OrderBy orderBy, Option option ) {
super();
setSelect( select );
setFrom( from );
setCriteria( criteria );
setGroupBy( groupBy );
setHaving( having );
setOrderBy( orderBy );
setOption( option );
}
/**
* Return type of command.
* @return TYPE_QUERY
*/
public int getType() {
return Command.TYPE_QUERY;
}
// =========================================================================
// S E L E C T M E T H O D S
// =========================================================================
/**
* Get the select clause for the query.
* @return SELECT clause
*/
public Select getSelect() {
return select;
}
/**
* Set the select clause for the query.
* @param select SELECT clause
*/
public void setSelect( Select select ) {
this.select = select;
}
/**
* Get the xml flag for the query
* @return boolean
*/
public boolean getIsXML() {
return isXML;
}
/**
* Get the xml flag for the query
* @return boolean
*/
public void setIsXML(boolean isXML) {
this.isXML = isXML;
}
// =========================================================================
// F R O M M E T H O D S
// =========================================================================
/**
* Get the from clause for the query.
* @return FROM clause
*/
public From getFrom() {
return from;
}
/**
* Set the from clause for the query.
* @param from FROM clause
*/
public void setFrom( From from ) {
this.from = from;
}
// =========================================================================
// C R I T E R I A M E T H O D S
// =========================================================================
/**
* Get the criteria clause for the query.
* @return WHERE clause
*/
public Criteria getCriteria() {
return criteria;
}
/**
* Set the criteria clause for the query.
* @param criteria WHERE clause
*/
public void setCriteria( Criteria criteria ) {
this.criteria = criteria;
}
/**
* Set the criteria clause to null
*/
public void clearCriteria() {
this.criteria = null;
}
// =========================================================================
// G R O U P B Y M E T H O D S
// =========================================================================
/**
* Get the group by clause for the query.
* @return GROUP BY clause
*/
public GroupBy getGroupBy() {
return groupBy;
}
/**
* Set the group by clause for the query.
* @param groupBy GROUP BY clause
*/
public void setGroupBy( GroupBy groupBy ) {
this.groupBy = groupBy;
}
// =========================================================================
// H A V I N G M E T H O D S
// =========================================================================
/**
* Get the having clause for the query.
* @return HAVING clause
*/
public Criteria getHaving() {
return having;
}
/**
* Set the criteria clause for the query.
* @param having HAVING clause
*/
public void setHaving( Criteria having ) {
this.having = having;
}
// =========================================================================
// I N T O M E T H O D S
// =========================================================================
/**
* @return
*/
public Into getInto() {
return into;
}
/**
* @param into
*/
public void setInto(Into into) {
this.into = into;
}
// =========================================================================
// P R O C E S S I N G M E T H O D S
// =========================================================================
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<Expression> getProjectedSymbols() {
if (!getIsXML()) {
if(getSelect() != null) {
if(getInto() != null){
//SELECT INTO clause
return Command.getUpdateCommandSymbol();
}
return getSelect().getProjectedSymbols();
}
return Collections.emptyList();
}
if(selectList == null){
selectList = new ArrayList<Expression>(1);
ElementSymbol xmlElement = new ElementSymbol("xml"); //$NON-NLS-1$
xmlElement.setType(DataTypeManager.DefaultDataClasses.XML);
selectList.add(xmlElement);
}
return selectList;
}
// =========================================================================
// O V E R R I D D E N O B J E C T M E T H O D S
// =========================================================================
/**
* Deep clone Query to produce a new identical query.
* @return Deep clone
*/
public Object clone() {
Query copy = new Query();
if(select != null) {
copy.setSelect( (Select) select.clone());
}
if(from != null) {
copy.setFrom( (From) from.clone());
}
if(criteria != null) {
copy.setCriteria( (Criteria) criteria.clone());
}
if(groupBy != null) {
copy.setGroupBy( (GroupBy) groupBy.clone());
}
if(having != null) {
copy.setHaving( (Criteria) having.clone());
}
if(getOrderBy() != null) {
copy.setOrderBy(getOrderBy().clone());
}
if(getLimit() != null) {
copy.setLimit( getLimit().clone());
}
copy.setWith(LanguageObject.Util.deepClone(this.getWith(), WithQueryCommand.class));
// Defect 13751: should clone isXML state.
copy.setIsXML(getIsXML());
if(selectList != null){
copy.selectList = LanguageObject.Util.deepClone(selectList, Expression.class);
}
if (into != null) {
copy.into = (Into)into.clone();
}
copyMetadataState(copy);
copy.setRowConstructor(this.isRowConstructor);
return copy;
}
/**
* Compare two queries for equality. Queries will only evaluate to equal if
* they are IDENTICAL: select variables are in the same order, criteria are in
* the same exact structure.
* @param obj Other object
* @return True if equal
*/
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(!(obj instanceof Query)) {
return false;
}
Query other = (Query) obj;
return EquivalenceUtil.areEqual(getSelect(), other.getSelect()) &&
EquivalenceUtil.areEqual(getFrom(), other.getFrom()) &&
EquivalenceUtil.areEqual(getCriteria(), other.getCriteria()) &&
EquivalenceUtil.areEqual(getGroupBy(), other.getGroupBy()) &&
EquivalenceUtil.areEqual(getHaving(), other.getHaving()) &&
EquivalenceUtil.areEqual(getOrderBy(), other.getOrderBy()) &&
EquivalenceUtil.areEqual(getLimit(), other.getLimit()) &&
EquivalenceUtil.areEqual(getWith(), other.getWith()) &&
getIsXML() == other.getIsXML() &&
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.select);
myHash = HashCodeUtil.hashCode(myHash, this.criteria);
return myHash;
}
/**
* @see org.teiid.query.sql.lang.Command#areResultsCachable()
*/
public boolean areResultsCachable() {
if(this.getInto() != null){
return false;
}
if (isXML) {
return true;
}
List<Expression> projectedSymbols = getProjectedSymbols();
return areColumnsCachable(projectedSymbols);
}
public static boolean areColumnsCachable(Collection<? extends Expression> projectedSymbols) {
return true;
}
/**
* @see org.teiid.query.sql.lang.QueryCommand#getProjectedQuery()
*/
@Override
public Query getProjectedQuery() {
return this;
}
@Override
public boolean returnsResultSet() {
return into == null;
}
public boolean hasAggregates() {
return getGroupBy() != null
|| getHaving() != null
|| !AggregateSymbolCollectorVisitor.getAggregates(getSelect(), false).isEmpty();
}
public boolean isRowConstructor() {
return isRowConstructor;
}
public void setRowConstructor(boolean isRowConstructor) {
this.isRowConstructor = isRowConstructor;
}
} // END CLASS