/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.query.resolver.command; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.TreeSet; import org.teiid.api.exception.query.QueryResolverException; import org.teiid.api.exception.query.UnresolvedSymbolDescription; import org.teiid.core.types.DataTypeManagerService; import org.teiid.designer.query.metadata.IQueryMetadataInterface; import org.teiid.designer.query.metadata.IQueryMetadataInterface.SupportConstants; import org.teiid.designer.query.metadata.IStoredProcedureInfo; import org.teiid.designer.query.sql.lang.IJoinType.Types; import org.teiid.designer.query.sql.lang.ISPParameter; import org.teiid.designer.query.sql.lang.ISetQuery.Operation; import org.teiid.designer.runtime.version.spi.TeiidServerVersion.Version; import org.teiid.query.metadata.TempMetadataAdapter; import org.teiid.query.metadata.TempMetadataID; import org.teiid.query.parser.TeiidNodeFactory.ASTNodes; import org.teiid.query.resolver.CommandResolver; import org.teiid.query.resolver.QueryResolver; import org.teiid.query.resolver.util.ResolverUtil; import org.teiid.query.resolver.util.ResolverVisitor; import org.teiid.query.sql.lang.ArrayTable; import org.teiid.query.sql.lang.Command; import org.teiid.query.sql.lang.ExistsCriteria; import org.teiid.query.sql.lang.From; import org.teiid.query.sql.lang.FromClause; import org.teiid.query.sql.lang.Into; import org.teiid.query.sql.lang.JoinPredicate; import org.teiid.query.sql.lang.LanguageObject; import org.teiid.query.sql.lang.Limit; import org.teiid.query.sql.lang.ObjectColumn; import org.teiid.query.sql.lang.ObjectTable; import org.teiid.query.sql.lang.Query; import org.teiid.query.sql.lang.QueryCommand; import org.teiid.query.sql.lang.SPParameter; import org.teiid.query.sql.lang.Select; import org.teiid.query.sql.lang.SetQuery; import org.teiid.query.sql.lang.StoredProcedure; import org.teiid.query.sql.lang.SubqueryCompareCriteria; import org.teiid.query.sql.lang.SubqueryContainer; import org.teiid.query.sql.lang.SubqueryFromClause; import org.teiid.query.sql.lang.SubquerySetCriteria; import org.teiid.query.sql.lang.TableFunctionReference; import org.teiid.query.sql.lang.TextColumn; import org.teiid.query.sql.lang.TextTable; import org.teiid.query.sql.lang.UnaryFromClause; import org.teiid.query.sql.lang.WithQueryCommand; import org.teiid.query.sql.lang.XMLColumn; import org.teiid.query.sql.lang.XMLTable; import org.teiid.query.sql.navigator.PostOrderNavigator; import org.teiid.query.sql.navigator.PreOrPostOrderNavigator; import org.teiid.query.sql.symbol.AggregateSymbol; import org.teiid.query.sql.symbol.AliasSymbol; import org.teiid.query.sql.symbol.ElementSymbol; import org.teiid.query.sql.symbol.Expression; import org.teiid.query.sql.symbol.ExpressionSymbol; import org.teiid.query.sql.symbol.Function; import org.teiid.query.sql.symbol.GroupSymbol; import org.teiid.query.sql.symbol.MultipleElementSymbol; import org.teiid.query.sql.symbol.Reference; import org.teiid.query.sql.symbol.ScalarSubquery; import org.teiid.query.sql.symbol.Symbol; import org.teiid.query.sql.visitor.ElementCollectorVisitor; import org.teiid.query.sql.visitor.ExpressionMappingVisitor; import org.teiid.runtime.client.Messages; /** * */ public class SimpleQueryResolver extends CommandResolver { /** * @param queryResolver */ public SimpleQueryResolver(QueryResolver queryResolver) { super(queryResolver); } /** * @see org.teiid.query.resolver.CommandResolver#resolveCommand(org.teiid.query.sql.lang.Command, org.teiid.query.metadata.TempMetadataAdapter, boolean) */ @Override public void resolveCommand(Command command, TempMetadataAdapter metadata, boolean resolveNullLiterals) throws Exception { Query query = (Query) command; resolveWith(metadata, query); try { QueryResolverVisitor qrv = new QueryResolverVisitor(query, metadata); qrv.visit(query); ResolverVisitor visitor = (ResolverVisitor)qrv.getVisitor(); visitor.throwException(true); if (visitor.hasUserDefinedAggregate() ) { ExpressionMappingVisitor emv = new ExpressionMappingVisitor(getTeiidVersion(), null) { @Override public Expression replaceExpression(Expression element) { if (element instanceof Function && !(element instanceof AggregateSymbol) && ((Function) element).isAggregate()) { Function f = (Function)element; AggregateSymbol as = createASTNode(ASTNodes.AGGREGATE_SYMBOL); as.setName(f.getName()); as.setDistinct(false); as.setArgs(f.getArgs()); as.setType(f.getType()); as.setFunctionDescriptor(f.getFunctionDescriptor()); return as; } return element; } }; PreOrPostOrderNavigator.doVisit(query, emv, PreOrPostOrderNavigator.POST_ORDER); } } catch (Exception e) { if (e.getCause() instanceof Exception) { throw (Exception)e.getCause(); } throw e; } if (query.getLimit() != null) { ResolverUtil.resolveLimit(query.getLimit()); } if (query.getOrderBy() != null) { ResolverUtil.resolveOrderBy(query.getOrderBy(), query, metadata); } List<Expression> symbols = query.getSelect().getProjectedSymbols(); if (query.getInto() != null) { GroupSymbol symbol = query.getInto().getGroup(); ResolverUtil.resolveImplicitTempGroup(metadata, symbol, symbols); } else if (resolveNullLiterals) { ResolverUtil.resolveNullLiterals(symbols); } } /** * @param metadata * @param query * @throws Exception */ public void resolveWith(TempMetadataAdapter metadata, QueryCommand query) throws Exception { if (query.getWith() == null) { return; } LinkedHashSet<GroupSymbol> discoveredGroups = new LinkedHashSet<GroupSymbol>(); for (WithQueryCommand obj : query.getWith()) { QueryCommand queryExpression = obj.getCommand(); getQueryResolver().setChildMetadata(queryExpression, query); QueryCommand recursive = null; try { getQueryResolver().resolveCommand(queryExpression, metadata.getMetadata(), false); } catch (QueryResolverException e) { if (!(queryExpression instanceof SetQuery)) { throw e; } SetQuery setQuery = (SetQuery)queryExpression; //valid form must be a union with nothing above if (setQuery.getOperation() != Operation.UNION || setQuery.getLimit() != null || setQuery.getOrderBy() != null || setQuery.getOption() != null) { throw e; } getQueryResolver().resolveCommand(setQuery.getLeftQuery(), metadata.getMetadata(), false); recursive = setQuery.getRightQuery(); } if (!discoveredGroups.add(obj.getGroupSymbol())) { throw new QueryResolverException(Messages.gs(Messages.TEIID.TEIID30101, obj.getGroupSymbol())); } List<? extends Expression> projectedSymbols = obj.getCommand().getProjectedSymbols(); if (obj.getColumns() != null && !obj.getColumns().isEmpty()) { if (obj.getColumns().size() != projectedSymbols.size()) { throw new QueryResolverException(Messages.gs(Messages.TEIID.TEIID30102, obj.getGroupSymbol())); } Iterator<ElementSymbol> iter = obj.getColumns().iterator(); for (Expression singleElementSymbol : projectedSymbols) { ElementSymbol es = iter.next(); es.setType(singleElementSymbol.getType()); } projectedSymbols = obj.getColumns(); } TempMetadataID id = ResolverUtil.addTempGroup(metadata, obj.getGroupSymbol(), projectedSymbols, true); obj.getGroupSymbol().setMetadataID(metadata.getMetadataStore().getTempGroupID(obj.getGroupSymbol().getName())); obj.getGroupSymbol().setIsTempTable(true); List<GroupSymbol> groups = Collections.singletonList(obj.getGroupSymbol()); ResolverVisitor visitor = new ResolverVisitor(obj.getTeiidVersion()); if (obj.getColumns() != null && !obj.getColumns().isEmpty()) { for (Expression singleElementSymbol : projectedSymbols) { visitor.resolveLanguageObject(singleElementSymbol, groups, metadata); } } if (obj.getColumns() != null && !obj.getColumns().isEmpty()) { Iterator<ElementSymbol> iter = obj.getColumns().iterator(); for (TempMetadataID colid : id.getElements()) { ElementSymbol es = iter.next(); es.setMetadataID(colid); es.setGroupSymbol(obj.getGroupSymbol()); } } if (recursive != null) { // Only be not null is version > 10 getQueryResolver().setChildMetadata(recursive, query); getQueryResolver().resolveCommand(recursive, metadata.getMetadata(), false); new SetQueryResolver(getQueryResolver()).resolveSetQuery(metadata, false, (SetQuery)queryExpression, ((SetQuery)queryExpression).getLeftQuery(), recursive); obj.setRecursive(true); } } } private GroupSymbol resolveAllInGroup(MultipleElementSymbol allInGroupSymbol, Set<GroupSymbol> groups, IQueryMetadataInterface metadata) throws Exception { String groupAlias = allInGroupSymbol.getGroup().getName(); List<GroupSymbol> groupSymbols = ResolverUtil.findMatchingGroups(groupAlias, groups, metadata); if(groupSymbols.isEmpty() || groupSymbols.size() > 1) { String msg = Messages.getString(groupSymbols.isEmpty() ? Messages.ERR.ERR_015_008_0047 : Messages.QueryResolver.ambiguous_all_in_group, allInGroupSymbol); QueryResolverException qre = new QueryResolverException(msg); qre.addUnresolvedSymbol(new UnresolvedSymbolDescription(allInGroupSymbol.toString(), msg)); throw qre; } GroupSymbol gs = allInGroupSymbol.getGroup(); allInGroupSymbol.setGroup(groupSymbols.get(0).clone()); return groupSymbols.get(0); } /** * */ public class QueryResolverVisitor extends PostOrderNavigator { private LinkedHashSet<GroupSymbol> currentGroups = new LinkedHashSet<GroupSymbol>(); private LinkedList<GroupSymbol> discoveredGroups = new LinkedList<GroupSymbol>(); private List<GroupSymbol> implicitGroups = new LinkedList<GroupSymbol>(); private TempMetadataAdapter metadata; private Query query; private boolean allowImplicit = true; /** * @param query * @param metadata */ public QueryResolverVisitor(Query query, TempMetadataAdapter metadata) { super(new ResolverVisitor(query.getTeiidVersion(), metadata, null, query.getExternalGroupContexts())); ResolverVisitor visitor = (ResolverVisitor)getVisitor(); visitor.setGroups(currentGroups); this.query = query; this.metadata = metadata; } @Override protected void postVisitVisitor(LanguageObject obj) { super.postVisitVisitor(obj); ResolverVisitor visitor = (ResolverVisitor)getVisitor(); try { visitor.throwException(false); } catch (Exception e) { throw new RuntimeException(e); } } /** * Resolving a Query requires a special ordering */ @Override public void visit(Query obj) { visitNode(obj.getInto()); visitNode(obj.getFrom()); visitNode(obj.getCriteria()); visitNode(obj.getGroupBy()); visitNode(obj.getHaving()); visitNode(obj.getSelect()); /* * TODO * Session Variables are not currently supported in Designer */ // if (getTeiidVersion().isGreaterThanOrEqualTo(Version.TEIID_8_11)) { // GroupBy groupBy = obj.getGroupBy(); // if (groupBy != null) { // Object var = DQPWorkContext.getWorkContext().getSession().getSessionVariables().get("resolve_groupby_positional"); //$NON-NLS-1$ // if (Boolean.TRUE.equals(var)) { // for (int i = 0; i < groupBy.getCount(); i++) { // List<Expression> select = obj.getSelect().getProjectedSymbols(); // Expression ex = groupBy.getSymbols().get(i); // ex = SymbolMap.getExpression(ex); // if (ex instanceof Constant && ex.getType() == DataTypeManager.DefaultDataClasses.INTEGER) { // Integer val = (Integer)((Constant)ex).getValue(); // if (val != null && val > 0 && val <= select.size()) { // Expression selectExpression = select.get(val - 1); // selectExpression = SymbolMap.getExpression(selectExpression); // groupBy.getSymbols().set(i, selectExpression.clone()); // } // } // } // } // } // } visitNode(obj.getLimit()); } @Override public void visit(GroupSymbol obj) { try { ResolverUtil.resolveGroup(obj, metadata); } catch (Exception err) { throw new RuntimeException(err); } } private void resolveSubQuery(SubqueryContainer<?> obj, Collection<GroupSymbol> externalGroups) { Command command = obj.getCommand(); getQueryResolver().setChildMetadata(command, query); command.pushNewResolvingContext(externalGroups); try { getQueryResolver().resolveCommand(command, metadata.getMetadata(), false); } catch (Exception err) { throw new RuntimeException(err); } } @Override public void visit(MultipleElementSymbol obj) { // Determine group that this symbol is for try { List<ElementSymbol> elementSymbols = new ArrayList<ElementSymbol>(); Collection<GroupSymbol> groups = currentGroups; if (obj.getGroup() != null) { groups = Arrays.asList(resolveAllInGroup(obj, currentGroups, metadata)); } for (GroupSymbol group : groups) { elementSymbols.addAll(resolveSelectableElements(group)); } obj.setElementSymbols(elementSymbols); } catch (Exception err) { throw new RuntimeException(err); } } private List<ElementSymbol> resolveSelectableElements(GroupSymbol group) throws Exception { List<ElementSymbol> elements = ResolverUtil.resolveElementsInGroup(group, metadata); List<ElementSymbol> result = new ArrayList<ElementSymbol>(elements.size()); // Look for elements that are not selectable and remove them for (ElementSymbol element : elements) { if(metadata.elementSupports(element.getMetadataID(), SupportConstants.Element.SELECT) && !metadata.isPseudo(element.getMetadataID())) { element = element.clone(); element.setGroupSymbol(group); result.add(element); } } return result; } @Override public void visit(ScalarSubquery obj) { resolveSubQuery(obj, this.currentGroups); } @Override public void visit(ExistsCriteria obj) { resolveSubQuery(obj, this.currentGroups); } @Override public void visit(SubqueryCompareCriteria obj) { visitNode(obj.getLeftExpression()); resolveSubQuery(obj, this.currentGroups); postVisitVisitor(obj); } @Override public void visit(SubquerySetCriteria obj) { visitNode(obj.getExpression()); resolveSubQuery(obj, this.currentGroups); postVisitVisitor(obj); } @Override public void visit(TextTable obj) { LinkedHashSet<GroupSymbol> saved = preTableFunctionReference(obj); this.visitNode(obj.getFile()); try { obj.setFile(ResolverUtil.convertExpression(obj.getFile(), DataTypeManagerService.DefaultDataTypes.CLOB.getId(), metadata)); } catch (Exception e) { throw new RuntimeException(e); } postTableFunctionReference(obj, saved); //set to fixed width if any column has width specified for (TextColumn col : obj.getColumns()) { if (col.getWidth() != null) { obj.setFixedWidth(true); break; } } } @Override public void visit(ArrayTable obj) { LinkedHashSet<GroupSymbol> saved = preTableFunctionReference(obj); visitNode(obj.getArrayValue()); postTableFunctionReference(obj, saved); } @Override public void visit(XMLTable obj) { LinkedHashSet<GroupSymbol> saved = preTableFunctionReference(obj); visitNodes(obj.getPassing()); postTableFunctionReference(obj, saved); try { ResolverUtil.setDesiredType(obj.getPassing(), obj); obj.compileXqueryExpression(); for (XMLColumn column : obj.getColumns()) { if (column.getDefaultExpression() == null) { continue; } visitNode(column.getDefaultExpression()); Expression ex = ResolverUtil.convertExpression(column.getDefaultExpression(), getDataTypeManager().getDataTypeName(column.getSymbol().getType()), metadata); column.setDefaultExpression(ex); } } catch (Exception e) { throw new RuntimeException(e); } } @Override public void visit(ObjectTable obj) { LinkedHashSet<GroupSymbol> saved = preTableFunctionReference(obj); visitNodes(obj.getPassing()); postTableFunctionReference(obj, saved); try { ResolverUtil.setDesiredType(obj.getPassing(), obj, DataTypeManagerService.DefaultDataTypes.OBJECT.getTypeClass()); for (ObjectColumn column : obj.getColumns()) { if (column.getDefaultExpression() == null) { continue; } visitNode(column.getDefaultExpression()); Expression ex = ResolverUtil.convertExpression(column.getDefaultExpression(), getDataTypeManager().getDataTypeName(column.getSymbol().getType()), metadata); column.setDefaultExpression(ex); } } catch (Exception e) { throw new RuntimeException(e); } } /** * @param tfr * @return set of group symbols */ public LinkedHashSet<GroupSymbol> preTableFunctionReference(TableFunctionReference tfr) { LinkedHashSet<GroupSymbol> saved = new LinkedHashSet<GroupSymbol>(this.currentGroups); if (allowImplicit) { currentGroups.addAll(this.implicitGroups); } return saved; } /** * @param obj * @param saved */ public void postTableFunctionReference(TableFunctionReference obj, LinkedHashSet<GroupSymbol> saved) { //we didn't create a true external context, so we manually mark external for (ElementSymbol symbol : ElementCollectorVisitor.getElements(obj, false)) { if (symbol.isExternalReference()) { continue; } if (implicitGroups.contains(symbol.getGroupSymbol())) { symbol.setIsExternalReference(true); } } if (allowImplicit) { this.currentGroups.clear(); this.currentGroups.addAll(saved); } discoveredGroup(obj.getGroupSymbol()); try { ResolverUtil.addTempGroup(metadata, obj.getGroupSymbol(), obj.getProjectedSymbols(), false); } catch (Exception err) { throw new RuntimeException(err); } obj.getGroupSymbol().setMetadataID(metadata.getMetadataStore().getTempGroupID(obj.getGroupSymbol().getName())); //now resolve the projected symbols Set<GroupSymbol> groups = new HashSet<GroupSymbol>(); groups.add(obj.getGroupSymbol()); ResolverVisitor visitor = new ResolverVisitor(obj.getTeiidVersion()); for (ElementSymbol symbol : obj.getProjectedSymbols()) { try { visitor.resolveLanguageObject(symbol, groups, null, metadata); } catch (Exception e) { throw new RuntimeException(e); } } } @Override public void visit(SubqueryFromClause obj) { Collection<GroupSymbol> externalGroups = this.currentGroups; if (obj.isLateral() && allowImplicit) { externalGroups = new ArrayList<GroupSymbol>(externalGroups); externalGroups.addAll(this.implicitGroups); } resolveSubQuery(obj, externalGroups); discoveredGroup(obj.getGroupSymbol()); try { ResolverUtil.addTempGroup(metadata, obj.getGroupSymbol(), obj.getCommand().getProjectedSymbols(), false); } catch (Exception err) { throw new RuntimeException(err); } obj.getGroupSymbol().setMetadataID(metadata.getMetadataStore().getTempGroupID(obj.getGroupSymbol().getName())); } @Override public void visit(UnaryFromClause obj) { GroupSymbol group = obj.getGroup(); visitNode(group); try { if (!group.isProcedure() && metadata.isXMLGroup(group.getMetadataID())) { throw new QueryResolverException(Messages.gs(Messages.TEIID.TEIID30112)); } discoveredGroup(group); if (group.isProcedure()) { createProcRelational(obj); } } catch(Exception e) { throw new RuntimeException(e); } } private void discoveredGroup(GroupSymbol group) { discoveredGroups.add(group); if (allowImplicit) { implicitGroups.add(group); } } private void createProcRelational(UnaryFromClause obj) throws Exception { GroupSymbol group = obj.getGroup(); String fullName = metadata.getFullName(group.getMetadataID()); String queryName = group.getName(); IStoredProcedureInfo storedProcedureInfo = metadata.getStoredProcedureInfoForProcedure(fullName); StoredProcedure storedProcedureCommand = createASTNode(ASTNodes.STORED_PROCEDURE); storedProcedureCommand.setProcedureRelational(true); storedProcedureCommand.setProcedureName(fullName); List<SPParameter> metadataParams = storedProcedureInfo.getParameters(); Query procQuery = createASTNode(ASTNodes.QUERY); From from = createASTNode(ASTNodes.FROM); SubqueryFromClause subqueryFromClause = createASTNode(ASTNodes.SUBQUERY_FROM_CLAUSE); subqueryFromClause.setName("X"); //$NON-NLS-1$ subqueryFromClause.setCommand(storedProcedureCommand); from.addClause(subqueryFromClause); procQuery.setFrom(from); Select select = createASTNode(ASTNodes.SELECT); MultipleElementSymbol mes = createASTNode(ASTNodes.MULTIPLE_ELEMENT_SYMBOL); mes.setName("X"); //$NON-NLS-1$ select.addSymbol(mes); procQuery.setSelect(select); List<String> accessPatternElementNames = new LinkedList<String>(); int paramIndex = 1; for (SPParameter metadataParameter : metadataParams) { SPParameter clonedParam = metadataParameter.clone(); if (clonedParam.getParameterType()==ISPParameter.ParameterInfo.IN.index() || metadataParameter.getParameterType()==ISPParameter.ParameterInfo.INOUT.index()) { ElementSymbol paramSymbol = clonedParam.getParameterSymbol(); Reference ref = createASTNode(ASTNodes.REFERENCE); ref.setExpression(paramSymbol); clonedParam.setExpression(ref); clonedParam.setIndex(paramIndex++); storedProcedureCommand.setParameter(clonedParam); String aliasName = paramSymbol.getShortName(); if (metadataParameter.getParameterType()==ISPParameter.ParameterInfo.INOUT.index()) { aliasName += "_IN"; //$NON-NLS-1$ } ExpressionSymbol es = createASTNode(ASTNodes.EXPRESSION_SYMBOL); es.setName(paramSymbol.getShortName()); es.setExpression(ref); AliasSymbol newSymbol = createASTNode(ASTNodes.ALIAS_SYMBOL); newSymbol.setName(aliasName); newSymbol.setSymbol(es); select.addSymbol(newSymbol); accessPatternElementNames.add(queryName + Symbol.SEPARATOR + aliasName); } } getQueryResolver().resolveCommand(procQuery, metadata.getMetadata()); List<Expression> projectedSymbols = procQuery.getProjectedSymbols(); Set<String> foundNames = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER); for (Expression ses : projectedSymbols) { if (!foundNames.add(Symbol.getShortName(ses))) { throw new QueryResolverException(Messages.gs(Messages.TEIID.TEIID30114, fullName)); } } TempMetadataID id = metadata.getMetadataStore().getTempGroupID(queryName); if (id == null) { metadata.getMetadataStore().addTempGroup(queryName, projectedSymbols, true); id = metadata.getMetadataStore().getTempGroupID(queryName); id.setOriginalMetadataID(storedProcedureCommand.getProcedureID()); if (!accessPatternElementNames.isEmpty()) { List<TempMetadataID> accessPatternIds = new LinkedList<TempMetadataID>(); for (String name : accessPatternElementNames) { accessPatternIds.add(metadata.getMetadataStore().getTempElementID(name)); } id.setAccessPatterns(Arrays.asList(new TempMetadataID("procedure access pattern", accessPatternIds))); //$NON-NLS-1$ } } group.setMetadataID(id); obj.setExpandedCommand(procQuery); } /** * @see org.teiid.query.sql.navigator.PreOrPostOrderNavigator#visit(org.teiid.query.sql.lang.Into) */ @Override public void visit(Into obj) { if (!obj.getGroup().isImplicitTempGroupSymbol()) { super.visit(obj); } } @Override public void visit(JoinPredicate obj) { assert currentGroups.isEmpty(); List<GroupSymbol> tempImplicitGroups = new ArrayList<GroupSymbol>(discoveredGroups); discoveredGroups.clear(); visitNode(obj.getLeftClause()); List<GroupSymbol> leftGroups = new ArrayList<GroupSymbol>(discoveredGroups); discoveredGroups.clear(); visitNode(obj.getRightClause()); discoveredGroups.addAll(leftGroups); addDiscoveredGroups(); visitNodes(obj.getJoinCriteria()); discoveredGroups.addAll(currentGroups); currentGroups.clear(); discoveredGroups.addAll(tempImplicitGroups); } private void addDiscoveredGroups() { for (GroupSymbol group : discoveredGroups) { if (!this.currentGroups.add(group)) { String msg = Messages.getString(Messages.ERR.ERR_015_008_0046, group.getName()); QueryResolverException qre = new QueryResolverException(msg); qre.addUnresolvedSymbol(new UnresolvedSymbolDescription(group.toString(), msg)); throw new RuntimeException(qre); } } discoveredGroups.clear(); } @Override public void visit(From obj) { assert currentGroups.isEmpty(); for (FromClause clause : obj.getClauses()) { checkImplicit(clause); } super.visit(obj); addDiscoveredGroups(); } private void checkImplicit(FromClause clause) { if (clause instanceof JoinPredicate) { JoinPredicate jp = (JoinPredicate)clause; if (Types.JOIN_FULL_OUTER.equals(jp.getJoinType().getKind()) || Types.JOIN_RIGHT_OUTER.equals(jp.getJoinType().getKind())) { allowImplicit = false; return; } checkImplicit(jp.getLeftClause()); if (allowImplicit) { checkImplicit(jp.getRightClause()); } } } @Override public void visit(Limit obj) { super.visit(obj); if (obj.getOffset() != null) { ResolverUtil.setTypeIfNull(obj.getOffset(), DataTypeManagerService.DefaultDataTypes.INTEGER.getTypeClass()); try { obj.setOffset(ResolverUtil.convertExpression(obj.getOffset(), DataTypeManagerService.DefaultDataTypes.INTEGER.getId(), metadata)); } catch (Exception e) { throw new RuntimeException(e); } } if (obj.getRowLimit() != null) { ResolverUtil.setTypeIfNull(obj.getRowLimit(), DataTypeManagerService.DefaultDataTypes.INTEGER.getTypeClass()); try { obj.setRowLimit(ResolverUtil.convertExpression(obj.getRowLimit(), DataTypeManagerService.DefaultDataTypes.INTEGER.getId(), metadata)); } catch (Exception e) { throw new RuntimeException(e); } } } } }