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

org.hibernate.hql.internal.ast.HqlSqlWalker Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
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.ast;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.hibernate.QueryException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.internal.JoinSequence;
import org.hibernate.engine.internal.ParameterBinder;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.hql.internal.CollectionProperties;
import org.hibernate.hql.internal.antlr.HqlSqlBaseWalker;
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
import org.hibernate.hql.internal.antlr.HqlTokenTypes;
import org.hibernate.hql.internal.antlr.SqlTokenTypes;
import org.hibernate.hql.internal.ast.tree.AggregateNode;
import org.hibernate.hql.internal.ast.tree.AssignmentSpecification;
import org.hibernate.hql.internal.ast.tree.CastFunctionNode;
import org.hibernate.hql.internal.ast.tree.CollectionFunction;
import org.hibernate.hql.internal.ast.tree.ConstructorNode;
import org.hibernate.hql.internal.ast.tree.DeleteStatement;
import org.hibernate.hql.internal.ast.tree.DotNode;
import org.hibernate.hql.internal.ast.tree.EntityJoinFromElement;
import org.hibernate.hql.internal.ast.tree.FromClause;
import org.hibernate.hql.internal.ast.tree.FromElement;
import org.hibernate.hql.internal.ast.tree.FromElementFactory;
import org.hibernate.hql.internal.ast.tree.FromReferenceNode;
import org.hibernate.hql.internal.ast.tree.IdentNode;
import org.hibernate.hql.internal.ast.tree.IndexNode;
import org.hibernate.hql.internal.ast.tree.InsertStatement;
import org.hibernate.hql.internal.ast.tree.IntoClause;
import org.hibernate.hql.internal.ast.tree.MethodNode;
import org.hibernate.hql.internal.ast.tree.OperatorNode;
import org.hibernate.hql.internal.ast.tree.ParameterContainer;
import org.hibernate.hql.internal.ast.tree.ParameterNode;
import org.hibernate.hql.internal.ast.tree.QueryNode;
import org.hibernate.hql.internal.ast.tree.ResolvableNode;
import org.hibernate.hql.internal.ast.tree.RestrictableStatement;
import org.hibernate.hql.internal.ast.tree.ResultVariableRefNode;
import org.hibernate.hql.internal.ast.tree.SelectClause;
import org.hibernate.hql.internal.ast.tree.SelectExpression;
import org.hibernate.hql.internal.ast.tree.UpdateStatement;
import org.hibernate.hql.internal.ast.util.ASTPrinter;
import org.hibernate.hql.internal.ast.util.ASTUtil;
import org.hibernate.hql.internal.ast.util.AliasGenerator;
import org.hibernate.hql.internal.ast.util.JoinProcessor;
import org.hibernate.hql.internal.ast.util.LiteralProcessor;
import org.hibernate.hql.internal.ast.util.NodeTraverser;
import org.hibernate.hql.internal.ast.util.SessionFactoryHelper;
import org.hibernate.hql.internal.ast.util.SyntheticAndFactory;
import org.hibernate.hql.internal.ast.util.TokenPrinters;
import org.hibernate.hql.spi.QueryTranslator;
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.log.DeprecationLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.param.CollectionFilterKeyParameterSpecification;
import org.hibernate.param.NamedParameterSpecification;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.param.PositionalParameterSpecification;
import org.hibernate.param.VersionTypeSeedParameterSpecification;
import org.hibernate.persister.collection.CollectionPropertyNames;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.JoinType;
import org.hibernate.type.AssociationType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.DbTimestampType;
import org.hibernate.type.Type;
import org.hibernate.type.VersionType;
import org.hibernate.usertype.UserVersionType;

import antlr.ASTFactory;
import antlr.RecognitionException;
import antlr.SemanticException;
import antlr.collections.AST;

import static org.hibernate.hql.spi.QueryTranslator.ERROR_LEGACY_ORDINAL_PARAMS_NO_LONGER_SUPPORTED;

/**
 * Implements methods used by the HQL->SQL tree transform grammar (a.k.a. the second phase).
 * 
    *
  • Isolates the Hibernate API-specific code from the ANTLR generated code.
  • *
  • Handles the SQL fragments generated by the persisters in order to create the SELECT and FROM clauses, * taking into account the joins and projections that are implied by the mappings (persister/queryable).
  • *
  • Uses SqlASTFactory to create customized AST nodes.
  • *
* * @see SqlASTFactory */ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, ParameterBinder.NamedParameterSource { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( HqlSqlWalker.class ); private final QueryTranslatorImpl queryTranslatorImpl; private final HqlParser hqlParser; private final SessionFactoryHelper sessionFactoryHelper; private final Map tokenReplacements; private final AliasGenerator aliasGenerator = new AliasGenerator(); private final LiteralProcessor literalProcessor; private final ParseErrorHandler parseErrorHandler; private final String collectionFilterRole; private FromClause currentFromClause; private SelectClause selectClause; /** * Maps each top-level result variable to its SelectExpression; * (excludes result variables defined in subqueries) */ private Map selectExpressionsByResultVariable = new HashMap<>(); private Set querySpaces = new HashSet<>(); private int parameterCount; private Map namedParameters; private Map positionalParameters; private ArrayList parameterSpecs = new ArrayList<>(); private int numberOfParametersInSetClause; private ArrayList assignmentSpecifications = new ArrayList(); private JoinType impliedJoinType = JoinType.INNER_JOIN; private boolean inEntityGraph; /** * Create a new tree transformer. * * @param qti Back pointer to the query translator implementation that is using this tree transform. * @param sfi The session factory implementor where the Hibernate mappings can be found. * @param parser A reference to the phase-1 parser * @param tokenReplacements Registers the token replacement map with the walker. This map will * be used to substitute function names and constants. * @param collectionRole The collection role name of the collection used as the basis for the * filter, NULL if this is not a collection filter compilation. */ public HqlSqlWalker( QueryTranslatorImpl qti, SessionFactoryImplementor sfi, HqlParser parser, Map tokenReplacements, String collectionRole) { setASTFactory( new SqlASTFactory( this ) ); // Initialize the error handling delegate. this.parseErrorHandler = new ErrorTracker( qti.getQueryString() ); this.queryTranslatorImpl = qti; this.sessionFactoryHelper = new SessionFactoryHelper( sfi ); this.literalProcessor = new LiteralProcessor( this ); this.tokenReplacements = tokenReplacements; this.collectionFilterRole = collectionRole; this.hqlParser = parser; } // handle trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private int traceDepth; @Override public void traceIn(String ruleName, AST tree) { if ( !LOG.isTraceEnabled() ) { return; } if ( inputState.guessing > 0 ) { return; } String prefix = StringHelper.repeat( '-', ( traceDepth++ * 2 ) ) + "-> "; String traceText = ruleName + " (" + buildTraceNodeName( tree ) + ")"; LOG.trace( prefix + traceText ); } private String buildTraceNodeName(AST tree) { return tree == null ? "???" : tree.getText() + " [" + TokenPrinters.SQL_TOKEN_PRINTER.getTokenTypeName( tree.getType() ) + "]"; } @Override public void traceOut(String ruleName, AST tree) { if ( !LOG.isTraceEnabled() ) { return; } if ( inputState.guessing > 0 ) { return; } String prefix = "<-" + StringHelper.repeat( '-', ( --traceDepth * 2 ) ) + " "; LOG.trace( prefix + ruleName ); } @Override protected void prepareFromClauseInputTree(AST fromClauseInput) { if ( !isSubQuery() ) { // // inject param specifications to account for dynamic filter param values // if ( ! getEnabledFilters().isEmpty() ) { // Iterator filterItr = getEnabledFilters().values().iterator(); // while ( filterItr.hasNext() ) { // FilterImpl filter = ( FilterImpl ) filterItr.next(); // if ( ! filter.getFilterDefinition().getParameterNames().isEmpty() ) { // Iterator paramItr = filter.getFilterDefinition().getParameterNames().iterator(); // while ( paramItr.hasNext() ) { // String parameterName = ( String ) paramItr.next(); // // currently param filters *only* work with single-column parameter types; // // if that limitation is ever lifted, this logic will need to change to account for that // ParameterNode collectionFilterKeyParameter = ( ParameterNode ) astFactory.create( PARAM, "?" ); // DynamicFilterParameterSpecification paramSpec = new DynamicFilterParameterSpecification( // filter.getName(), // parameterName, // filter.getFilterDefinition().getParameterType( parameterName ), // positionalParameterCount++ // ); // collectionFilterKeyParameter.setHqlParameterSpecification( paramSpec ); // parameterSpecs.add( paramSpec ); // } // } // } // } if ( isFilter() ) { // Handle collection-filter compilation. // IMPORTANT NOTE: This is modifying the INPUT (HQL) tree, not the output tree! QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole ); Type collectionElementType = persister.getElementType(); if ( !collectionElementType.isEntityType() ) { throw new QueryException( "collection of values in filter: this" ); } String collectionElementEntityName = persister.getElementPersister().getEntityName(); ASTFactory inputAstFactory = hqlParser.getASTFactory(); AST fromElement = inputAstFactory.create( HqlTokenTypes.FILTER_ENTITY, collectionElementEntityName ); ASTUtil.createSibling( inputAstFactory, HqlTokenTypes.ALIAS, "this", fromElement ); fromClauseInput.addChild( fromElement ); // Show the modified AST. LOG.debug( "prepareFromClauseInputTree() : Filter - Added 'this' as a from element..." ); queryTranslatorImpl.showHqlAst( hqlParser.getAST() ); // Create a parameter specification for the collection filter... final Type collectionFilterKeyType = sessionFactoryHelper.requireQueryableCollection( collectionFilterRole ) .getKeyType(); final ParameterNode collectionFilterKeyParameter = (ParameterNode) astFactory.create( PARAM, "?" ); final CollectionFilterKeyParameterSpecification collectionFilterKeyParameterSpec = new CollectionFilterKeyParameterSpecification( collectionFilterRole, collectionFilterKeyType ); parameterCount++; collectionFilterKeyParameter.setHqlParameterSpecification( collectionFilterKeyParameterSpec ); parameterSpecs.add( collectionFilterKeyParameterSpec ); } } } public boolean isFilter() { return collectionFilterRole != null; } public String getCollectionFilterRole() { return collectionFilterRole; } public boolean isInEntityGraph() { return inEntityGraph; } public SessionFactoryHelper getSessionFactoryHelper() { return sessionFactoryHelper; } public Map getTokenReplacements() { return tokenReplacements; } public AliasGenerator getAliasGenerator() { return aliasGenerator; } public FromClause getCurrentFromClause() { return currentFromClause; } public ParseErrorHandler getParseErrorHandler() { return parseErrorHandler; } @Override public void reportError(RecognitionException e) { parseErrorHandler.reportError( e ); // Use the delegate. } @Override public void reportError(String s) { parseErrorHandler.reportError( s ); // Use the delegate. } @Override public void reportWarning(String s) { parseErrorHandler.reportWarning( s ); } /** * Returns the set of unique query spaces (a.k.a. * table names) that occurred in the query. * * @return A set of table names (Strings). */ public Set getQuerySpaces() { return querySpaces; } @Override protected AST createFromElement(String path, AST alias, AST propertyFetch) throws SemanticException { FromElement fromElement = currentFromClause.addFromElement( path, alias ); fromElement.setAllPropertyFetch( propertyFetch != null ); return fromElement; } @Override protected AST createFromFilterElement(AST filterEntity, AST alias) throws SemanticException { FromElement fromElement = currentFromClause.addFromElement( filterEntity.getText(), alias ); FromClause fromClause = fromElement.getFromClause(); QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole ); // Get the names of the columns used to link between the collection // owner and the collection elements. String[] keyColumnNames = persister.getKeyColumnNames(); String fkTableAlias = persister.isOneToMany() ? fromElement.getTableAlias() : fromClause.getAliasGenerator().createName( collectionFilterRole ); JoinSequence join = sessionFactoryHelper.createJoinSequence(); join.setRoot( persister, fkTableAlias ); if ( !persister.isOneToMany() ) { join.addJoin( (AssociationType) persister.getElementType(), fromElement.getTableAlias(), JoinType.INNER_JOIN, persister.getElementColumnNames( fkTableAlias ) ); } join.addCondition( fkTableAlias, keyColumnNames, " = ?" ); fromElement.setJoinSequence( join ); fromElement.setFilter( true ); LOG.debug( "createFromFilterElement() : processed filter FROM element." ); return fromElement; } @Override protected void createFromJoinElement( AST path, AST alias, int joinType, AST fetchNode, AST propertyFetch, AST with) throws SemanticException { boolean fetch = fetchNode != null; if ( fetch && isSubQuery() ) { throw new QueryException( "fetch not allowed in subquery from-elements" ); } // the incoming "path" can be either: // 1) an implicit join path (join p.address.city) // 2) an entity-join (join com.acme.User) // // so make the proper interpretation here... final EntityPersister entityJoinReferencedPersister = resolveEntityJoinReferencedPersister( path ); if ( entityJoinReferencedPersister != null ) { // `path` referenced an entity final EntityJoinFromElement join = createEntityJoin( entityJoinReferencedPersister, alias, joinType, propertyFetch, with ); ( (FromReferenceNode) path ).setFromElement( join ); } else { if ( path.getType() != SqlTokenTypes.DOT ) { throw new SemanticException( "Path expected for join!" ); } DotNode dot = (DotNode) path; JoinType hibernateJoinType = JoinProcessor.toHibernateJoinType( joinType ); dot.setJoinType( hibernateJoinType ); // Tell the dot node about the join type. dot.setFetch( fetch ); // Generate an explicit join for the root dot node. The implied joins will be collected and passed up // to the root dot node. dot.resolve( true, false, alias == null ? null : alias.getText() ); final FromElement fromElement; if ( dot.getDataType() != null && dot.getDataType().isComponentType() ) { if ( dot.getDataType().isAnyType() ) { throw new SemanticException( "An AnyType attribute cannot be join fetched" ); // ^^ because the discriminator (aka, the "meta columns") must be known to the SQL in // a non-parameterized way. } FromElementFactory factory = new FromElementFactory( getCurrentFromClause(), dot.getLhs().getFromElement(), dot.getPropertyPath(), alias == null ? null : alias.getText(), null, false ); fromElement = factory.createComponentJoin( (CompositeType) dot.getDataType() ); } else { fromElement = dot.getImpliedJoin(); fromElement.setAllPropertyFetch( propertyFetch != null ); if ( with != null ) { if ( fetch ) { throw new SemanticException( "with-clause not allowed on fetched associations; use filters" ); } handleWithFragment( fromElement, with ); } } if ( LOG.isDebugEnabled() ) { LOG.debug( "createFromJoinElement() : " + getASTPrinter().showAsString( fromElement, "-- join tree --" ) ); } } } private EntityPersister resolveEntityJoinReferencedPersister(AST path) { if ( path.getType() == IDENT ) { final IdentNode pathIdentNode = (IdentNode) path; String name = path.getText(); if ( name == null ) { name = pathIdentNode.getOriginalText(); } return sessionFactoryHelper.findEntityPersisterByName( name ); } else if ( path.getType() == DOT ) { final String pathText = ASTUtil.getPathText( path ); return sessionFactoryHelper.findEntityPersisterByName( pathText ); } return null; } @Override protected void finishFromClause(AST fromClause) throws SemanticException { ( (FromClause) fromClause ).finishInit(); } private EntityJoinFromElement createEntityJoin( EntityPersister entityPersister, AST aliasNode, int joinType, AST propertyFetch, AST with) throws SemanticException { final String alias = aliasNode == null ? null : aliasNode.getText(); LOG.debugf( "Creating entity-join FromElement [%s -> %s]", alias, entityPersister.getEntityName() ); EntityJoinFromElement join = new EntityJoinFromElement( this, getCurrentFromClause(), entityPersister, JoinProcessor.toHibernateJoinType( joinType ), propertyFetch != null, alias ); if ( with != null ) { handleWithFragment( join, with ); } return join; } private void handleWithFragment(FromElement fromElement, AST hqlWithNode) throws SemanticException { try { withClause( hqlWithNode ); AST hqlSqlWithNode = returnAST; if ( LOG.isDebugEnabled() ) { LOG.debug( "handleWithFragment() : " + getASTPrinter().showAsString( hqlSqlWithNode, "-- with clause --" ) ); } WithClauseVisitor visitor = new WithClauseVisitor( fromElement, queryTranslatorImpl ); NodeTraverser traverser = new NodeTraverser( visitor ); traverser.traverseDepthFirst( hqlSqlWithNode ); SqlGenerator sql = new SqlGenerator( getSessionFactoryHelper().getFactory() ); sql.whereExpr( hqlSqlWithNode.getFirstChild() ); fromElement.setWithClauseFragment( "(" + sql.getSQL() + ")" ); } catch (SemanticException e) { throw e; } catch (InvalidWithClauseException e) { throw e; } catch (Exception e) { throw new SemanticException( e.getMessage() ); } } private static class WithClauseVisitor implements NodeTraverser.VisitationStrategy { private final FromElement joinFragment; private final QueryTranslatorImpl queryTranslatorImpl; private FromElement referencedFromElement; private String joinAlias; public WithClauseVisitor(FromElement fromElement, QueryTranslatorImpl queryTranslatorImpl) { this.joinFragment = fromElement; this.queryTranslatorImpl = queryTranslatorImpl; } public void visit(AST node) { // TODO : currently expects that the individual with expressions apply to the same sql table join. // This may not be the case for joined-subclass where the property values // might be coming from different tables in the joined hierarchy. At some // point we should expand this to support that capability. However, that has // some difficulties: // 1) the biggest is how to handle ORs when the individual comparisons are // linked to different sql joins. // 2) here we would need to track each comparison individually, along with // the join alias to which it applies and then pass that information // back to the FromElement so it can pass it along to the JoinSequence if ( node instanceof DotNode ) { DotNode dotNode = (DotNode) node; FromElement fromElement = dotNode.getFromElement(); if ( referencedFromElement != null ) { // if ( fromElement != referencedFromElement ) { // throw new HibernateException( "with-clause referenced two different from-clause elements" ); // } } else { referencedFromElement = fromElement; joinAlias = extractAppliedAlias( dotNode ); // TODO : temporary // needed because currently persister is the one that // creates and renders the join fragments for inheritance // hierarchies... // if ( !joinAlias.equals( referencedFromElement.getTableAlias() ) ) { // throw new InvalidWithClauseException( // "with clause can only reference columns in the driving table", // queryTranslatorImpl.getQueryString() // ); // } } } else if ( node instanceof ParameterNode ) { applyParameterSpecification( ( (ParameterNode) node ).getHqlParameterSpecification() ); } else if ( node instanceof ParameterContainer ) { applyParameterSpecifications( (ParameterContainer) node ); } } private void applyParameterSpecifications(ParameterContainer parameterContainer) { if ( parameterContainer.hasEmbeddedParameters() ) { ParameterSpecification[] specs = parameterContainer.getEmbeddedParameters(); for ( ParameterSpecification spec : specs ) { applyParameterSpecification( spec ); } } } private void applyParameterSpecification(ParameterSpecification paramSpec) { joinFragment.addEmbeddedParameter( paramSpec ); } private String extractAppliedAlias(DotNode dotNode) { return dotNode.getText().substring( 0, dotNode.getText().indexOf( '.' ) ); } public FromElement getReferencedFromElement() { return referencedFromElement; } public String getJoinAlias() { return joinAlias; } } /** * Sets the current 'FROM' context. * * @param fromNode The new 'FROM' context. * @param inputFromNode The from node from the input AST. */ @Override protected void pushFromClause(AST fromNode, AST inputFromNode) { FromClause newFromClause = (FromClause) fromNode; newFromClause.setParentFromClause( currentFromClause ); currentFromClause = newFromClause; } /** * Returns to the previous 'FROM' context. */ private void popFromClause() { currentFromClause = currentFromClause.getParentFromClause(); } @Override protected void lookupAlias(AST aliasRef) throws SemanticException { FromElement alias = currentFromClause.getFromElement( aliasRef.getText() ); FromReferenceNode aliasRefNode = (FromReferenceNode) aliasRef; aliasRefNode.setFromElement( alias ); } @Override protected void setImpliedJoinType(int joinType) { impliedJoinType = JoinProcessor.toHibernateJoinType( joinType ); } public JoinType getImpliedJoinType() { return impliedJoinType; } @Override protected AST lookupProperty(AST dot, boolean root, boolean inSelect) throws SemanticException { DotNode dotNode = (DotNode) dot; FromReferenceNode lhs = dotNode.getLhs(); AST rhs = lhs.getNextSibling(); // this used to be a switch statement based on the rhs's node type // expecting it to be SqlTokenTypes.ELEMENTS or // SqlTokenTypes.INDICES in the cases where the re-arranging is needed // // In such cases it additionally expects the RHS to be a CollectionFunction node. // // However, in my experience these assumptions sometimes did not works as sometimes the node // types come in with the node type WEIRD_IDENT. What this does now is to: // 1) see if the LHS is a collection // 2) see if the RHS is a reference to one of the "collection properties". // if both are true, we log a deprecation warning if ( lhs.getDataType() != null && lhs.getDataType().isCollectionType() ) { if ( CollectionProperties.isCollectionProperty( rhs.getText() ) ) { DeprecationLogger.DEPRECATION_LOGGER.logDeprecationOfCollectionPropertiesInHql( rhs.getText(), lhs.getPath() ); } // perform the re-arrangement if ( CollectionPropertyNames.COLLECTION_INDICES.equalsIgnoreCase( rhs.getText() ) || CollectionPropertyNames.COLLECTION_ELEMENTS.equalsIgnoreCase( rhs.getText() ) ) { if ( LOG.isDebugEnabled() ) { LOG.debugf( "lookupProperty() %s => %s(%s)", dotNode.getPath(), rhs.getText(), lhs.getPath() ); } final CollectionFunction f; if ( rhs instanceof CollectionFunction ) { f = (CollectionFunction) rhs; } else { f = new CollectionFunction(); f.initialize( SqlTokenTypes.METHOD_CALL, rhs.getText() ); f.initialize( this ); } // Re-arrange the tree so that the collection function is the root and the lhs is the path. f.setFirstChild( lhs ); lhs.setNextSibling( null ); dotNode.setFirstChild( f ); resolve( lhs ); // Don't forget to resolve the argument! f.resolve( inSelect ); // Resolve the collection function now. return f; } } // otherwise, resolve the path and return it dotNode.resolveFirstChild(); return dotNode; } @Override protected boolean isNonQualifiedPropertyRef(AST ident) { final String identText = ident.getText(); if ( currentFromClause.isFromElementAlias( identText ) ) { return false; } List fromElements = currentFromClause.getExplicitFromElements(); if ( fromElements.size() == 1 ) { final FromElement fromElement = (FromElement) fromElements.get( 0 ); try { LOG.tracev( "Attempting to resolve property [{0}] as a non-qualified ref", identText ); return fromElement.getPropertyMapping( identText ).toType( identText ) != null; } catch (QueryException e) { // Should mean that no such property was found } } return false; } @Override protected AST lookupNonQualifiedProperty(AST property) throws SemanticException { final FromElement fromElement = (FromElement) currentFromClause.getExplicitFromElements().get( 0 ); AST syntheticDotNode = generateSyntheticDotNodeForNonQualifiedPropertyRef( property, fromElement ); return lookupProperty( syntheticDotNode, false, getCurrentClauseType() == HqlSqlTokenTypes.SELECT ); } private AST generateSyntheticDotNodeForNonQualifiedPropertyRef(AST property, FromElement fromElement) { AST dot = getASTFactory().create( DOT, "{non-qualified-property-ref}" ); // TODO : better way?!? ( (DotNode) dot ).setPropertyPath( ( (FromReferenceNode) property ).getPath() ); IdentNode syntheticAlias = (IdentNode) getASTFactory().create( IDENT, "{synthetic-alias}" ); syntheticAlias.setFromElement( fromElement ); syntheticAlias.setResolved(); dot.setFirstChild( syntheticAlias ); dot.addChild( property ); return dot; } @Override protected void processQuery(AST select, AST query) throws SemanticException { if ( LOG.isDebugEnabled() ) { LOG.debugf( "processQuery() : %s", query.toStringTree() ); } try { QueryNode qn = (QueryNode) query; // Was there an explicit select expression? boolean explicitSelect = select != null && select.getNumberOfChildren() > 0; // Add in the EntityGraph attribute nodes. if ( queryTranslatorImpl.getEntityGraphQueryHint() != null ) { final boolean oldInEntityGraph = inEntityGraph; try { inEntityGraph = true; qn.getFromClause().getFromElements().addAll( queryTranslatorImpl.getEntityGraphQueryHint().toFromElements( qn.getFromClause(), this ) ); } finally { inEntityGraph = oldInEntityGraph; } } if ( !explicitSelect ) { // No explicit select expression; render the id and properties // projection lists for every persister in the from clause into // a single 'token node'. //TODO: the only reason we need this stuff now is collection filters, // we should get rid of derived select clause completely! createSelectClauseFromFromClause( qn ); } else { // Use the explicitly declared select expression; determine the // return types indicated by each select token useSelectClause( select ); } // After that, process the JOINs. // Invoke a delegate to do the work, as this is farily complex. JoinProcessor joinProcessor = new JoinProcessor( this ); joinProcessor.processJoins( qn ); // Attach any mapping-defined "ORDER BY" fragments Iterator itr = qn.getFromClause().getProjectionList().iterator(); while ( itr.hasNext() ) { final FromElement fromElement = (FromElement) itr.next(); // if ( fromElement.isFetch() && fromElement.isCollectionJoin() ) { if ( fromElement.isFetch() && fromElement.getQueryableCollection() != null ) { // Does the collection referenced by this FromElement // specify an order-by attribute? If so, attach it to // the query's order-by if ( fromElement.getQueryableCollection().hasOrdering() ) { String orderByFragment = fromElement .getQueryableCollection() .getSQLOrderByString( fromElement.getCollectionTableAlias() ); qn.getOrderByClause().addOrderFragment( orderByFragment ); } if ( fromElement.getQueryableCollection().hasManyToManyOrdering() ) { String orderByFragment = fromElement.getQueryableCollection() .getManyToManyOrderByString( fromElement.getTableAlias() ); qn.getOrderByClause().addOrderFragment( orderByFragment ); } } } } finally { popFromClause(); } } protected void postProcessDML(RestrictableStatement statement) throws SemanticException { statement.getFromClause().resolve(); FromElement fromElement = (FromElement) statement.getFromClause().getFromElements().get( 0 ); Queryable persister = fromElement.getQueryable(); // Make #@%$^#^&# sure no alias is applied to the table name fromElement.setText( persister.getTableName() ); // // append any filter fragments; the EMPTY_MAP is used under the assumption that // // currently enabled filters should not affect this process // if ( persister.getDiscriminatorType() != null ) { // new SyntheticAndFactory( getASTFactory() ).addDiscriminatorWhereFragment( // statement, // persister, // java.util.Collections.EMPTY_MAP, // fromElement.getTableAlias() // ); // } if ( persister.getDiscriminatorType() != null || !queryTranslatorImpl.getEnabledFilters().isEmpty() ) { new SyntheticAndFactory( this ).addDiscriminatorWhereFragment( statement, persister, queryTranslatorImpl.getEnabledFilters(), fromElement.getTableAlias() ); } } @Override protected void postProcessUpdate(AST update) throws SemanticException { UpdateStatement updateStatement = (UpdateStatement) update; postProcessDML( updateStatement ); } @Override protected void postProcessDelete(AST delete) throws SemanticException { postProcessDML( (DeleteStatement) delete ); } @Override protected void postProcessInsert(AST insert) throws SemanticException, QueryException { InsertStatement insertStatement = (InsertStatement) insert; insertStatement.validate(); SelectClause selectClause = insertStatement.getSelectClause(); Queryable persister = insertStatement.getIntoClause().getQueryable(); if ( !insertStatement.getIntoClause().isExplicitIdInsertion() ) { // the insert did not explicitly reference the id. See if // 1) that is allowed // 2) whether we need to alter the SQL tree to account for id final IdentifierGenerator generator = persister.getIdentifierGenerator(); if ( !BulkInsertionCapableIdentifierGenerator.class.isInstance( generator ) ) { throw new QueryException( "Invalid identifier generator encountered for implicit id handling as part of bulk insertions" ); } final BulkInsertionCapableIdentifierGenerator capableGenerator = BulkInsertionCapableIdentifierGenerator.class.cast( generator ); if ( !capableGenerator.supportsBulkInsertionIdentifierGeneration() ) { throw new QueryException( "Identifier generator reported it does not support implicit id handling as part of bulk insertions" ); } final String fragment = capableGenerator.determineBulkInsertionIdentifierGenerationSelectFragment( sessionFactoryHelper.getFactory().getDialect() ); if ( fragment != null ) { // we got a fragment from the generator, so alter the sql tree... // // first, wrap the fragment as a node AST fragmentNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, fragment ); // next, rearrange the SQL tree to add the fragment node as the first select expression AST originalFirstSelectExprNode = selectClause.getFirstChild(); selectClause.setFirstChild( fragmentNode ); fragmentNode.setNextSibling( originalFirstSelectExprNode ); // finally, prepend the id column name(s) to the insert-spec insertStatement.getIntoClause().prependIdColumnSpec(); } } if ( sessionFactoryHelper.getFactory().getDialect().supportsParametersInInsertSelect() ) { AST child = selectClause.getFirstChild(); int i = 0; while ( child != null ) { if ( child instanceof ParameterNode ) { // infer the parameter type from the type listed in the INSERT INTO clause ( (ParameterNode) child ).setExpectedType( insertStatement.getIntoClause() .getInsertionTypes()[selectClause.getParameterPositions().get( i )] ); i++; } child = child.getNextSibling(); } } final boolean includeVersionProperty = persister.isVersioned() && !insertStatement.getIntoClause().isExplicitVersionInsertion() && persister.isVersionPropertyInsertable(); if ( includeVersionProperty ) { // We need to seed the version value as part of this bulk insert VersionType versionType = persister.getVersionType(); AST versionValueNode = null; if ( sessionFactoryHelper.getFactory().getDialect().supportsParametersInInsertSelect() ) { int[] sqlTypes = versionType.sqlTypes( sessionFactoryHelper.getFactory() ); if ( sqlTypes == null || sqlTypes.length == 0 ) { throw new IllegalStateException( versionType.getClass() + ".sqlTypes() returns null or empty array" ); } if ( sqlTypes.length > 1 ) { throw new IllegalStateException( versionType.getClass() + ".sqlTypes() returns > 1 element; only single-valued versions are allowed." ); } versionValueNode = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" ); ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType ); ( (ParameterNode) versionValueNode ).setHqlParameterSpecification( paramSpec ); parameterSpecs.add( 0, paramSpec ); if ( sessionFactoryHelper.getFactory().getDialect().requiresCastingOfParametersInSelectClause() ) { // we need to wrtap the param in a cast() MethodNode versionMethodNode = (MethodNode) getASTFactory().create( HqlSqlTokenTypes.METHOD_CALL, "(" ); AST methodIdentNode = getASTFactory().create( HqlSqlTokenTypes.IDENT, "cast" ); versionMethodNode.addChild( methodIdentNode ); versionMethodNode.initializeMethodNode( methodIdentNode, true ); AST castExprListNode = getASTFactory().create( HqlSqlTokenTypes.EXPR_LIST, "exprList" ); methodIdentNode.setNextSibling( castExprListNode ); castExprListNode.addChild( versionValueNode ); versionValueNode.setNextSibling( getASTFactory().create( HqlSqlTokenTypes.IDENT, sessionFactoryHelper.getFactory().getDialect().getTypeName( sqlTypes[0] ) ) ); processFunction( versionMethodNode, true ); versionValueNode = versionMethodNode; } } else { if ( isIntegral( versionType ) ) { try { Object seedValue = versionType.seed( null ); versionValueNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, seedValue.toString() ); } catch (Throwable t) { throw new QueryException( "could not determine seed value for version on bulk insert [" + versionType + "]" ); } } else if ( isDatabaseGeneratedTimestamp( versionType ) ) { String functionName = sessionFactoryHelper.getFactory() .getDialect() .getCurrentTimestampSQLFunctionName(); versionValueNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, functionName ); } else { throw new QueryException( "cannot handle version type [" + versionType + "] on bulk inserts with dialects not supporting parameterSpecs in insert-select statements" ); } } AST currentFirstSelectExprNode = selectClause.getFirstChild(); selectClause.setFirstChild( versionValueNode ); versionValueNode.setNextSibling( currentFirstSelectExprNode ); insertStatement.getIntoClause().prependVersionColumnSpec(); } if ( insertStatement.getIntoClause().isDiscriminated() ) { String sqlValue = insertStatement.getIntoClause().getQueryable().getDiscriminatorSQLValue(); AST discrimValue = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, sqlValue ); insertStatement.getSelectClause().addChild( discrimValue ); } } private boolean isDatabaseGeneratedTimestamp(Type type) { // currently only the Hibernate-supplied DbTimestampType is supported here return DbTimestampType.class.isAssignableFrom( type.getClass() ); } private boolean isIntegral(Type type) { return Long.class.isAssignableFrom( type.getReturnedClass() ) || Integer.class.isAssignableFrom( type.getReturnedClass() ) || long.class.isAssignableFrom( type.getReturnedClass() ) || int.class.isAssignableFrom( type.getReturnedClass() ); } private void useSelectClause(AST select) throws SemanticException { selectClause = (SelectClause) select; selectClause.initializeExplicitSelectClause( currentFromClause ); } private void createSelectClauseFromFromClause(QueryNode qn) throws SemanticException { AST select = astFactory.create( SELECT_CLAUSE, "{derived select clause}" ); AST sibling = qn.getFromClause(); qn.setFirstChild( select ); select.setNextSibling( sibling ); selectClause = (SelectClause) select; selectClause.initializeDerivedSelectClause( currentFromClause ); LOG.debug( "Derived SELECT clause created." ); } @Override protected void resolve(AST node) throws SemanticException { resolve(node, null); } @Override protected void resolve(AST node, AST predicateNode) throws SemanticException { if ( node != null ) { // This is called when it's time to fully resolve a path expression. ResolvableNode r = (ResolvableNode) node; if ( isInFunctionCall() ) { r.resolveInFunctionCall( false, true ); } else { r.resolve( false, true, null, null, predicateNode ); // Generate implicit joins, only if necessary. } } } @Override protected void resolveSelectExpression(AST node) throws SemanticException { // This is called when it's time to fully resolve a path expression. int type = node.getType(); switch ( type ) { case DOT: { DotNode dot = (DotNode) node; dot.resolveSelectExpression(); break; } case ALIAS_REF: { // Notify the FROM element that it is being referenced by the select. FromReferenceNode aliasRefNode = (FromReferenceNode) node; //aliasRefNode.resolve( false, false, aliasRefNode.getText() ); //TODO: is it kosher to do it here? aliasRefNode.resolve( false, false ); //TODO: is it kosher to do it here? FromElement fromElement = aliasRefNode.getFromElement(); if ( fromElement != null ) { fromElement.setIncludeSubclasses( true ); } break; } default: { break; } } } @Override protected void beforeSelectClause() throws SemanticException { // Turn off includeSubclasses on all FromElements. FromClause from = getCurrentFromClause(); List fromElements = from.getFromElements(); for ( Iterator iterator = fromElements.iterator(); iterator.hasNext(); ) { FromElement fromElement = (FromElement) iterator.next(); fromElement.setIncludeSubclasses( false ); } } @Override protected AST generatePositionalParameter(AST delimiterNode, AST numberNode) throws SemanticException { // todo : we check this multiple times if ( getSessionFactoryHelper().isStrictJPAQLComplianceEnabled() && namedParameters != null ) { throw new SemanticException( "Cannot mix positional and named parameters: " + queryTranslatorImpl.getQueryString() ); } if ( numberNode == null ) { throw new QueryException( String.format( Locale.ROOT, ERROR_LEGACY_ORDINAL_PARAMS_NO_LONGER_SUPPORTED, queryTranslatorImpl.getQueryString() ) ); } final String positionString = numberNode.getText(); final int label = Integer.parseInt( positionString ); trackPositionalParameterPositions( label ); final ParameterNode parameter = (ParameterNode) astFactory.create( PARAM, positionString ); parameter.setText( "?" ); final int queryParamtersPosition = isFilter() ? label : label - 1; final PositionalParameterSpecification paramSpec = new PositionalParameterSpecification( delimiterNode.getLine(), delimiterNode.getColumn(), label, queryParamtersPosition ); parameter.setHqlParameterSpecification( paramSpec ); parameterSpecs.add( paramSpec ); return parameter; } @SuppressWarnings("unchecked") private void trackPositionalParameterPositions(int label) { if ( positionalParameters == null ) { positionalParameters = new HashMap(); } final Integer loc = parameterCount++; final Object existingValue = positionalParameters.get( label ); if ( existingValue == null ) { positionalParameters.put( label, loc ); } else if ( existingValue instanceof Integer ) { final ArrayList list = new ArrayList(); positionalParameters.put( label, list ); list.add( existingValue ); list.add( loc ); } else { ( (List) existingValue ).add( loc ); } } @Override protected AST generateNamedParameter(AST delimiterNode, AST nameNode) throws SemanticException { if ( getSessionFactoryHelper().isStrictJPAQLComplianceEnabled() && positionalParameters != null ) { throw new SemanticException( "Cannot mix positional and named parameters: " + queryTranslatorImpl.getQueryString() ); } final String name = nameNode.getText(); trackNamedParameterPositions( name ); // create the node initially with the param name so that it shows // appropriately in the "original text" attribute final ParameterNode parameter = (ParameterNode) astFactory.create( NAMED_PARAM, name ); parameter.setText( "?" ); final NamedParameterSpecification paramSpec = new NamedParameterSpecification( delimiterNode.getLine(), delimiterNode.getColumn(), name ); parameter.setHqlParameterSpecification( paramSpec ); parameterSpecs.add( paramSpec ); return parameter; } @SuppressWarnings("unchecked") private void trackNamedParameterPositions(String name) { if ( namedParameters == null ) { namedParameters = new HashMap(); } final Integer loc = parameterCount++; final Object existingValue = namedParameters.get( name ); if ( existingValue == null ) { namedParameters.put( name, loc ); } else if ( existingValue instanceof Integer ) { ArrayList list = new ArrayList<>( 4 ); list.add( (Integer) existingValue ); list.add( loc ); namedParameters.put( name, list ); } else { ( (List) existingValue ).add( loc ); } } @Override protected void processConstant(AST constant) throws SemanticException { literalProcessor.processConstant( constant, true ); // Use the delegate, resolve identifiers as FROM element aliases. } @Override protected void processBoolean(AST constant) throws SemanticException { literalProcessor.processBoolean( constant ); // Use the delegate. } @Override protected void processNumericLiteral(AST literal) { literalProcessor.processNumeric( literal ); } @Override protected void processIndex(AST indexOp) throws SemanticException { IndexNode indexNode = (IndexNode) indexOp; indexNode.resolve( true, true ); } @Override protected void processFunction(AST functionCall, boolean inSelect) throws SemanticException { MethodNode methodNode = (MethodNode) functionCall; methodNode.resolve( inSelect ); } @Override protected void processCastFunction(AST castFunctionCall, boolean inSelect) throws SemanticException { CastFunctionNode castFunctionNode = (CastFunctionNode) castFunctionCall; castFunctionNode.resolve( inSelect ); } @Override protected void processAggregation(AST node, boolean inSelect) throws SemanticException { AggregateNode aggregateNode = (AggregateNode) node; aggregateNode.resolve(); } @Override protected void processConstructor(AST constructor) throws SemanticException { ConstructorNode constructorNode = (ConstructorNode) constructor; constructorNode.prepare(); } @Override protected void setAlias(AST selectExpr, AST ident) { ( (SelectExpression) selectExpr ).setAlias( ident.getText() ); // only put the alias (i.e., result variable) in selectExpressionsByResultVariable // if is not defined in a subquery. if ( !isSubQuery() ) { selectExpressionsByResultVariable.put( ident.getText(), (SelectExpression) selectExpr ); } } @Override protected boolean isOrderExpressionResultVariableRef(AST orderExpressionNode) throws SemanticException { // ORDER BY is not supported in a subquery // TODO: should an exception be thrown if an ORDER BY is in a subquery? if ( !isSubQuery() && orderExpressionNode.getType() == IDENT && selectExpressionsByResultVariable.containsKey( orderExpressionNode.getText() ) ) { return true; } return false; } @Override protected void handleResultVariableRef(AST resultVariableRef) throws SemanticException { if ( isSubQuery() ) { throw new SemanticException( "References to result variables in subqueries are not supported." ); } ( (ResultVariableRefNode) resultVariableRef ).setSelectExpression( selectExpressionsByResultVariable.get( resultVariableRef.getText() ) ); } /** * Returns the locations of all occurrences of the named parameter. */ public int[] getNamedParameterLocations(String name) throws QueryException { Object o = namedParameters.get( name ); if ( o == null ) { throw new QueryException( QueryTranslator.ERROR_NAMED_PARAMETER_DOES_NOT_APPEAR + name, queryTranslatorImpl.getQueryString() ); } if ( o instanceof Integer ) { return new int[] {(Integer) o}; } else { return ArrayHelper.toIntArray( (ArrayList) o ); } } public void addQuerySpaces(Serializable[] spaces) { querySpaces.addAll( Arrays.asList( spaces ) ); } public Type[] getReturnTypes() { return selectClause.getQueryReturnTypes(); } public String[] getReturnAliases() { return selectClause.getQueryReturnAliases(); } public SelectClause getSelectClause() { return selectClause; } public FromClause getFinalFromClause() { FromClause top = currentFromClause; while ( top.getParentFromClause() != null ) { top = top.getParentFromClause(); } return top; } public boolean isShallowQuery() { // select clauses for insert statements should alwasy be treated as shallow return getStatementType() == INSERT || queryTranslatorImpl.isShallowQuery(); } public Map getEnabledFilters() { return queryTranslatorImpl.getEnabledFilters(); } public LiteralProcessor getLiteralProcessor() { return literalProcessor; } public ASTPrinter getASTPrinter() { return TokenPrinters.SQL_TOKEN_PRINTER; } public ArrayList getParameterSpecs() { return parameterSpecs; } public int getNumberOfParametersInSetClause() { return numberOfParametersInSetClause; } @Override protected void evaluateAssignment(AST eq) throws SemanticException { prepareLogicOperator( eq ); Queryable persister = getCurrentFromClause().getFromElement().getQueryable(); evaluateAssignment( eq, persister, -1 ); } private void evaluateAssignment(AST eq, Queryable persister, int targetIndex) { if ( persister.isMultiTable() ) { // no need to even collect this information if the persister is considered multi-table AssignmentSpecification specification = new AssignmentSpecification( eq, persister ); if ( targetIndex >= 0 ) { assignmentSpecifications.add( targetIndex, specification ); } else { assignmentSpecifications.add( specification ); } numberOfParametersInSetClause += specification.getParameters().length; } } public ArrayList getAssignmentSpecifications() { return assignmentSpecifications; } @Override protected AST createIntoClause(String path, AST propertySpec) throws SemanticException { Queryable persister = (Queryable) getSessionFactoryHelper().requireClassPersister( path ); IntoClause intoClause = (IntoClause) getASTFactory().create( INTO, persister.getEntityName() ); intoClause.setFirstChild( propertySpec ); intoClause.initialize( persister ); addQuerySpaces( persister.getQuerySpaces() ); return intoClause; } @Override protected void prepareVersioned(AST updateNode, AST versioned) throws SemanticException { UpdateStatement updateStatement = (UpdateStatement) updateNode; FromClause fromClause = updateStatement.getFromClause(); if ( versioned != null ) { // Make sure that the persister is versioned Queryable persister = fromClause.getFromElement().getQueryable(); if ( !persister.isVersioned() ) { throw new SemanticException( "increment option specified for update of non-versioned entity" ); } VersionType versionType = persister.getVersionType(); if ( versionType instanceof UserVersionType ) { throw new SemanticException( "user-defined version types not supported for increment option" ); } AST eq = getASTFactory().create( HqlSqlTokenTypes.EQ, "=" ); AST versionPropertyNode = generateVersionPropertyNode( persister ); eq.setFirstChild( versionPropertyNode ); AST versionIncrementNode = null; if ( isTimestampBasedVersion( versionType ) ) { versionIncrementNode = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" ); ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType ); ( (ParameterNode) versionIncrementNode ).setHqlParameterSpecification( paramSpec ); parameterSpecs.add( 0, paramSpec ); } else { // Not possible to simply re-use the versionPropertyNode here as it causes // OOM errors due to circularity :( versionIncrementNode = getASTFactory().create( HqlSqlTokenTypes.PLUS, "+" ); versionIncrementNode.setFirstChild( generateVersionPropertyNode( persister ) ); versionIncrementNode.addChild( getASTFactory().create( HqlSqlTokenTypes.IDENT, "1" ) ); } eq.addChild( versionIncrementNode ); evaluateAssignment( eq, persister, 0 ); AST setClause = updateStatement.getSetClause(); AST currentFirstSetElement = setClause.getFirstChild(); setClause.setFirstChild( eq ); eq.setNextSibling( currentFirstSetElement ); } } private boolean isTimestampBasedVersion(VersionType versionType) { final Class javaType = versionType.getReturnedClass(); return Date.class.isAssignableFrom( javaType ) || Calendar.class.isAssignableFrom( javaType ); } private AST generateVersionPropertyNode(Queryable persister) throws SemanticException { String versionPropertyName = persister.getPropertyNames()[persister.getVersionProperty()]; AST versionPropertyRef = getASTFactory().create( HqlSqlTokenTypes.IDENT, versionPropertyName ); AST versionPropertyNode = lookupNonQualifiedProperty( versionPropertyRef ); resolve( versionPropertyNode ); return versionPropertyNode; } @Override protected void prepareLogicOperator(AST operator) throws SemanticException { ( (OperatorNode) operator ).initialize(); } @Override protected void prepareArithmeticOperator(AST operator) throws SemanticException { ( (OperatorNode) operator ).initialize(); } @Override protected void validateMapPropertyExpression(AST node) throws SemanticException { try { FromReferenceNode fromReferenceNode = (FromReferenceNode) node; QueryableCollection collectionPersister = fromReferenceNode.getFromElement().getQueryableCollection(); if ( !Map.class.isAssignableFrom( collectionPersister.getCollectionType().getReturnedClass() ) ) { throw new SemanticException( "node did not reference a map" ); } } catch (SemanticException se) { throw se; } catch (Throwable t) { throw new SemanticException( "node did not reference a map" ); } } public Set getTreatAsDeclarationsByPath(String path) { return hqlParser.getTreatMap().get( path ); } public Dialect getDialect() { return sessionFactoryHelper.getFactory().getServiceRegistry().getService( JdbcServices.class ).getDialect(); } public static void panic() { throw new QueryException( "TreeWalker: panic" ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy