All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.hql.internal.classic.QueryTranslatorImpl Maven / Gradle / Ivy

There is a newer version: 6.5.0.CR2
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.hql.internal.classic;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.internal.JoinSequence;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.spi.EventSource;
import org.hibernate.hql.internal.HolderInstantiator;
import org.hibernate.hql.internal.NameGenerator;
import org.hibernate.hql.spi.FilterTranslator;
import org.hibernate.hql.spi.NamedParameterInformation;
import org.hibernate.hql.spi.ParameterTranslations;
import org.hibernate.hql.spi.PositionalParameterInformation;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.IteratorImpl;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.loader.BasicLoader;
import org.hibernate.loader.internal.AliasConstantsHelper;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.param.CollectionFilterKeyParameterSpecification;
import org.hibernate.param.ParameterBinder;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.entity.PropertyMapping;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.sql.JoinFragment;
import org.hibernate.sql.JoinType;
import org.hibernate.sql.QuerySelect;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.type.AssociationType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;

/**
 * An instance of QueryTranslator translates a Hibernate
 * query string to SQL.
 */
public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator {
	private static final CoreMessageLogger LOG = CoreLogging.messageLogger( QueryTranslatorImpl.class );

	private static final String[] NO_RETURN_ALIASES = new String[] {};

	private final String queryIdentifier;
	private final String queryString;

	private final Map typeMap = new LinkedHashMap();
	private final Map collections = new LinkedHashMap();
	private List returnedTypes = new ArrayList();
	private final List fromTypes = new ArrayList();
	private final List scalarTypes = new ArrayList();
	private final Map aliasNames = new HashMap();
	private final Map oneToOneOwnerNames = new HashMap();
	private final Map uniqueKeyOwnerReferences = new HashMap();
	private final Map decoratedPropertyMappings = new HashMap();

	private final Map namedParameters = new HashMap<>();
	private final Map ordinalParameters = new HashMap<>();
	private final List paramValueBinders = new ArrayList<>();

	private final List scalarSelectTokens = new ArrayList();
	private final List whereTokens = new ArrayList();
	private final List havingTokens = new ArrayList();
	private final Map joins = new LinkedHashMap();
	private final List orderByTokens = new ArrayList();
	private final List groupByTokens = new ArrayList();
	private final Set querySpaces = new HashSet();
	private final Set entitiesToFetch = new HashSet();

	private final Map pathAliases = new HashMap();
	private final Map pathJoins = new HashMap();

	private Queryable[] persisters;
	private int[] owners;
	private EntityType[] ownerAssociationTypes;
	private String[] names;
	private boolean[] includeInSelect;
	private int selectLength;
	private Type[] returnTypes;
	private Type[] actualReturnTypes;
	private String[][] scalarColumnNames;
	private Map tokenReplacements;
	private int nameCount;
	private int parameterCount;
	private boolean distinct;
	private boolean compiled;
	private String sqlString;
	private Class holderClass;
	private Constructor holderConstructor;
	private boolean hasScalars;
	private boolean shallowQuery;
	private QueryTranslatorImpl superQuery;

	private QueryableCollection collectionPersister;
	private int collectionOwnerColumn = -1;
	private String collectionOwnerName;
	private String fetchName;

	private String[] suffixes;

	private Map enabledFilters;

	/**
	 * Construct a query translator
	 *
	 * @param queryIdentifier A unique identifier for the query of which this
	 * translation is part; typically this is the original, user-supplied query string.
	 * @param queryString The "preprocessed" query string; at the very least
	 * already processed by {@link org.hibernate.hql.internal.QuerySplitter}.
	 * @param enabledFilters Any enabled filters.
	 * @param factory The session factory.
	 */
	public QueryTranslatorImpl(
			String queryIdentifier,
			String queryString,
			Map enabledFilters,
			SessionFactoryImplementor factory) {
		super( factory );
		this.queryIdentifier = queryIdentifier;
		this.queryString = queryString;
		this.enabledFilters = enabledFilters;
	}

	/**
	 * Construct a query translator; this form used internally.
	 *
	 * @param queryString The query string to process.
	 * @param enabledFilters Any enabled filters.
	 * @param factory The session factory.
	 */
	public QueryTranslatorImpl(
			String queryString,
			Map enabledFilters,
			SessionFactoryImplementor factory) {
		this( queryString, queryString, enabledFilters, factory );
	}

	/**
	 * Compile a subquery.
	 *
	 * @param superquery The containing query of the query to be compiled.
	 *
	 * @throws org.hibernate.MappingException Indicates problems resolving
	 * things referenced in the query.
	 * @throws org.hibernate.QueryException Generally some form of syntatic
	 * failure.
	 */
	void compile(QueryTranslatorImpl superquery) throws QueryException, MappingException {
		this.tokenReplacements = superquery.tokenReplacements;
		this.superQuery = superquery;
		this.shallowQuery = true;
		this.enabledFilters = superquery.getEnabledFilters();
		compile();
	}


	/**
	 * Compile a "normal" query. This method may be called multiple
	 * times. Subsequent invocations are no-ops.
	 */
	public synchronized void compile(
			Map replacements,
			boolean scalar) throws QueryException, MappingException {
		if ( !compiled ) {
			this.tokenReplacements = replacements;
			this.shallowQuery = scalar;
			compile();
		}
	}

	/**
	 * Compile a filter. This method may be called multiple
	 * times. Subsequent invocations are no-ops.
	 */
	public synchronized void compile(
			String collectionRole,
			Map replacements,
			boolean scalar) throws QueryException, MappingException {

		if ( !isCompiled() ) {
			addFromAssociation( "this", collectionRole );
			paramValueBinders.add(
					new CollectionFilterKeyParameterSpecification(
							collectionRole,
							getFactory().getMetamodel().collectionPersister( collectionRole ).getKeyType()
					)
			);
			compile( replacements, scalar );
		}
	}

	/**
	 * Compile the query (generate the SQL).
	 *
	 * @throws org.hibernate.MappingException Indicates problems resolving
	 * things referenced in the query.
	 * @throws org.hibernate.QueryException Generally some form of syntatic
	 * failure.
	 */
	private void compile() throws QueryException, MappingException {
		LOG.trace( "Compiling query" );
		try {
			ParserHelper.parse(
					new PreprocessingParser( tokenReplacements ),
					queryString,
					ParserHelper.HQL_SEPARATORS,
					this
			);
			renderSQL();
		}
		catch (QueryException qe) {
			if ( qe.getQueryString() == null ) {
				throw qe.wrapWithQueryString( queryString );
			}
			else {
				throw qe;
			}
		}
		catch (MappingException me) {
			throw me;
		}
		catch (Exception e) {
			LOG.debug( "Unexpected query compilation problem", e );
			throw new QueryException( "Incorrect query syntax", queryString, e );
		}

		postInstantiate();

		compiled = true;

	}

	@Override
	public String getSQLString() {
		return sqlString;
	}

	public List collectSqlStrings() {
		return ArrayHelper.toList( new String[] {sqlString} );
	}

	public String getQueryString() {
		return queryString;
	}

	/**
	 * Persisters for the return values of a find() style query.
	 *
	 * @return an array of EntityPersisters.
	 */
	@Override
	protected Loadable[] getEntityPersisters() {
		return persisters;
	}

	/**
	 * Types of the return values of an iterate() style query.
	 *
	 * @return an array of Types.
	 */
	public Type[] getReturnTypes() {
		return actualReturnTypes;
	}

	public String[] getReturnAliases() {
		// return aliases not supported in classic translator!
		return NO_RETURN_ALIASES;
	}

	public String[][] getColumnNames() {
		return scalarColumnNames;
	}

	private static void logQuery(String hql, String sql) {
		if ( LOG.isDebugEnabled() ) {
			LOG.debugf( "HQL: %s", hql );
			LOG.debugf( "SQL: %s", sql );
		}
	}

	void setAliasName(String alias, String name) {
		aliasNames.put( alias, name );
	}

	public String getAliasName(String alias) {
		String name = (String) aliasNames.get( alias );
		if ( name == null ) {
			if ( superQuery != null ) {
				name = superQuery.getAliasName( alias );
			}
			else {
				name = alias;
			}
		}
		return name;
	}

	String unalias(String path) {
		String alias = StringHelper.root( path );
		String name = getAliasName( alias );
		if ( name != null ) {
			return name + path.substring( alias.length() );
		}
		return path;
	}

	void addEntityToFetch(String name, String oneToOneOwnerName, AssociationType ownerAssociationType) {
		addEntityToFetch( name );
		if ( oneToOneOwnerName != null ) {
			oneToOneOwnerNames.put( name, oneToOneOwnerName );
		}
		if ( ownerAssociationType != null ) {
			uniqueKeyOwnerReferences.put( name, ownerAssociationType );
		}
	}

	private void addEntityToFetch(String name) {
		entitiesToFetch.add( name );
	}

	private int nextCount() {
		return ( superQuery == null ) ? nameCount++ : superQuery.nameCount++;
	}

	String createNameFor(String type) {
		return StringHelper.generateAlias( type, nextCount() );
	}

	String createNameForCollection(String role) {
		return StringHelper.generateAlias( role, nextCount() );
	}

	private String getType(String name) {
		String type = (String) typeMap.get( name );
		if ( type == null && superQuery != null ) {
			type = superQuery.getType( name );
		}
		return type;
	}

	private String getRole(String name) {
		String role = (String) collections.get( name );
		if ( role == null && superQuery != null ) {
			role = superQuery.getRole( name );
		}
		return role;
	}

	boolean isName(String name) {
		return aliasNames.containsKey( name ) ||
				typeMap.containsKey( name ) ||
				collections.containsKey( name ) || (
				superQuery != null && superQuery.isName( name )
		);
	}

	PropertyMapping getPropertyMapping(String name) throws QueryException {
		PropertyMapping decorator = getDecoratedPropertyMapping( name );
		if ( decorator != null ) {
			return decorator;
		}

		String type = getType( name );
		if ( type == null ) {
			String role = getRole( name );
			if ( role == null ) {
				throw new QueryException( "alias not found: " + name );
			}
			return getCollectionPersister( role ); //.getElementPropertyMapping();
		}
		else {
			Queryable persister = getEntityPersister( type );
			if ( persister == null ) {
				throw new QueryException( "persistent class not found: " + type );
			}
			return persister;
		}
	}

	private PropertyMapping getDecoratedPropertyMapping(String name) {
		return (PropertyMapping) decoratedPropertyMappings.get( name );
	}

	void decoratePropertyMapping(String name, PropertyMapping mapping) {
		decoratedPropertyMappings.put( name, mapping );
	}

	private Queryable getEntityPersisterForName(String name) throws QueryException {
		String type = getType( name );
		Queryable persister = getEntityPersister( type );
		if ( persister == null ) {
			throw new QueryException( "persistent class not found: " + type );
		}
		return persister;
	}

	Queryable getEntityPersisterUsingImports(String className) {
		final String importedClassName = getFactory().getMetamodel().getImportedClassName( className );
		if ( importedClassName == null ) {
			return null;
		}
		try {
			return (Queryable) getFactory().getMetamodel().entityPersister( importedClassName );
		}
		catch (MappingException me) {
			return null;
		}
	}

	Queryable getEntityPersister(String entityName) throws QueryException {
		try {
			return (Queryable) getFactory().getMetamodel().entityPersister( entityName );
		}
		catch (Exception e) {
			throw new QueryException( "persistent class not found: " + entityName );
		}
	}

	QueryableCollection getCollectionPersister(String role) throws QueryException {
		try {
			return (QueryableCollection) getFactory().getMetamodel().collectionPersister( role );
		}
		catch (ClassCastException cce) {
			throw new QueryException( "collection role is not queryable: " + role );
		}
		catch (Exception e) {
			throw new QueryException( "collection role not found: " + role );
		}
	}

	void addType(String name, String type) {
		typeMap.put( name, type );
	}

	void addCollection(String name, String role) {
		collections.put( name, role );
	}

	void addFrom(String name, String type, JoinSequence joinSequence)
			throws QueryException {
		addType( name, type );
		addFrom( name, joinSequence );
	}

	void addFromCollection(String name, String collectionRole, JoinSequence joinSequence)
			throws QueryException {
		//register collection role
		addCollection( name, collectionRole );
		addJoin( name, joinSequence );
	}

	void addFrom(String name, JoinSequence joinSequence)
			throws QueryException {
		fromTypes.add( name );
		addJoin( name, joinSequence );
	}

	void addFromClass(String name, Queryable classPersister)
			throws QueryException {
		JoinSequence joinSequence = new JoinSequence( getFactory() )
				.setRoot( classPersister, name );
		//crossJoins.add(name);
		addFrom( name, classPersister.getEntityName(), joinSequence );
	}

	void addSelectClass(String name) {
		returnedTypes.add( name );
	}

	void addSelectScalar(Type type) {
		scalarTypes.add( type );
	}

	void appendWhereToken(String token) {
		whereTokens.add( token );
	}

	void appendHavingToken(String token) {
		havingTokens.add( token );
	}

	void appendOrderByToken(String token) {
		orderByTokens.add( token );
	}

	void appendGroupByToken(String token) {
		groupByTokens.add( token );
	}

	void appendScalarSelectToken(String token) {
		scalarSelectTokens.add( token );
	}

	void appendScalarSelectTokens(String[] tokens) {
		scalarSelectTokens.add( tokens );
	}

	void addFromJoinOnly(String name, JoinSequence joinSequence) throws QueryException {
		addJoin( name, joinSequence.getFromPart() );
	}

	void addJoin(String name, JoinSequence joinSequence) throws QueryException {
		if ( !joins.containsKey( name ) ) {
			joins.put( name, joinSequence );
		}
	}

	void addNamedParameter(String name) {
		if ( superQuery != null ) {
			superQuery.addNamedParameter( name );
		}

		final int loc = parameterCount++;

		final NamedParameterInformationImpl info = namedParameters.computeIfAbsent(
				name,
				k -> new NamedParameterInformationImpl( name )
		);
		paramValueBinders.add( info );
		info.addSourceLocation( loc );
	}

	private enum OrdinalParameterStyle { LABELED, LEGACY }

	private OrdinalParameterStyle ordinalParameterStyle;

	private int legacyPositionalParameterCount = 0;

	void addLegacyPositionalParameter() {
		if ( superQuery != null ) {
			superQuery.addLegacyPositionalParameter();
		}

		if ( ordinalParameterStyle == null ) {
			ordinalParameterStyle = OrdinalParameterStyle.LEGACY;
		}
		else if ( ordinalParameterStyle != OrdinalParameterStyle.LEGACY ) {
			throw new QueryException( "Cannot mix legacy and labeled positional parameters" );
		}

		final int label = legacyPositionalParameterCount++;
		final PositionalParameterInformationImpl paramInfo = new PositionalParameterInformationImpl( label );
		ordinalParameters.put( label, paramInfo );
		paramValueBinders.add( paramInfo );

		final int loc = parameterCount++;
		paramInfo.addSourceLocation( loc );

	}

	void addOrdinalParameter(int label) {
		if ( superQuery != null ) {
			superQuery.addOrdinalParameter( label );
		}

		if ( ordinalParameterStyle == null ) {
			ordinalParameterStyle = OrdinalParameterStyle.LABELED;
		}
		else if ( ordinalParameterStyle != OrdinalParameterStyle.LABELED ) {
			throw new QueryException( "Cannot mix legacy and labeled positional parameters" );
		}

		final int loc = parameterCount++;

		final PositionalParameterInformationImpl  info = ordinalParameters.computeIfAbsent(
				label,
				k -> new PositionalParameterInformationImpl( label )
		);

		paramValueBinders.add( info );

		info.addSourceLocation( loc );
	}

	@Override
	protected int bindParameterValues(
			PreparedStatement statement,
			QueryParameters queryParameters,
			int startIndex,
			SharedSessionContractImplementor session) throws SQLException {

		int span = 0;
		for ( ParameterBinder binder : paramValueBinders ) {
			span += binder.bind( statement, queryParameters, session, startIndex + span );
		}
		return span;
	}

	@Override
	public int[] getNamedParameterLocs(String name) throws QueryException {
		Object o = namedParameters.get( name );
		if ( o == null ) {
			throw new QueryException( ERROR_NAMED_PARAMETER_DOES_NOT_APPEAR + name, queryString );
		}
		if ( o instanceof Integer ) {
			return new int[] {(Integer) o};
		}
		else {
			return ArrayHelper.toIntArray( (ArrayList) o );
		}
	}

	private void renderSQL() throws QueryException, MappingException {
		final int rtsize;
		if ( returnedTypes.size() == 0 && scalarTypes.size() == 0 ) {
			//ie no select clause in HQL
			returnedTypes = fromTypes;
			rtsize = returnedTypes.size();
		}
		else {
			rtsize = returnedTypes.size();
			Iterator iter = entitiesToFetch.iterator();
			while ( iter.hasNext() ) {
				returnedTypes.add( iter.next() );
			}
		}
		int size = returnedTypes.size();
		persisters = new Queryable[size];
		names = new String[size];
		owners = new int[size];
		ownerAssociationTypes = new EntityType[size];
		suffixes = new String[size];
		includeInSelect = new boolean[size];
		for ( int i = 0; i < size; i++ ) {
			String name = (String) returnedTypes.get( i );
			//if ( !isName(name) ) throw new QueryException("unknown type: " + name);
			persisters[i] = getEntityPersisterForName( name );
			// TODO: cannot use generateSuffixes() - it handles the initial suffix differently.
			suffixes[i] = ( size == 1 ) ? "" : AliasConstantsHelper.get( i );
			names[i] = name;
			includeInSelect[i] = !entitiesToFetch.contains( name );
			if ( includeInSelect[i] ) {
				selectLength++;
			}
			if ( name.equals( collectionOwnerName ) ) {
				collectionOwnerColumn = i;
			}
			String oneToOneOwner = (String) oneToOneOwnerNames.get( name );
			owners[i] = ( oneToOneOwner == null ) ? -1 : returnedTypes.indexOf( oneToOneOwner );
			ownerAssociationTypes[i] = (EntityType) uniqueKeyOwnerReferences.get( name );
		}

		if ( ArrayHelper.isAllNegative( owners ) ) {
			owners = null;
		}

		String scalarSelect = renderScalarSelect(); //Must be done here because of side-effect! yuck...

		int scalarSize = scalarTypes.size();
		hasScalars = scalarTypes.size() != rtsize;

		returnTypes = new Type[scalarSize];
		for ( int i = 0; i < scalarSize; i++ ) {
			returnTypes[i] = (Type) scalarTypes.get( i );
		}

		QuerySelect sql = new QuerySelect( getFactory().getDialect() );
		sql.setDistinct( distinct );

		if ( !shallowQuery ) {
			renderIdentifierSelect( sql );
			renderPropertiesSelect( sql );
		}

		if ( collectionPersister != null ) {
			sql.addSelectFragmentString( collectionPersister.selectFragment( fetchName, "__" ) );
		}

		if ( hasScalars || shallowQuery ) {
			sql.addSelectFragmentString( scalarSelect );
		}

		//TODO: for some dialects it would be appropriate to add the renderOrderByPropertiesSelect() to other select strings
		mergeJoins( sql.getJoinFragment() );

		sql.setWhereTokens( whereTokens.iterator() );

		sql.setGroupByTokens( groupByTokens.iterator() );
		sql.setHavingTokens( havingTokens.iterator() );
		sql.setOrderByTokens( orderByTokens.iterator() );

		if ( collectionPersister != null && collectionPersister.hasOrdering() ) {
			sql.addOrderBy( collectionPersister.getSQLOrderByString( fetchName ) );
		}

		scalarColumnNames = NameGenerator.generateColumnNames( returnTypes, getFactory() );

		// initialize the Set of queried identifier spaces (ie. tables)
		Iterator iter = collections.values().iterator();
		while ( iter.hasNext() ) {
			CollectionPersister p = getCollectionPersister( (String) iter.next() );
			addQuerySpaces( p.getCollectionSpaces() );
		}
		iter = typeMap.keySet().iterator();
		while ( iter.hasNext() ) {
			Queryable p = getEntityPersisterForName( (String) iter.next() );
			addQuerySpaces( p.getQuerySpaces() );
		}

		sqlString = sql.toQueryString();

		if ( holderClass != null ) {
			holderConstructor = ReflectHelper.getConstructor( holderClass, returnTypes );
		}

		if ( hasScalars ) {
			actualReturnTypes = returnTypes;
		}
		else {
			actualReturnTypes = new Type[selectLength];
			int j = 0;
			for ( int i = 0; i < persisters.length; i++ ) {
				if ( includeInSelect[i] ) {
					actualReturnTypes[j++] = getFactory().getTypeResolver()
							.getTypeFactory()
							.manyToOne( persisters[i].getEntityName(), shallowQuery );
				}
			}
		}

	}

	private void renderIdentifierSelect(QuerySelect sql) {
		int size = returnedTypes.size();

		for ( int k = 0; k < size; k++ ) {
			String name = (String) returnedTypes.get( k );
			String suffix = size == 1 ? "" : AliasConstantsHelper.get( k );
			sql.addSelectFragmentString( persisters[k].identifierSelectFragment( name, suffix ) );
		}

	}

	/*private String renderOrderByPropertiesSelect() {
		StringBuffer buf = new StringBuffer(10);

		//add the columns we are ordering by to the select ID select clause
		Iterator iter = orderByTokens.iterator();
		while ( iter.hasNext() ) {
			String token = (String) iter.next();
			if ( token.lastIndexOf(".") > 0 ) {
				//ie. it is of form "foo.bar", not of form "asc" or "desc"
				buf.append(StringHelper.COMMA_SPACE).append(token);
			}
		}

		return buf.toString();
	}*/

	private void renderPropertiesSelect(QuerySelect sql) {
		int size = returnedTypes.size();
		for ( int k = 0; k < size; k++ ) {
			String suffix = size == 1 ? "" : AliasConstantsHelper.get( k );
			String name = (String) returnedTypes.get( k );
			sql.addSelectFragmentString( persisters[k].propertySelectFragment( name, suffix, false ) );
		}
	}

	/**
	 * WARNING: side-effecty
	 */
	private String renderScalarSelect() {

		boolean isSubselect = superQuery != null;

		StringBuilder buf = new StringBuilder( 20 );

		if ( scalarTypes.size() == 0 ) {
			//ie. no select clause
			int size = returnedTypes.size();
			for ( int k = 0; k < size; k++ ) {

				scalarTypes.add(
						getFactory().getTypeResolver().getTypeFactory().manyToOne(
								persisters[k].getEntityName(),
								shallowQuery
						)
				);

				String[] idColumnNames = persisters[k].getIdentifierColumnNames();
				for ( int i = 0; i < idColumnNames.length; i++ ) {
					buf.append( returnedTypes.get( k ) ).append( '.' ).append( idColumnNames[i] );
					if ( !isSubselect ) {
						buf.append( " as " ).append( NameGenerator.scalarName( k, i ) );
					}
					if ( i != idColumnNames.length - 1 || k != size - 1 ) {
						buf.append( ", " );
					}
				}

			}

		}
		else {
			//there _was_ a select clause
			Iterator iter = scalarSelectTokens.iterator();
			int c = 0;
			boolean nolast = false; //real hacky...
			int parenCount = 0; // used to count the nesting of parentheses
			while ( iter.hasNext() ) {
				Object next = iter.next();
				if ( next instanceof String ) {
					String token = (String) next;

					if ( "(".equals( token ) ) {
						parenCount++;
					}
					else if ( ")".equals( token ) ) {
						parenCount--;
					}

					String lc = token.toLowerCase( Locale.ROOT );
					if ( lc.equals( ", " ) ) {
						if ( nolast ) {
							nolast = false;
						}
						else {
							if ( !isSubselect && parenCount == 0 ) {
								int x = c++;
								buf.append( " as " ).append( NameGenerator.scalarName( x, 0 ) );
							}
						}
					}
					buf.append( token );
					if ( lc.equals( "distinct" ) || lc.equals( "all" ) ) {
						buf.append( ' ' );
					}
				}
				else {
					nolast = true;
					String[] tokens = (String[]) next;
					for ( int i = 0; i < tokens.length; i++ ) {
						buf.append( tokens[i] );
						if ( !isSubselect ) {
							buf.append( " as " ).append( NameGenerator.scalarName( c, i ) );
						}
						if ( i != tokens.length - 1 ) {
							buf.append( ", " );
						}
					}
					c++;
				}
			}
			if ( !isSubselect && !nolast ) {
				int x = c++;
				buf.append( " as " ).append( NameGenerator.scalarName( x, 0 ) );
			}

		}

		return buf.toString();
	}

	private void mergeJoins(JoinFragment ojf) throws MappingException, QueryException {

		Iterator iter = joins.entrySet().iterator();
		while ( iter.hasNext() ) {
			Map.Entry me = (Map.Entry) iter.next();
			String name = (String) me.getKey();
			JoinSequence join = (JoinSequence) me.getValue();
			join.setSelector(
					new JoinSequence.Selector() {
						@Override
						public boolean includeSubclasses(String alias) {
							return returnedTypes.contains( alias ) && !isShallowQuery();
						}
					}
			);

			if ( typeMap.containsKey( name ) ) {
				ojf.addFragment( join.toJoinFragment( enabledFilters, true ) );
			}
			else if ( collections.containsKey( name ) ) {
				ojf.addFragment( join.toJoinFragment( enabledFilters, true ) );
			}
		}

	}

	@Override
	public final Set getQuerySpaces() {
		return querySpaces;
	}

	/**
	 * Is this query called by scroll() or iterate()?
	 *
	 * @return true if it is, false if it is called by find() or list()
	 */
	boolean isShallowQuery() {
		return shallowQuery;
	}

	void addQuerySpaces(Serializable[] spaces) {
		Collections.addAll( querySpaces, spaces );
		if ( superQuery != null ) {
			superQuery.addQuerySpaces( spaces );
		}
	}

	void setDistinct(boolean distinct) {
		this.distinct = distinct;
	}

	boolean isSubquery() {
		return superQuery != null;
	}

	@Override
	public CollectionPersister[] getCollectionPersisters() {
		return collectionPersister == null ? null : new CollectionPersister[] {collectionPersister};
	}

	@Override
	protected String[] getCollectionSuffixes() {
		return collectionPersister == null ? null : new String[] {"__"};
	}

	void setCollectionToFetch(String role, String name, String ownerName, String entityName)
			throws QueryException {
		fetchName = name;
		collectionPersister = getCollectionPersister( role );
		collectionOwnerName = ownerName;
		if ( collectionPersister.getElementType().isEntityType() ) {
			addEntityToFetch( entityName );
		}
	}

	@Override
	protected String[] getSuffixes() {
		return suffixes;
	}

	@Override
	protected String[] getAliases() {
		return names;
	}

	/**
	 * Used for collection filters
	 */
	private void addFromAssociation(final String elementName, final String collectionRole)
			throws QueryException {
		//q.addCollection(collectionName, collectionRole);
		QueryableCollection persister = getCollectionPersister( collectionRole );
		Type collectionElementType = persister.getElementType();
		if ( !collectionElementType.isEntityType() ) {
			throw new QueryException( "collection of values in filter: " + elementName );
		}

		String[] keyColumnNames = persister.getKeyColumnNames();
		//if (keyColumnNames.length!=1) throw new QueryException("composite-key collection in filter: " + collectionRole);

		String collectionName;
		JoinSequence join = new JoinSequence( getFactory() );
		collectionName = persister.isOneToMany() ?
				elementName :
				createNameForCollection( collectionRole );
		join.setRoot( persister, collectionName );
		if ( !persister.isOneToMany() ) {
			//many-to-many
			addCollection( collectionName, collectionRole );
			try {
				join.addJoin(
						(AssociationType) persister.getElementType(),
						elementName,
						JoinType.INNER_JOIN,
						persister.getElementColumnNames( collectionName )
				);
			}
			catch (MappingException me) {
				throw new QueryException( me );
			}
		}
		join.addCondition( collectionName, keyColumnNames, " = ?" );
		//if ( persister.hasWhere() ) join.addCondition( persister.getSQLWhereString(collectionName) );
		EntityType elemType = (EntityType) collectionElementType;
		addFrom( elementName, elemType.getAssociatedEntityName(), join );

	}

	String getPathAlias(String path) {
		return (String) pathAliases.get( path );
	}

	JoinSequence getPathJoin(String path) {
		return (JoinSequence) pathJoins.get( path );
	}

	void addPathAliasAndJoin(String path, String alias, JoinSequence joinSequence) {
		pathAliases.put( path, alias );
		pathJoins.put( path, joinSequence );
	}

	@Override
	public List list(SharedSessionContractImplementor session, QueryParameters queryParameters)
			throws HibernateException {
		return list( session, queryParameters, getQuerySpaces(), actualReturnTypes );
	}

	/**
	 * Return the query results as an iterator
	 */
	@Override
	public Iterator iterate(QueryParameters queryParameters, EventSource session)
			throws HibernateException {

		boolean stats = session.getFactory().getStatistics().isStatisticsEnabled();
		long startTime = 0;
		if ( stats ) {
			startTime = System.nanoTime();
		}

		try {
			final List afterLoadActions = new ArrayList<>();
			final SqlStatementWrapper wrapper = executeQueryStatement(
					queryParameters,
					false,
					afterLoadActions,
					session
			);
			final ResultSet rs = wrapper.getResultSet();
			final PreparedStatement st = (PreparedStatement) wrapper.getStatement();
			HolderInstantiator hi = HolderInstantiator.createClassicHolderInstantiator(
					holderConstructor,
					queryParameters.getResultTransformer()
			);
			Iterator result = new IteratorImpl(
					rs,
					st,
					session,
					queryParameters.isReadOnly( session ),
					returnTypes,
					getColumnNames(),
					hi
			);

			if ( stats ) {
				final long endTime = System.nanoTime();
				final long milliseconds = TimeUnit.MILLISECONDS.convert( endTime - startTime, TimeUnit.NANOSECONDS );
				session.getFactory().getStatistics().queryExecuted(
						"HQL: " + queryString,
						0,
						milliseconds
				);
			}

			return result;

		}
		catch (SQLException sqle) {
			throw getFactory().getJdbcServices().getSqlExceptionHelper().convert(
					sqle,
					"could not execute query using iterate",
					getSQLString()
			);
		}

	}

	@Override
	public int executeUpdate(QueryParameters queryParameters, SharedSessionContractImplementor session) throws HibernateException {
		throw new UnsupportedOperationException( "Not supported!  Use the AST translator..." );
	}

	@Override
	protected boolean[] includeInResultRow() {
		boolean[] isResultReturned = includeInSelect;
		if ( hasScalars ) {
			isResultReturned = new boolean[returnedTypes.size()];
			Arrays.fill( isResultReturned, true );
		}
		return isResultReturned;
	}


	@Override
	protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
		return HolderInstantiator.resolveClassicResultTransformer(
				holderConstructor,
				resultTransformer
		);
	}

	@Override
	protected Object getResultColumnOrRow(
			Object[] row,
			ResultTransformer transformer,
			ResultSet rs,
			SharedSessionContractImplementor session)
			throws SQLException, HibernateException {
		Object[] resultRow = getResultRow( row, rs, session );
		return ( holderClass == null && resultRow.length == 1 ?
				resultRow[0] :
				resultRow
		);
	}

	@Override
	protected Object[] getResultRow(Object[] row, ResultSet rs, SharedSessionContractImplementor session)
			throws SQLException, HibernateException {
		Object[] resultRow;
		if ( hasScalars ) {
			String[][] scalarColumns = getColumnNames();
			int queryCols = returnTypes.length;
			resultRow = new Object[queryCols];
			for ( int i = 0; i < queryCols; i++ ) {
				resultRow[i] = returnTypes[i].nullSafeGet( rs, scalarColumns[i], session, null );
			}
		}
		else {
			resultRow = toResultRow( row );
		}
		return resultRow;
	}

	@Override
	protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
		if ( holderClass != null ) {
			for ( int i = 0; i < results.size(); i++ ) {
				Object[] row = (Object[]) results.get( i );
				try {
					results.set( i, holderConstructor.newInstance( row ) );
				}
				catch (Exception e) {
					throw new QueryException( "could not instantiate: " + holderClass, e );
				}
			}
		}
		return results;
	}

	private Object[] toResultRow(Object[] row) {
		if ( selectLength == row.length ) {
			return row;
		}
		else {
			Object[] result = new Object[selectLength];
			int j = 0;
			for ( int i = 0; i < row.length; i++ ) {
				if ( includeInSelect[i] ) {
					result[j++] = row[i];
				}
			}
			return result;
		}
	}

	void setHolderClass(Class clazz) {
		holderClass = clazz;
	}

	@Override
	protected LockMode[] getLockModes(LockOptions lockOptions) {

		// unfortunately this stuff can't be cached because
		// it is per-invocation, not constant for the
		// QueryTranslator instance
		HashMap nameLockOptions = new HashMap();
		if ( lockOptions == null ) {
			lockOptions = LockOptions.NONE;
		}

		if ( lockOptions.getAliasLockCount() > 0 ) {
			Iterator iter = lockOptions.getAliasLockIterator();
			while ( iter.hasNext() ) {
				Map.Entry me = (Map.Entry) iter.next();
				nameLockOptions.put(
						getAliasName( (String) me.getKey() ),
						me.getValue()
				);
			}
		}
		LockMode[] lockModesArray = new LockMode[names.length];
		for ( int i = 0; i < names.length; i++ ) {
			LockMode lm = (LockMode) nameLockOptions.get( names[i] );
			//if ( lm == null ) lm = LockOptions.NONE;
			if ( lm == null ) {
				lm = lockOptions.getLockMode();
			}
			lockModesArray[i] = lm;
		}
		return lockModesArray;
	}

	@Override
	protected String applyLocks(
			String sql,
			QueryParameters parameters,
			Dialect dialect,
			List afterLoadActions) throws QueryException {
		// can't cache this stuff either (per-invocation)
		final LockOptions lockOptions = parameters.getLockOptions();
		final String result;
		if ( lockOptions == null ||
				( lockOptions.getLockMode() == LockMode.NONE && lockOptions.getAliasLockCount() == 0 ) ) {
			return sql;
		}
		else {
			LockOptions locks = new LockOptions();
			locks.setLockMode( lockOptions.getLockMode() );
			locks.setTimeOut( lockOptions.getTimeOut() );
			locks.setScope( lockOptions.getScope() );
			Iterator iter = lockOptions.getAliasLockIterator();
			while ( iter.hasNext() ) {
				Map.Entry me = (Map.Entry) iter.next();
				locks.setAliasSpecificLockMode( getAliasName( (String) me.getKey() ), (LockMode) me.getValue() );
			}
			Map keyColumnNames = null;
			if ( dialect.forUpdateOfColumns() ) {
				keyColumnNames = new HashMap();
				for ( int i = 0; i < names.length; i++ ) {
					keyColumnNames.put( names[i], persisters[i].getIdentifierColumnNames() );
				}
			}
			result = dialect.applyLocksToSql( sql, locks, keyColumnNames );
		}
		logQuery( queryString, result );
		return result;
	}

	@Override
	protected boolean upgradeLocks() {
		return true;
	}

	@Override
	protected int[] getCollectionOwners() {
		return new int[] {collectionOwnerColumn};
	}

	protected boolean isCompiled() {
		return compiled;
	}

	@Override
	public String toString() {
		return queryString;
	}

	@Override
	protected int[] getOwners() {
		return owners;
	}

	@Override
	protected EntityType[] getOwnerAssociationTypes() {
		return ownerAssociationTypes;
	}

	public Class getHolderClass() {
		return holderClass;
	}

	@Override
	public Map getEnabledFilters() {
		return enabledFilters;
	}

	@Override
	public ScrollableResultsImplementor scroll(
			final QueryParameters queryParameters,
			final SharedSessionContractImplementor session) throws HibernateException {
		HolderInstantiator hi = HolderInstantiator.createClassicHolderInstantiator(
				holderConstructor,
				queryParameters.getResultTransformer()
		);
		return scroll( queryParameters, returnTypes, hi, session );
	}

	@Override
	public String getQueryIdentifier() {
		return queryIdentifier;
	}

	@Override
	protected boolean isSubselectLoadingEnabled() {
		return hasSubselectLoadableCollections();
	}

	@Override
	public void validateScrollability() throws HibernateException {
		// This is the legacy behaviour for HQL queries...
		if ( getCollectionPersisters() != null ) {
			throw new HibernateException( "Cannot scroll queries which initialize collections" );
		}
	}

	@Override
	public boolean containsCollectionFetches() {
		return false;
	}

	@Override
	public boolean isManipulationStatement() {
		// classic parser does not support bulk manipulation statements
		return false;
	}

	@Override
	public Class getDynamicInstantiationResultType() {
		return holderClass;
	}

	@Override
	public ParameterTranslations getParameterTranslations() {
		return new ParameterTranslations() {
			@Override
			@SuppressWarnings("unchecked")
			public Map getNamedParameterInformationMap() {
				return namedParameters;
			}

			@Override
			@SuppressWarnings("unchecked")
			public Map getPositionalParameterInformationMap() {
				return ordinalParameters;
			}

			@Override
			public PositionalParameterInformation getPositionalParameterInformation(int position) {
				return ordinalParameters.get( position );
			}

			@Override
			public NamedParameterInformation getNamedParameterInformation(String name) {
				return namedParameters.get( name );
			}
		};
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy