package org.hibernate.engine.query.spi;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.Filter;
import org.hibernate.HibernateException;
import org.hibernate.QueryException;
import org.hibernate.ScrollableResults;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.RowSelection;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.event.spi.EventSource;
import org.hibernate.hql.internal.QuerySplitter;
import org.hibernate.hql.spi.FilterTranslator;
import org.hibernate.hql.spi.ParameterTranslations;
import org.hibernate.hql.spi.QueryTranslator;
import org.hibernate.hql.spi.QueryTranslatorFactory;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.EmptyIterator;
import org.hibernate.internal.util.collections.IdentitySet;
import org.hibernate.internal.util.collections.JoinedIterator;
import org.hibernate.type.Type;
public class HQLQueryPlan implements Serializable {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( HQLQueryPlan.class );
private final String sourceQuery;
private final QueryTranslator[] translators;
private final String[] sqlStrings;
private final ParameterMetadata parameterMetadata;
private final ReturnMetadata returnMetadata;
private final Set querySpaces;
private final Set<String> enabledFilterNames;
private final boolean shallow;
private final boolean TRACE_ENABLED = LOG.isTraceEnabled();
public HQLQueryPlan(String hql, boolean shallow, Map<String,Filter> enabledFilters,
SessionFactoryImplementor factory) {
this( hql, null, shallow, enabledFilters, factory, null );
}
public HQLQueryPlan(String hql, boolean shallow, Map<String,Filter> enabledFilters,
SessionFactoryImplementor factory, EntityGraphQueryHint entityGraphQueryHint) {
this( hql, null, shallow, enabledFilters, factory, entityGraphQueryHint );
}
@SuppressWarnings("unchecked")
protected HQLQueryPlan(
String hql,
String collectionRole,
boolean shallow,
Map<String,Filter> enabledFilters,
SessionFactoryImplementor factory,
EntityGraphQueryHint entityGraphQueryHint) {
this.sourceQuery = hql;
this.shallow = shallow;
final Set<String> copy = new HashSet<String>();
copy.addAll( enabledFilters.keySet() );
this.enabledFilterNames = java.util.Collections.unmodifiableSet( copy );
final String[] concreteQueryStrings = QuerySplitter.concreteQueries( hql, factory );
final int length = concreteQueryStrings.length;
this.translators = new QueryTranslator[length];
final List<String> sqlStringList = new ArrayList<String>();
final Set<Serializable> combinedQuerySpaces = new HashSet<Serializable>();
final boolean hasCollectionRole = (collectionRole == null);
final Map querySubstitutions = factory.getSettings().getQuerySubstitutions();
final QueryTranslatorFactory queryTranslatorFactory = factory.getSettings().getQueryTranslatorFactory();
for ( int i=0; i<length; i++ ) {
if ( hasCollectionRole ) {
translators[i] = queryTranslatorFactory
.createQueryTranslator( hql, concreteQueryStrings[i], enabledFilters, factory, entityGraphQueryHint );
translators[i].compile( querySubstitutions, shallow );
}
else {
translators[i] = queryTranslatorFactory
.createFilterTranslator( hql, concreteQueryStrings[i], enabledFilters, factory );
( (FilterTranslator) translators[i] ).compile( collectionRole, querySubstitutions, shallow );
}
combinedQuerySpaces.addAll( translators[i].getQuerySpaces() );
sqlStringList.addAll( translators[i].collectSqlStrings() );
}
this.sqlStrings = ArrayHelper.toStringArray( sqlStringList );
this.querySpaces = combinedQuerySpaces;
if ( length == 0 ) {
parameterMetadata = new ParameterMetadata( null, null );
returnMetadata = null;
}
else {
this.parameterMetadata = buildParameterMetadata( translators[0].getParameterTranslations(), hql );
if ( translators[0].isManipulationStatement() ) {
returnMetadata = null;
}
else {
final Type[] types = ( length > 1 ) ? new Type[translators[0].getReturnTypes().length] : translators[0].getReturnTypes();
returnMetadata = new ReturnMetadata( translators[0].getReturnAliases(), types );
}
}
}
public String getSourceQuery() {
return sourceQuery;
}
public Set getQuerySpaces() {
return querySpaces;
}
public ParameterMetadata getParameterMetadata() {
return parameterMetadata;
}
public ReturnMetadata getReturnMetadata() {
return returnMetadata;
}
public Set getEnabledFilterNames() {
return enabledFilterNames;
}
public String[] getSqlStrings() {
return sqlStrings;
}
public Set getUtilizedFilterNames() {
return null;
}
public boolean isShallow() {
return shallow;
}
@SuppressWarnings("unchecked")
public List performList(
QueryParameters queryParameters,
SessionImplementor session) throws HibernateException {
if ( TRACE_ENABLED ) {
LOG.tracev( "Find: {0}", getSourceQuery() );
queryParameters.traceParameters( session.getFactory() );
}
final RowSelection rowSelection = queryParameters.getRowSelection();
final boolean hasLimit = rowSelection != null
&& rowSelection.definesLimits();
final boolean needsLimit = hasLimit && translators.length > 1;
final QueryParameters queryParametersToUse;
if ( needsLimit ) {
LOG.needsLimit();
final RowSelection selection = new RowSelection();
selection.setFetchSize( queryParameters.getRowSelection().getFetchSize() );
selection.setTimeout( queryParameters.getRowSelection().getTimeout() );
queryParametersToUse = queryParameters.createCopyUsing( selection );
}
else {
queryParametersToUse = queryParameters;
}
final int guessedResultSize = guessResultSize( rowSelection );
final List combinedResults = new ArrayList( guessedResultSize );
final IdentitySet distinction = new IdentitySet( guessedResultSize );
int includedCount = -1;
translator_loop:
for ( QueryTranslator translator : translators ) {
final List tmp = translator.list( session, queryParametersToUse );
if ( needsLimit ) {
final int first = queryParameters.getRowSelection().getFirstRow() == null
? 0
: queryParameters.getRowSelection().getFirstRow();
final int max = queryParameters.getRowSelection().getMaxRows() == null
? -1
: queryParameters.getRowSelection().getMaxRows();
for ( final Object result : tmp ) {
if ( !distinction.add( result ) ) {
continue;
}
includedCount++;
if ( includedCount < first ) {
continue;
}
combinedResults.add( result );
if ( max >= 0 && includedCount > max ) {
break translator_loop;
}
}
}
else {
combinedResults.addAll( tmp );
}
}
return combinedResults;
}
private final int guessResultSize(RowSelection rowSelection) {
if ( rowSelection != null ) {
final int maxReasonableAllocation = rowSelection.getFetchSize() != null ? rowSelection.getFetchSize().intValue() : 100;
if ( rowSelection.getMaxRows() != null && rowSelection.getMaxRows().intValue() > 0 ) {
return Math.min( maxReasonableAllocation, rowSelection.getMaxRows().intValue() );
}
else if ( rowSelection.getFetchSize() != null && rowSelection.getFetchSize().intValue() > 0 ) {
return rowSelection.getFetchSize().intValue();
}
}
return 7;
}
@SuppressWarnings("unchecked")
public Iterator performIterate(
QueryParameters queryParameters,
EventSource session) throws HibernateException {
if ( TRACE_ENABLED ) {
LOG.tracev( "Iterate: {0}", getSourceQuery() );
queryParameters.traceParameters( session.getFactory() );
}
if ( translators.length == 0 ) {
return EmptyIterator.INSTANCE;
}
final boolean many = translators.length > 1;
Iterator[] results = null;
if ( many ) {
results = new Iterator[translators.length];
}
Iterator result = null;
for ( int i = 0; i < translators.length; i++ ) {
result = translators[i].iterate( queryParameters, session );
if ( many ) {
results[i] = result;
}
}
return many ? new JoinedIterator( results ) : result;
}
public ScrollableResults performScroll(
QueryParameters queryParameters,
SessionImplementor session) throws HibernateException {
if ( TRACE_ENABLED ) {
LOG.tracev( "Iterate: {0}", getSourceQuery() );
queryParameters.traceParameters( session.getFactory() );
}
if ( translators.length != 1 ) {
throw new QueryException( "implicit polymorphism not supported for scroll() queries" );
}
if ( queryParameters.getRowSelection().definesLimits() && translators[0].containsCollectionFetches() ) {
throw new QueryException( "firstResult/maxResults not supported in conjunction with scroll() of a query containing collection fetches" );
}
return translators[0].scroll( queryParameters, session );
}
public int performExecuteUpdate(QueryParameters queryParameters, SessionImplementor session)
throws HibernateException {
if ( TRACE_ENABLED ) {
LOG.tracev( "Execute update: {0}", getSourceQuery() );
queryParameters.traceParameters( session.getFactory() );
}
if ( translators.length != 1 ) {
LOG.splitQueries( getSourceQuery(), translators.length );
}
int result = 0;
for ( QueryTranslator translator : translators ) {
result += translator.executeUpdate( queryParameters, session );
}
return result;
}
private ParameterMetadata buildParameterMetadata(ParameterTranslations parameterTranslations, String hql) {
final long start = TRACE_ENABLED ? System.nanoTime() : 0;
final ParamLocationRecognizer recognizer = ParamLocationRecognizer.parseLocations( hql );
if ( TRACE_ENABLED ) {
final long end = System.nanoTime();
LOG.tracev( "HQL param location recognition took {0} nanoseconds ({1})", ( end - start ), hql );
}
int ordinalParamCount = parameterTranslations.getOrdinalParameterCount();
final int[] locations = ArrayHelper.toIntArray( recognizer.getOrdinalParameterLocationList() );
if ( parameterTranslations.supportsOrdinalParameterMetadata() && locations.length != ordinalParamCount ) {
throw new HibernateException( "ordinal parameter mismatch" );
}
ordinalParamCount = locations.length;
final OrdinalParameterDescriptor[] ordinalParamDescriptors = new OrdinalParameterDescriptor[ordinalParamCount];
for ( int i = 1; i <= ordinalParamCount; i++ ) {
ordinalParamDescriptors[ i - 1 ] = new OrdinalParameterDescriptor(
i,
parameterTranslations.supportsOrdinalParameterMetadata()
? parameterTranslations.getOrdinalParameterExpectedType( i )
: null,
locations[ i - 1 ]
);
}
final Map<String, NamedParameterDescriptor> namedParamDescriptorMap = new HashMap<String, NamedParameterDescriptor>();
final Map<String, ParamLocationRecognizer.NamedParameterDescription> map = recognizer.getNamedParameterDescriptionMap();
for ( final String name : map.keySet() ) {
final ParamLocationRecognizer.NamedParameterDescription description = map.get( name );
namedParamDescriptorMap.put(
name,
new NamedParameterDescriptor(
name,
parameterTranslations.getNamedParameterExpectedType( name ),
description.buildPositionsArray(),
description.isJpaStyle()
)
);
}
return new ParameterMetadata( ordinalParamDescriptors, namedParamDescriptorMap );
}
public QueryTranslator[] getTranslators() {
final QueryTranslator[] copy = new QueryTranslator[translators.length];
System.arraycopy( translators, 0, copy, 0, copy.length );
return copy;
}
public Class getDynamicInstantiationResultType() {
return translators[0].getDynamicInstantiationResultType();
}
public boolean isSelect() {
return !translators[0].isManipulationStatement();
}
}