/*
* 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.validator;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.script.Compilable;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import org.teiid.api.exception.query.QueryValidatorException;
import org.teiid.core.types.ArrayImpl;
import org.teiid.core.types.DataTypeManagerService;
import org.teiid.core.types.DataTypeManagerService.DefaultDataTypes;
import org.teiid.designer.annotation.Removed;
import org.teiid.designer.query.metadata.IQueryMetadataInterface;
import org.teiid.designer.query.metadata.IQueryMetadataInterface.SupportConstants;
import org.teiid.designer.query.metadata.IQueryNode;
import org.teiid.designer.query.metadata.IStoredProcedureInfo;
import org.teiid.designer.query.sql.lang.ICommand;
import org.teiid.designer.query.sql.lang.ISPParameter;
import org.teiid.designer.query.sql.lang.ISetQuery.Operation;
import org.teiid.designer.query.sql.proc.ICreateProcedureCommand;
import org.teiid.designer.query.sql.symbol.IAggregateSymbol;
import org.teiid.designer.runtime.version.spi.ITeiidServerVersion;
import org.teiid.designer.runtime.version.spi.TeiidServerVersion.Version;
import org.teiid.designer.udf.IFunctionLibrary;
import org.teiid.language.SQLConstants;
import org.teiid.metadata.AggregateAttributes;
import org.teiid.metadata.Table;
import org.teiid.query.eval.Evaluator;
import org.teiid.query.function.FunctionMethods;
import org.teiid.query.function.source.XMLSystemFunctions;
import org.teiid.query.parser.LanguageVisitor;
import org.teiid.query.parser.TeiidNodeFactory.ASTNodes;
import org.teiid.query.resolver.ProcedureContainerResolver;
import org.teiid.query.resolver.QueryResolver;
import org.teiid.query.resolver.util.ResolverUtil;
import org.teiid.query.sql.ProcedureReservedWords;
import org.teiid.query.sql.lang.Alter;
import org.teiid.query.sql.lang.AlterProcedure;
import org.teiid.query.sql.lang.AlterTrigger;
import org.teiid.query.sql.lang.AlterView;
import org.teiid.query.sql.lang.BetweenCriteria;
import org.teiid.query.sql.lang.Command;
import org.teiid.query.sql.lang.CompareCriteria;
import org.teiid.query.sql.lang.CompoundCriteria;
import org.teiid.query.sql.lang.Create;
import org.teiid.query.sql.lang.Criteria;
import org.teiid.query.sql.lang.CriteriaOperator.Operator;
import org.teiid.query.sql.lang.Delete;
import org.teiid.query.sql.lang.Drop;
import org.teiid.query.sql.lang.DynamicCommand;
import org.teiid.query.sql.lang.ExistsCriteria;
import org.teiid.query.sql.lang.GroupBy;
import org.teiid.query.sql.lang.HasCriteria;
import org.teiid.query.sql.lang.Insert;
import org.teiid.query.sql.lang.Into;
import org.teiid.query.sql.lang.IsDistinctCriteria;
import org.teiid.query.sql.lang.IsNullCriteria;
import org.teiid.query.sql.lang.Labeled;
import org.teiid.query.sql.lang.LanguageObject;
import org.teiid.query.sql.lang.Limit;
import org.teiid.query.sql.lang.MatchCriteria;
import org.teiid.query.sql.lang.NamespaceItem;
import org.teiid.query.sql.lang.NotCriteria;
import org.teiid.query.sql.lang.ObjectColumn;
import org.teiid.query.sql.lang.ObjectTable;
import org.teiid.query.sql.lang.Option;
import org.teiid.query.sql.lang.OrderBy;
import org.teiid.query.sql.lang.OrderByItem;
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.SetClause;
import org.teiid.query.sql.lang.SetClauseList;
import org.teiid.query.sql.lang.SetCriteria;
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.TargetedCommand;
import org.teiid.query.sql.lang.TextColumn;
import org.teiid.query.sql.lang.TextTable;
import org.teiid.query.sql.lang.TranslateCriteria;
import org.teiid.query.sql.lang.Update;
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.PreOrderNavigator;
import org.teiid.query.sql.proc.AssignmentStatement;
import org.teiid.query.sql.proc.Block;
import org.teiid.query.sql.proc.BranchingStatement;
import org.teiid.query.sql.proc.BranchingStatement.BranchingMode;
import org.teiid.query.sql.proc.CommandStatement;
import org.teiid.query.sql.proc.CreateProcedureCommand;
import org.teiid.query.sql.proc.CreateUpdateProcedureCommand;
import org.teiid.query.sql.proc.LoopStatement;
import org.teiid.query.sql.proc.WhileStatement;
import org.teiid.query.sql.symbol.AggregateSymbol;
import org.teiid.query.sql.symbol.Constant;
import org.teiid.query.sql.symbol.DerivedColumn;
import org.teiid.query.sql.symbol.ElementSymbol;
import org.teiid.query.sql.symbol.Expression;
import org.teiid.query.sql.symbol.Function;
import org.teiid.query.sql.symbol.GroupSymbol;
import org.teiid.query.sql.symbol.JSONObject;
import org.teiid.query.sql.symbol.QueryString;
import org.teiid.query.sql.symbol.Reference;
import org.teiid.query.sql.symbol.Reference.Constraint;
import org.teiid.query.sql.symbol.ScalarSubquery;
import org.teiid.query.sql.symbol.TextLine;
import org.teiid.query.sql.symbol.WindowFunction;
import org.teiid.query.sql.symbol.XMLAttributes;
import org.teiid.query.sql.symbol.XMLCast;
import org.teiid.query.sql.symbol.XMLElement;
import org.teiid.query.sql.symbol.XMLExists;
import org.teiid.query.sql.symbol.XMLForest;
import org.teiid.query.sql.symbol.XMLNamespaces;
import org.teiid.query.sql.symbol.XMLParse;
import org.teiid.query.sql.symbol.XMLQuery;
import org.teiid.query.sql.symbol.XMLSerialize;
import org.teiid.query.sql.visitor.AggregateSymbolCollectorVisitor;
import org.teiid.query.sql.visitor.AggregateSymbolCollectorVisitor.AggregateStopNavigator;
import org.teiid.query.sql.visitor.ElementCollectorVisitor;
import org.teiid.query.sql.visitor.EvaluatableVisitor;
import org.teiid.query.sql.visitor.FunctionCollectorVisitor;
import org.teiid.query.sql.visitor.GroupCollectorVisitor;
import org.teiid.query.sql.visitor.GroupsUsedByElementsVisitor;
import org.teiid.query.sql.visitor.SQLStringVisitor;
import org.teiid.query.sql.visitor.ValueIteratorProviderCollectorVisitor;
import org.teiid.query.validator.UpdateValidator.UpdateInfo;
import org.teiid.query.xquery.saxon.SaxonXQueryExpression;
import org.teiid.runtime.client.Messages;
import org.teiid.runtime.client.TeiidClientException;
import org.teiid.translator.SourceSystemFunctions;
import net.sf.saxon.om.Name11Checker;
import net.sf.saxon.om.QNameException;
/**
*
*/
public class ValidationVisitor extends AbstractValidationVisitor {
private static final class PositiveIntegerConstraint implements Reference.Constraint {
private Messages.ValidationVisitor msgKey;
public PositiveIntegerConstraint(Messages.ValidationVisitor enumKey) {
this.msgKey = enumKey;
}
@Override
public void validate(Object value) throws Exception {
if (value == null || ((Integer)value).intValue() < 0) {
throw new TeiidClientException(Messages.getString(msgKey));
}
}
}
public static final Constraint LIMIT_CONSTRAINT = new PositiveIntegerConstraint(Messages.ValidationVisitor.badlimit2);
// State during validation
private boolean isXML = false; // only used for Query commands
private boolean inQuery;
// update procedure being validated
private ICreateProcedureCommand<Block, GroupSymbol, Expression, LanguageVisitor> createProc;
private final DataTypeManagerService dataTypeManager;
/**
* @param teiidVersion
*/
public ValidationVisitor(ITeiidServerVersion teiidVersion) {
super(teiidVersion);
dataTypeManager = DataTypeManagerService.getInstance(teiidVersion);
}
@Override
public void reset() {
super.reset();
this.isXML = false;
this.inQuery = false;
this.createProc = null;
}
// ############### Visitor methods for language objects ##################
// public void visit(BatchedUpdateCommand obj) {
// List<Command> commands = obj.getUpdateCommands();
// Command command = null;
// int type = 0;
// for (int i = 0; i < commands.size(); i++) {
// command = commands.get(i);
// type = command.getType();
// if (type != Command.TYPE_INSERT &&
// type != Command.TYPE_UPDATE &&
// type != Command.TYPE_DELETE &&
// type != Command.TYPE_QUERY) { // SELECT INTO command
// handleValidationError(Messages.getString(Messages.ValidationVisitor.invalid_batch_command),command);
// } else if (type == Command.TYPE_QUERY) {
// Into into = ((Query)command).getInto();
// if (into == null) {
// handleValidationError(Messages.getString(Messages.ValidationVisitor.invalid_batch_command),command);
// }
// }
// }
// }
@Override
public void visit(Delete obj) {
validateNoXMLUpdates(obj);
GroupSymbol group = obj.getGroup();
validateGroupSupportsUpdate(group);
if (obj.getUpdateInfo() != null && obj.getUpdateInfo().isInherentDelete()) {
validateUpdate(obj, ICommand.TYPE_DELETE, obj.getUpdateInfo());
}
}
@Override
public void visit(GroupBy obj) {
// Get list of all group by IDs
List<Expression> groupBySymbols = obj.getSymbols();
validateSortable(groupBySymbols);
for (Expression expr : groupBySymbols) {
if (!ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(expr).isEmpty() || expr instanceof Constant || expr instanceof Reference) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.groupby_subquery, expr), expr);
}
}
}
@Override
public void visit(GroupSymbol obj) {
try {
if (this.getMetadata().isScalarGroup(obj.getMetadataID())) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.invalid_scalar_group_reference, obj),obj);
}
} catch (Exception e) {
handleException(e);
}
}
@Override
public void visit(Insert obj) {
validateNoXMLUpdates(obj);
validateGroupSupportsUpdate(obj.getGroup());
validateInsert(obj);
try {
if (obj.isMerge()) {
Collection keys = getMetadata().getUniqueKeysInGroup(obj.getGroup().getMetadataID());
if (keys.isEmpty()) {
handleValidationError(Messages.gs(Messages.TEIID.TEIID31132, obj.getGroup()), obj);
} else {
Set<Object> keyCols = new LinkedHashSet<Object>(getMetadata().getElementIDsInKey(keys.iterator().next()));
for (ElementSymbol es : obj.getVariables()) {
keyCols.remove(es.getMetadataID());
}
if (!keyCols.isEmpty()) {
handleValidationError(Messages.gs(Messages.TEIID.TEIID31133, obj.getGroup(), obj.getVariables()), obj);
}
}
}
} catch (Exception e1) {
handleException(e1);
}
if (obj.getQueryExpression() != null) {
validateMultisourceInsert(obj.getGroup());
}
if (obj.getUpdateInfo() != null && obj.getUpdateInfo().isInherentInsert()) {
validateUpdate(obj, ICommand.TYPE_INSERT, obj.getUpdateInfo());
try {
if (obj.getUpdateInfo().findInsertUpdateMapping(obj, false) == null) {
handleValidationError(Messages.gs(Messages.TEIID.TEIID30376, obj.getVariables()), obj);
}
} catch (Exception e) {
handleValidationError(e.getMessage(), obj);
}
}
}
@Override
public void visit(OrderByItem obj) {
validateSortable(obj.getSymbol());
if (obj.getExpressionPosition() < 0 ) {
for (SubqueryContainer subquery : ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(obj)) {
for (ElementSymbol es : ElementCollectorVisitor.getElements(obj, true, true)) {
if (es.isExternalReference()) {
handleValidationError(Messages.gs(Messages.TEIID.TEIID31156, subquery), obj);
}
}
}
}
}
@Override
public void visit(Query obj) {
validateHasProjectedSymbols(obj);
if(isXMLCommand(obj)) {
//no temp table (Select Into) allowed
if(obj.getInto() != null){
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0069),obj);
}
this.isXML = true;
validateXMLQuery(obj);
} else {
this.inQuery = true;
validateAggregates(obj);
//if it is select with no from, should not have ScalarSubQuery
if(obj.getSelect() != null && obj.getFrom() == null){
if(!ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(obj.getSelect()).isEmpty()){
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0067),obj);
}
}
if (obj.getInto() != null) {
validateSelectInto(obj);
}
}
}
@Override
public void visit(Select obj) {
validateSelectElements(obj);
if(obj.isDistinct()) {
validateSortable(obj.getProjectedSymbols());
}
}
@Override
public void visit(SubquerySetCriteria obj) {
validateSubquery(obj);
if (isNonComparable(obj.getExpression())) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0027, obj, getDataTypeManager().getDataTypeName(obj.getExpression().getType())), obj);
}
this.validateRowLimitFunctionNotInInvalidCriteria(obj);
Collection<Expression> projSymbols = obj.getCommand().getProjectedSymbols();
//Subcommand should have one projected symbol (query with one expression
//in SELECT or stored procedure execution that returns a single value).
if(projSymbols.size() != 1) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0011),obj);
}
}
@Override
public void visit(XMLSerialize obj) {
if (obj.getEncoding() != null ) {
try {
Charset.forName(obj.getEncoding());
} catch (IllegalArgumentException e) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.invalid_encoding, obj.getEncoding()), obj);
}
if ((obj.getType() != DataTypeManagerService.DefaultDataTypes.BLOB.getTypeClass() && obj.getType() != DataTypeManagerService.DefaultDataTypes.VARBINARY.getTypeClass())) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.encoding_for_binary), obj);
}
}
}
@Override
public void visit(SetQuery obj) {
validateHasProjectedSymbols(obj);
validateSetQuery(obj);
}
@Override
public void visit(Update obj) {
validateNoXMLUpdates(obj);
validateGroupSupportsUpdate(obj.getGroup());
validateUpdate(obj);
}
@Override
public void visit(Into obj) {
GroupSymbol target = obj.getGroup();
validateGroupSupportsUpdate(target);
validateMultisourceInsert(obj.getGroup());
}
private void validateMultisourceInsert(GroupSymbol group) {
try {
if (getMetadata().isMultiSource(getMetadata().getModelID(group.getMetadataID()))) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.multisource_insert, group), group);
}
} catch (Exception e) {
handleException(e);
}
}
@Override
public void visit(Function obj) {
if(IFunctionLibrary.FunctionName.LOOKUP.equalsIgnoreCase(obj.getName())) {
try {
ResolverUtil.ResolvedLookup resolvedLookup = ResolverUtil.resolveLookup(obj, getMetadata());
if(ValidationVisitor.isNonComparable(resolvedLookup.getKeyElement())) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.invalid_lookup_key, resolvedLookup.getKeyElement(), getDataTypeManager().getDataTypeName(resolvedLookup.getKeyElement().getType())), resolvedLookup.getKeyElement());
}
} catch (Exception e) {
handleException(e, obj);
}
} else if (IFunctionLibrary.FunctionName.CONTEXT.equalsIgnoreCase(obj.getName())) {
if(!isXML) {
// can't use this pseudo-function in non-XML queries
handleValidationError(Messages.getString(Messages.ValidationVisitor.The_context_function_cannot_be_used_in_a_non_XML_command), obj);
} else {
if (!(obj.getArg(0) instanceof ElementSymbol)){
handleValidationError(Messages.getString(Messages.ERR.ERR_015_004_0036), obj);
}
for (Iterator<Function> functions = FunctionCollectorVisitor.getFunctions(obj.getArg(1), false).iterator(); functions.hasNext();) {
Function function = functions.next();
if (IFunctionLibrary.FunctionName.CONTEXT.equalsIgnoreCase(function.getName())) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.Context_function_nested), obj);
}
}
}
} else if (IFunctionLibrary.FunctionName.ROWLIMIT.equalsIgnoreCase(obj.getName()) ||
IFunctionLibrary.FunctionName.ROWLIMITEXCEPTION.equalsIgnoreCase(obj.getName())) {
if(isXML) {
if (!(obj.getArg(0) instanceof ElementSymbol)) {
// Arg must be an element symbol
handleValidationError(Messages.getString(Messages.ValidationVisitor.rowlimit2), obj);
}
} else {
// can't use this pseudo-function in non-XML queries
handleValidationError(Messages.getString(Messages.ValidationVisitor.The_rowlimit_function_cannot_be_used_in_a_non_XML_command), obj);
}
} else if(obj.getName().equalsIgnoreCase(SourceSystemFunctions.XPATHVALUE)) {
// Validate the xpath value is valid
if(obj.getArgs()[1] instanceof Constant) {
Constant xpathConst = (Constant) obj.getArgs()[1];
try {
XMLSystemFunctions.validateXpath((String)xpathConst.getValue());
} catch(Exception e) {
handleValidationError(Messages.getString(Messages.QueryResolver.invalid_xpath, e.getMessage()), obj);
}
}
} else if(obj.getName().equalsIgnoreCase(SourceSystemFunctions.TO_BYTES) || obj.getName().equalsIgnoreCase(SourceSystemFunctions.TO_CHARS)) {
try {
FunctionMethods.getCharset((String)((Constant)obj.getArg(1)).getValue());
} catch (IllegalArgumentException e) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.invalid_encoding, obj.getArg(1)), obj);
}
} else if (obj.isAggregate()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.user_defined_aggregate_as_function, obj, obj.getName()), obj);
} else if (IFunctionLibrary.FunctionName.JSONARRAY.equalsIgnoreCase(obj.getName())) {
Expression[] args = obj.getArgs();
for (Expression expression : args) {
validateJSONValue(obj, expression);
}
}
}
// ############### Visitor methods for stored procedure lang objects ##################
public void visit(AssignmentStatement obj) {
ElementSymbol variable = obj.getVariable();
validateAssignment(obj, variable);
}
@Override
public void visit(CommandStatement obj) {
visit9(obj);
}
@Override
public void visit(StoredProcedure obj) {
for (SPParameter param : obj.getInputParameters()) {
try {
if (!getMetadata().elementSupports(param.getMetadataID(), SupportConstants.Element.NULL) && EvaluatableVisitor.isFullyEvaluatable(param.getExpression(), true)) {
try {
// If nextValue is an expression, evaluate it before checking for null
Object evaluatedValue = Evaluator.assess(param.getExpression());
if(evaluatedValue == null) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0055, param.getParameterSymbol()), param.getParameterSymbol());
} else if (evaluatedValue instanceof ArrayImpl && getMetadata().isVariadic(param.getMetadataID())) {
ArrayImpl av = (ArrayImpl)evaluatedValue;
for (Object o : av.getValues()) {
if (o == null) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0055, param.getParameterSymbol()), param.getParameterSymbol());
}
}
}
} catch(Exception e) {
//ignore for now, we don't have the context which could be the problem
}
}
} catch (Exception e) {
handleException(e);
}
}
}
private void validateAssignment(LanguageObject obj,
ElementSymbol variable) {
String groupName = variable.getGroupSymbol().getName();
//This will actually get detected by the resolver, since we inject an automatic declaration.
if(groupName.equals(ProcedureReservedWords.CHANGING) || groupName.equals(ProcedureReservedWords.INPUTS)) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0012, ProcedureReservedWords.INPUTS, ProcedureReservedWords.CHANGING), obj);
}
}
@Override
public void visit(ScalarSubquery obj) {
validateSubquery(obj);
Collection<Expression> projSymbols = obj.getCommand().getProjectedSymbols();
//Scalar subquery should have one projected symbol (query with one expression
//in SELECT or stored procedure execution that returns a single value).
if(projSymbols.size() != 1) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_008_0032, obj.getCommand()), obj.getCommand());
}
}
/**
* REMOVED IN TEIID 8.0
* Validate that the command assigns a value to the ROWS_UPDATED variable
* @param obj
* @since 4.2
*/
protected void validateContainsRowsUpdatedVariable(CreateUpdateProcedureCommand obj) {
throw new UnsupportedOperationException();
}
// REMOVED IN TEIID 8.0
@Override
public void visit(CreateUpdateProcedureCommand obj) {
throw new UnsupportedOperationException();
}
@Override
public void visit(CreateProcedureCommand obj) {
//check that the procedure does not contain references to itself
if (obj.getUpdateType() == ICommand.TYPE_UNKNOWN) {
if (GroupCollectorVisitor.getGroups(obj,true).contains(obj.getVirtualGroup())) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.Procedure_has_group_self_reference),obj);
}
if (obj.getResultSetColumns() != null) {
//some unit tests bypass setting the columns
this.createProc = obj;
}
}
}
private boolean isUpdateProcedure() {
if (this.createProc == null)
return false;
if (!(this.createProc instanceof CreateUpdateProcedureCommand))
return false;
throw new UnsupportedOperationException();
}
// REMOVED IN TEIID 8.0
@Override
public void visit(HasCriteria obj) {
throw new UnsupportedOperationException();
}
// REMOVED IN TEIID 8.0
@Override
public void visit(TranslateCriteria obj) {
throw new UnsupportedOperationException();
}
@Override
public void visit(CompoundCriteria obj) {
// Validate use of 'rowlimit' or 'rowlimitexception' pseudo-function - each occurrence must be in a single
// CompareCriteria which is entirely it's own conjunct (not OR'ed with anything else)
if (isXML) {
// Collect all occurrances of rowlimit and rowlimitexception functions
List<Function> rowLimitFunctions = new ArrayList<Function>();
FunctionCollectorVisitor visitor = new FunctionCollectorVisitor(getTeiidVersion(), rowLimitFunctions, IFunctionLibrary.FunctionName.ROWLIMIT.text());
PreOrderNavigator.doVisit(obj, visitor);
visitor = new FunctionCollectorVisitor(getTeiidVersion(), rowLimitFunctions, IFunctionLibrary.FunctionName.ROWLIMITEXCEPTION.text());
PreOrderNavigator.doVisit(obj, visitor);
final int functionCount = rowLimitFunctions.size();
if (functionCount > 0) {
// Verify each use of rowlimit function is in a compare criteria that is
// entirely it's own conjunct
Iterator<Criteria> conjunctIter = Criteria.separateCriteriaByAnd(obj).iterator();
int i = 0;
while (conjunctIter.hasNext() && i<functionCount ) {
Object conjunct = conjunctIter.next();
if (conjunct instanceof CompareCriteria) {
CompareCriteria crit = (CompareCriteria)conjunct;
if ((rowLimitFunctions.contains(crit.getLeftExpression()) && !rowLimitFunctions.contains(crit.getRightExpression())) ||
(rowLimitFunctions.contains(crit.getRightExpression()) && !rowLimitFunctions.contains(crit.getLeftExpression()))) {
i++;
}
}
}
if (i<functionCount) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.rowlimit3), obj);
}
}
}
}
// ######################### Validation methods #########################
/**
* REMOVED IN TEIID 8.0
* A valid translated expression is not an <code>AggregateSymbol</code> and
* does not include elements not present on the groups of the command using
* the translated criteria.
*/
protected void validateTranslateCriteria(TranslateCriteria obj) {
throw new UnsupportedOperationException();
}
protected void validateSelectElements(Select obj) {
if(isXML) {
return;
}
Collection<ElementSymbol> elements = ElementCollectorVisitor.getElements(obj, true);
Collection<ElementSymbol> cantSelect = validateElementsSupport(
elements,
SupportConstants.Element.SELECT );
if(cantSelect != null) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0024, cantSelect), cantSelect);
}
}
protected void validateHasProjectedSymbols(Command obj) {
if(obj.getProjectedSymbols().size() == 0) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0025), obj);
}
}
/**
* Validate that no elements of type OBJECT are in a SELECT DISTINCT or
* and ORDER BY.
* @param symbols List of SingleElementSymbol
*/
protected void validateSortable(List<? extends Expression> symbols) {
for (Expression expression : symbols) {
validateSortable(expression);
}
}
private void validateSortable(Expression symbol) {
if (isNonComparable(symbol)) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0026, symbol, getDataTypeManager().getDataTypeName(symbol.getType())), symbol);
}
}
/**
* @param symbol
* @return whether symbol type is non-comparable
*/
public static boolean isNonComparable(Expression symbol) {
DataTypeManagerService dataTypeManager = DataTypeManagerService.getInstance(symbol.getTeiidVersion());
return dataTypeManager.isNonComparable(dataTypeManager.getDataTypeName(symbol.getType()));
}
/**
* This method can be used to validate Update commands cannot be
* executed against XML documents.
*/
protected void validateNoXMLUpdates(Command obj) {
if(isXMLCommand(obj)) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0029), obj);
}
}
/**
* This method can be used to validate commands used in the stored
* procedure languge cannot be executed against XML documents.
*/
protected void validateNoXMLProcedures(Command obj) {
if(isXMLCommand(obj)) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0030), obj);
}
}
private void validateXMLQuery(Query obj) {
if(obj.getGroupBy() != null) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0031), obj);
}
if(obj.getHaving() != null) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0032), obj);
}
if(obj.getLimit() != null) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.limit_not_valid_for_xml), obj);
}
if (obj.getOrderBy() != null) {
OrderBy orderBy = obj.getOrderBy();
for (OrderByItem item : orderBy.getOrderByItems()) {
if (!(item.getSymbol() instanceof ElementSymbol)) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.orderby_expression_xml), obj);
}
}
}
}
protected void validateGroupSupportsUpdate(GroupSymbol groupSymbol) {
try {
if(! getMetadata().groupSupports(groupSymbol.getMetadataID(), SupportConstants.Group.UPDATE)) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0033,
SQLStringVisitor.getSQLString(groupSymbol)), groupSymbol);
}
} catch (Exception e) {
handleException(e, groupSymbol);
}
}
protected void validateSetQuery(SetQuery query) {
// Walk through sub queries - validate each one separately and
// also check the columns of each for comparability
for (QueryCommand subQuery : query.getQueryCommands()) {
if(isXMLCommand(subQuery)) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0034), query);
}
if (subQuery instanceof Query && ((Query)subQuery).getInto() != null) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.union_insert), query);
}
}
if (!query.isAll() || query.getOperation() == Operation.EXCEPT || query.getOperation() == Operation.INTERSECT) {
validateSortable(query.getProjectedSymbols());
}
if (query.isAll() && (query.getOperation() == Operation.EXCEPT || query.getOperation() == Operation.INTERSECT)) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.excpet_intersect_all), query);
}
}
private void validateAggregates(Query query) {
Select select = query.getSelect();
GroupBy groupBy = query.getGroupBy();
Criteria having = query.getHaving();
validateNoAggsInClause(groupBy);
List<GroupSymbol> correlationGroups = null;
validateNoAggsInClause(query.getCriteria());
if (query.getFrom() == null) {
validateNoAggsInClause(select);
validateNoAggsInClause(query.getOrderBy());
} else {
validateNoAggsInClause(query.getFrom());
correlationGroups = query.getFrom().getGroups();
}
Set<Expression> groupSymbols = null;
boolean hasAgg = false;
if (groupBy != null) {
groupSymbols = new HashSet<Expression>(groupBy.getSymbols());
hasAgg = true;
}
LinkedHashSet<Expression> invalid = new LinkedHashSet<Expression>();
LinkedHashSet<Expression> invalidWindowFunctions = new LinkedHashSet<Expression>();
LinkedList<AggregateSymbol> aggs = new LinkedList<AggregateSymbol>();
if (having != null) {
validateCorrelatedReferences(query, correlationGroups, groupSymbols, having, invalid);
AggregateSymbolCollectorVisitor.getAggregates(having, aggs, invalid, null, invalidWindowFunctions, groupSymbols);
hasAgg = true;
}
if (groupBy != null && query.getOrderBy() != null) {
Set<Expression> exanded = new HashSet<Expression>(groupSymbols);
exanded.addAll(select.getProjectedSymbols());
for (OrderByItem item : query.getOrderBy().getOrderByItems()) {
if (item.isUnrelated()) {
AggregateSymbolCollectorVisitor.getAggregates(item.getSymbol(), aggs, invalid, null, invalidWindowFunctions, exanded);
}
}
}
for (Expression symbol : select.getProjectedSymbols()) {
if (hasAgg || !aggs.isEmpty()) {
validateCorrelatedReferences(query, correlationGroups, groupSymbols, symbol, invalid);
}
AggregateSymbolCollectorVisitor.getAggregates(symbol, aggs, invalid, null, null, groupSymbols);
}
if ((!aggs.isEmpty() || hasAgg) && !invalid.isEmpty()) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0037, invalid), invalid);
}
if (!invalidWindowFunctions.isEmpty()) {
handleValidationError(Messages.getString(Messages.TeiidParser.window_only_top_level, invalidWindowFunctions), invalidWindowFunctions);
}
}
/**
* This validation is more convoluted than needed since it is being run before rewrite/planning.
* Ideally we would already have correlated references set on the subqueries.
*/
private void validateCorrelatedReferences(Query query,
final List<GroupSymbol> correlationGroups, final Set<Expression> groupingSymbols, LanguageObject object, LinkedHashSet<Expression> invalid) {
if (query.getFrom() == null) {
return;
}
ElementCollectorVisitor ecv = new ElementCollectorVisitor(getTeiidVersion(), invalid) {
@Override
public void visit(ElementSymbol obj) {
if (obj.isExternalReference() && correlationGroups.contains(obj.getGroupSymbol())
&& (groupingSymbols == null || !groupingSymbols.contains(obj))) {
super.visit(obj);
}
}
};
AggregateStopNavigator asn = new AggregateStopNavigator(ecv, groupingSymbols);
object.acceptVisitor(asn);
}
private void validateNoAggsInClause(LanguageObject clause) {
if (clause == null) {
return;
}
LinkedHashSet<Expression> aggs = new LinkedHashSet<Expression>();
AggregateSymbolCollectorVisitor.getAggregates(clause, aggs, null, null, aggs, null);
if (!aggs.isEmpty()) {
handleValidationError(Messages.getString(Messages.TeiidParser.Aggregate_only_top_level, aggs), aggs);
}
}
protected void validateInsert(Insert obj) {
Collection<ElementSymbol> vars = obj.getVariables();
Iterator<ElementSymbol> varIter = vars.iterator();
Collection values = obj.getValues();
Iterator valIter = values.iterator();
GroupSymbol insertGroup = obj.getGroup();
try {
Set<ElementSymbol> seen = new HashSet<ElementSymbol>();
boolean multiSource = getMetadata().isMultiSource(getMetadata().getModelID(insertGroup.getMetadataID()));
// Validate that all elements in variable list are updatable
for (ElementSymbol insertElem : vars) {
if(! getMetadata().elementSupports(insertElem.getMetadataID(), SupportConstants.Element.UPDATE)) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0052, insertElem), insertElem);
}
if (multiSource && getMetadata().isMultiSourceElement(insertElem.getMetadataID())) {
multiSource = false;
}
if (!seen.add(insertElem)) {
handleValidationError(Messages.getString(Messages.TEIID.TEIID31179, obj.getGroup(), insertElem), insertElem);
}
}
if (multiSource) {
validateMultisourceInsert(insertGroup);
}
// Get elements in the group.
Collection<ElementSymbol> insertElmnts = new LinkedList<ElementSymbol>(ResolverUtil.resolveElementsInGroup(insertGroup, getMetadata()));
// remove all elements specified in insert to get the ignored elements
insertElmnts.removeAll(vars);
for (ElementSymbol nextElmnt : insertElmnts) {
if(!getMetadata().elementSupports(nextElmnt.getMetadataID(), SupportConstants.Element.DEFAULT_VALUE) &&
!getMetadata().elementSupports(nextElmnt.getMetadataID(), SupportConstants.Element.NULL) &&
!getMetadata().elementSupports(nextElmnt.getMetadataID(), SupportConstants.Element.AUTO_INCREMENT)) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0053, new Object[] {insertGroup, nextElmnt}), nextElmnt);
}
}
//check to see if the elements support nulls in metadata,
// if any of the value present in the insert are null
while(valIter.hasNext() && varIter.hasNext()) {
Expression nextValue = (Expression) valIter.next();
ElementSymbol nextVar = varIter.next();
if (EvaluatableVisitor.isFullyEvaluatable(nextValue, true)) {
try {
// If nextValue is an expression, evaluate it before checking for null
Object evaluatedValue = Evaluator.assess(nextValue);
if(evaluatedValue == null && ! getMetadata().elementSupports(nextVar.getMetadataID(), SupportConstants.Element.NULL)) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0055, nextVar), nextVar);
}
} catch(Exception e) {
//ignore for now, we don't have the context which could be the problem
}
}
}// end of while
} catch(Exception e) {
handleException(e, obj);
}
}
protected void validateSetClauseList(SetClauseList list) {
Set<ElementSymbol> dups = new HashSet<ElementSymbol>();
HashSet<ElementSymbol> changeVars = new HashSet<ElementSymbol>();
for (SetClause clause : list.getClauses()) {
ElementSymbol elementID = clause.getSymbol();
if (!changeVars.add(elementID)) {
dups.add(elementID);
}
}
if(!dups.isEmpty()) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0062, dups), dups);
}
}
protected void validateUpdate(Update update) {
try {
UpdateInfo info = update.getUpdateInfo();
// list of elements that are being updated
for (SetClause entry : update.getChangeList().getClauses()) {
ElementSymbol elementID = entry.getSymbol();
// Check that left side element is updatable
if(! getMetadata().elementSupports(elementID.getMetadataID(), SupportConstants.Element.UPDATE)) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0059, elementID), elementID);
}
Object metadataID = elementID.getMetadataID();
if (getMetadata().isMultiSourceElement(metadataID)){
handleValidationError(Messages.getString(Messages.ValidationVisitor.multi_source_update_not_allowed, elementID), elementID);
}
// Check that right expression is a constant and is non-null
Expression value = entry.getValue();
if (EvaluatableVisitor.isFullyEvaluatable(value, true)) {
try {
Constant c = createASTNode(ASTNodes.CONSTANT);
c.setValue(Evaluator.assess(value));
value = c;
} catch (Exception err) {
}
}
if(value instanceof Constant) {
// If value is null, check that element supports this as a nullable column
if(((Constant)value).getValue() == null && ! getMetadata().elementSupports(elementID.getMetadataID(), SupportConstants.Element.NULL)) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0060, SQLStringVisitor.getSQLString(elementID)), elementID);
}// end of if
}
}
if (info != null && info.isInherentUpdate()) {
validateUpdate(update, ICommand.TYPE_UPDATE, info);
Set<ElementSymbol> updateCols = update.getChangeList().getClauseMap().keySet();
if (!info.hasValidUpdateMapping(updateCols)) {
handleValidationError(Messages.gs(Messages.TEIID.TEIID30376, updateCols), update);
}
}
} catch(Exception e) {
handleException(e, update);
}
validateSetClauseList(update.getChangeList());
}
private void validateUpdate(TargetedCommand update, int type, UpdateInfo info) {
String error = ProcedureContainerResolver.validateUpdateInfo(update.getGroup(), type, info);
if (error != null) {
handleValidationError(error, update.getGroup());
}
}
/**
* Validates SELECT INTO queries.
* @param query
* @since 4.2
*/
protected void validateSelectInto(Query query) {
List<Expression> symbols = query.getSelect().getProjectedSymbols();
GroupSymbol intoGroup = query.getInto().getGroup();
validateInto(query, symbols, intoGroup);
}
private void validateInto(LanguageObject query,
List<Expression> symbols,
GroupSymbol intoGroup) {
try {
List elementIDs = getMetadata().getElementIDsInGroupID(intoGroup.getMetadataID());
// Check if there are too many elements in the SELECT clause
if (symbols.size() != elementIDs.size()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.select_into_wrong_elements, new Object[] {new Integer(elementIDs.size()), new Integer(symbols.size())}), query);
return;
}
for (int symbolNum = 0; symbolNum < symbols.size(); symbolNum++) {
Expression symbol = symbols.get(symbolNum);
Object elementID = elementIDs.get(symbolNum);
// Check if supports updates
if (!getMetadata().elementSupports(elementID, SupportConstants.Element.UPDATE)) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.element_updates_not_allowed, getMetadata().getFullName(elementID)), intoGroup);
}
Class<?> symbolType = symbol.getType();
String symbolTypeName = dataTypeManager.getDataTypeName(symbolType);
String targetTypeName = getMetadata().getElementType(elementID);
if (symbolTypeName.equals(targetTypeName)) {
continue;
}
if (!dataTypeManager.isImplicitConversion(symbolTypeName, targetTypeName)) { // If there's no implicit conversion between the two
Object[] params = new Object [] {symbolTypeName, targetTypeName, new Integer(symbolNum + 1), query};
handleValidationError(Messages.getString(Messages.ValidationVisitor.select_into_no_implicit_conversion, params), query);
continue;
}
}
} catch (Exception e) {
handleException(e, query);
}
}
private void validateRowLimitFunctionNotInInvalidCriteria(Criteria obj) {
// Collect all occurrances of rowlimit and rowlimitexception functions
List<Function> rowLimitFunctions = new ArrayList<Function>();
FunctionCollectorVisitor visitor = new FunctionCollectorVisitor(getTeiidVersion(), rowLimitFunctions, IFunctionLibrary.FunctionName.ROWLIMIT.text());
PreOrderNavigator.doVisit(obj, visitor);
visitor = new FunctionCollectorVisitor(getTeiidVersion(), rowLimitFunctions, IFunctionLibrary.FunctionName.ROWLIMITEXCEPTION.text());
PreOrderNavigator.doVisit(obj, visitor);
if (rowLimitFunctions.size() > 0) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.rowlimit3), obj);
}
}
/**
* @see LanguageVisitor#visit(org.teiid.query.sql.lang.BetweenCriteria)
* @since 4.3
*/
@Override
public void visit(BetweenCriteria obj) {
if (isNonComparable(obj.getExpression())) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0027, obj, getDataTypeManager().getDataTypeName(obj.getExpression().getType())), obj);
}
this.validateRowLimitFunctionNotInInvalidCriteria(obj);
}
/**
* @see LanguageVisitor#visit(org.teiid.query.sql.lang.IsNullCriteria)
* @since 4.3
*/
@Override
public void visit(IsNullCriteria obj) {
this.validateRowLimitFunctionNotInInvalidCriteria(obj);
}
@Override
public void visit(IsDistinctCriteria isDistinctCriteria) {
try {
IQueryMetadataInterface metadata = getMetadata();
if (!metadata.isScalarGroup(isDistinctCriteria.getLeftRowValue().getMetadataID())) {
handleValidationError(Messages.gs(Messages.TEIID.TEIID31171, isDistinctCriteria.getLeftRowValue()), isDistinctCriteria.getLeftRowValue());
}
if (!metadata.isScalarGroup(isDistinctCriteria.getRightRowValue().getMetadataID())) {
handleValidationError(Messages.gs(Messages.TEIID.TEIID31171, isDistinctCriteria.getRightRowValue()), isDistinctCriteria.getRightRowValue());
}
} catch (Exception e) {
handleException(e);
}
}
/**
* @see LanguageVisitor#visit(org.teiid.query.sql.lang.MatchCriteria)
* @since 4.3
*/
@Override
public void visit(MatchCriteria obj) {
this.validateRowLimitFunctionNotInInvalidCriteria(obj);
}
/**
* @see LanguageVisitor#visit(org.teiid.query.sql.lang.NotCriteria)
* @since 4.3
*/
@Override
public void visit(NotCriteria obj) {
this.validateRowLimitFunctionNotInInvalidCriteria(obj);
}
/**
* @see LanguageVisitor#visit(org.teiid.query.sql.lang.SetCriteria)
* @since 4.3
*/
@Override
public void visit(SetCriteria obj) {
if (isNonComparable(obj.getExpression())) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0027, obj, getDataTypeManager().getDataTypeName(obj.getExpression().getType())), obj);
}
this.validateRowLimitFunctionNotInInvalidCriteria(obj);
}
/**
* @see LanguageVisitor#visit(org.teiid.query.sql.lang.SubqueryCompareCriteria)
* @since 4.3
*/
@Override
public void visit(SubqueryCompareCriteria obj) {
validateSubquery(obj);
if (isNonComparable(obj.getLeftExpression())) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0027, obj, getDataTypeManager().getDataTypeName(obj.getLeftExpression().getType())), obj);
}
this.validateRowLimitFunctionNotInInvalidCriteria(obj);
}
@Override
public void visit(Option obj) {
List<String> dep = obj.getDependentGroups();
List<String> notDep = obj.getNotDependentGroups();
if (dep != null && !dep.isEmpty()
&& notDep != null && !notDep.isEmpty()) {
String groupName = null;
String notDepGroup = null;
for (Iterator<String> i = dep.iterator(); i.hasNext();) {
groupName = i.next();
for (Iterator<String> j = notDep.iterator(); j.hasNext();) {
notDepGroup = j.next();
if (notDepGroup.equalsIgnoreCase(groupName)) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.group_in_both_dep, groupName), obj);
return;
}
}
}
}
}
/**
* @see LanguageVisitor#visit(org.teiid.query.sql.lang.DynamicCommand)
*/
@Override
public void visit(DynamicCommand obj) {
if (obj.getIntoGroup() != null) {
validateInto(obj, obj.getAsColumns(), obj.getIntoGroup());
}
if (obj.getUsing() != null) {
validateSetClauseList(obj.getUsing());
}
}
@Override
public void visit(Create obj) {
if (!obj.getPrimaryKey().isEmpty()) {
validateSortable(obj.getPrimaryKey());
}
if (obj.getTableMetadata() != null) {
Table t = obj.getTableMetadata();
if (!t.getForeignKeys().isEmpty()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.temp_fk, obj.getTable()), obj);
}
}
}
/**
* @see LanguageVisitor#visit(org.teiid.query.sql.lang.Drop)
*/
@Override
public void visit(Drop drop) {
if (!drop.getTable().isTempTable()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.drop_of_nontemptable, drop.getTable()), drop);
}
try {
if (getMetadata().isVirtualGroup(drop.getTable().getMetadataID())) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.drop_of_globaltemptable, drop.getTable()), drop);
}
} catch (Exception e) {
handleException(e);
}
}
@Override
public void visit(CompareCriteria obj) {
if (isNonComparable(obj.getLeftExpression())) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0027, obj, getDataTypeManager().getDataTypeName(obj.getLeftExpression().getType())), obj);
}
// Validate use of 'rowlimit' and 'rowlimitexception' pseudo-functions - they cannot be nested within another
// function, and their operands must be a nonnegative integers
// Collect all occurrences of rowlimit function
List rowLimitFunctions = new ArrayList();
FunctionCollectorVisitor visitor = new FunctionCollectorVisitor(getTeiidVersion(), rowLimitFunctions, IFunctionLibrary.FunctionName.ROWLIMIT.text());
PreOrderNavigator.doVisit(obj, visitor);
visitor = new FunctionCollectorVisitor(getTeiidVersion(), rowLimitFunctions, IFunctionLibrary.FunctionName.ROWLIMITEXCEPTION.text());
PreOrderNavigator.doVisit(obj, visitor);
final int functionCount = rowLimitFunctions.size();
if (functionCount > 0) {
Function function = null;
Expression expr = null;
if (obj.getLeftExpression() instanceof Function) {
Function leftExpr = (Function)obj.getLeftExpression();
if (IFunctionLibrary.FunctionName.ROWLIMIT.equalsIgnoreCase(leftExpr.getName()) ||
IFunctionLibrary.FunctionName.ROWLIMITEXCEPTION.equalsIgnoreCase(leftExpr.getName())) {
function = leftExpr;
expr = obj.getRightExpression();
}
}
if (function == null && obj.getRightExpression() instanceof Function) {
Function rightExpr = (Function)obj.getRightExpression();
if (IFunctionLibrary.FunctionName.ROWLIMIT.equalsIgnoreCase(rightExpr.getName()) ||
IFunctionLibrary.FunctionName.ROWLIMITEXCEPTION.equalsIgnoreCase(rightExpr.getName())) {
function = rightExpr;
expr = obj.getLeftExpression();
}
}
if (function == null) {
// must be nested, which is invalid
handleValidationError(Messages.getString(Messages.ValidationVisitor.rowlimit0), obj);
} else {
if (expr instanceof Constant) {
Constant constant = (Constant)expr;
if (constant.getValue() instanceof Integer) {
Integer integer = (Integer)constant.getValue();
if (integer.intValue() < 0) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.rowlimit1), obj);
}
} else {
handleValidationError(Messages.getString(Messages.ValidationVisitor.rowlimit1), obj);
}
} else if (expr instanceof Reference) {
((Reference)expr).setConstraint(new PositiveIntegerConstraint(Messages.ValidationVisitor.rowlimit1));
} else {
handleValidationError(Messages.getString(Messages.ValidationVisitor.rowlimit1), obj);
}
}
}
}
@Override
public void visit(Limit obj) {
validateLimitExpression(obj, obj.getOffset());
validateLimitExpression(obj, obj.getRowLimit());
}
private void validateLimitExpression(Limit obj, Expression limitExpr) {
if (limitExpr != null) {
if (limitExpr instanceof Constant) {
Integer limit = (Integer)((Constant)limitExpr).getValue();
if (limit.intValue() < 0) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.badlimit2), obj);
}
} else if (limitExpr instanceof Reference) {
((Reference)limitExpr).setConstraint(LIMIT_CONSTRAINT);
} else if (!EvaluatableVisitor.willBecomeConstant(limitExpr)) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.badlimit1), obj);
}
}
}
@Override
public void visit(XMLForest obj) {
validateDerivedColumnNames(obj, obj.getArgs());
for (DerivedColumn dc : obj.getArgs()) {
if (dc.getAlias() == null) {
continue;
}
validateQName(obj, dc.getAlias());
validateXMLContentTypes(dc.getExpression(), obj);
}
}
@Override
public void visit(JSONObject obj) {
for (DerivedColumn dc : obj.getArgs()) {
validateJSONValue(obj, dc.getExpression());
}
}
@Override
public void visit(WindowFunction windowFunction) {
AggregateSymbol.Type type = windowFunction.getFunction().getAggregateFunction();
switch (type) {
case RANK:
case DENSE_RANK:
case ROW_NUMBER:
if (windowFunction.getWindowSpecification().getOrderBy() == null) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.ranking_requires_order_by, windowFunction), windowFunction);
}
break;
case TEXTAGG:
case ARRAY_AGG:
case JSONARRAY_AGG:
case XMLAGG:
case STRING_AGG:
if (windowFunction.getWindowSpecification().getOrderBy() != null) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.window_order_by, windowFunction), windowFunction);
}
break;
default:
break;
}
validateNoSubqueriesOrOuterReferences(windowFunction);
if (windowFunction.getFunction().getOrderBy() != null || (windowFunction.getFunction().isDistinct() && windowFunction.getWindowSpecification().getOrderBy() != null)) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0042, new Object[] {windowFunction.getFunction(), windowFunction}), windowFunction);
}
if (windowFunction.getWindowSpecification().getPartition() != null) {
validateSortable(windowFunction.getWindowSpecification().getPartition());
}
}
@Override
public void visit(AggregateSymbol obj) {
if (!inQuery) {
handleValidationError(Messages.getString(Messages.TeiidParser.Aggregate_only_top_level, obj), obj);
return;
}
if (obj.getAggregateFunction() == AggregateSymbol.Type.USER_DEFINED) {
AggregateAttributes aa = obj.getFunctionDescriptor().getMethod().getAggregateAttributes();
if (!aa.allowsDistinct() && obj.isDistinct()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.uda_not_allowed, "DISTINCT", obj), obj); //$NON-NLS-1$
}
if (!aa.allowsOrderBy() && obj.getOrderBy() != null) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.uda_not_allowed, "ORDER BY", obj), obj); //$NON-NLS-1$
}
if (aa.isAnalytic() && !obj.isWindowed()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.uda_analytic, obj), obj);
}
}
if (obj.getCondition() != null) {
Expression condition = obj.getCondition();
validateNoSubqueriesOrOuterReferences(condition);
}
Expression aggExp = null;
Expression[] aggExps = obj.getArgs();
for (Expression expression : aggExps) {
boolean windowed = obj.isWindowed();
validateNoNestedAggs(expression, windowed);
}
if (aggExps.length > 0)
aggExp = aggExps[0];
validateNoNestedAggs(obj.getOrderBy(), false);
validateNoNestedAggs(obj.getCondition(), false);
// Verify data type of aggregate expression
IAggregateSymbol.Type aggregateFunction = obj.getAggregateFunction();
if((aggregateFunction == IAggregateSymbol.Type.SUM || aggregateFunction == IAggregateSymbol.Type.AVG) && obj.getType() == null) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0041, new Object[] {aggregateFunction, obj}), obj);
} else if (obj.getType() != DataTypeManagerService.DefaultDataTypes.NULL.getTypeClass()) {
if (aggregateFunction == IAggregateSymbol.Type.XMLAGG && aggExp.getType() != DataTypeManagerService.DefaultDataTypes.XML.getTypeClass()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.non_xml, new Object[] {aggregateFunction, obj}), obj);
} else if (obj.isBoolean() && aggExp.getType() != DataTypeManagerService.DefaultDataTypes.BOOLEAN.getTypeClass()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.non_boolean, new Object[] {aggregateFunction, obj}), obj);
} else if (aggregateFunction == IAggregateSymbol.Type.JSONARRAY_AGG) {
validateJSONValue(obj, aggExp);
} else if (obj.getType() == null) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.aggregate_type, obj), obj); //$NON-NLS-1$
}
}
if((obj.isDistinct() ||
aggregateFunction == IAggregateSymbol.Type.MIN ||
aggregateFunction == IAggregateSymbol.Type.MAX) &&
dataTypeManager.isNonComparable(dataTypeManager.getDataTypeName(aggExp.getType()))) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.non_comparable, new Object[] {aggregateFunction, obj}), obj);
}
if(obj.isEnhancedNumeric()) {
if (!Number.class.isAssignableFrom(aggExp.getType())) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0041, new Object[] {aggregateFunction, obj}), obj);
}
if (obj.isDistinct()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.invalid_distinct, new Object[] {aggregateFunction, obj}), obj);
}
}
if (obj.isDistinct() && obj.getOrderBy() != null ) {
HashSet<Expression> args = new HashSet<Expression>(Arrays.asList(obj.getArgs()));
for (OrderByItem item : obj.getOrderBy().getOrderByItems()) {
if (!args.contains(item.getSymbol())) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.distinct_orderby_agg, obj), obj); //$NON-NLS-1$
break;
}
}
}
if (obj.getAggregateFunction() != IAggregateSymbol.Type.TEXTAGG) {
return;
}
TextLine tl = (TextLine)aggExp;
if (tl.isIncludeHeader()) {
validateDerivedColumnNames(obj, tl.getExpressions());
}
for (DerivedColumn dc : tl.getExpressions()) {
Expression expression = dc.getExpression();
if (expression.getType() == DefaultDataTypes.OBJECT.getTypeClass()
|| expression.getType() == null
|| expression.getType().isArray()
|| expression.getType() == DefaultDataTypes.VARBINARY.getTypeClass()
|| expression.getType() == DefaultDataTypes.BLOB.getTypeClass()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.text_content_type, expression), obj); //$NON-NLS-1$
}
}
validateTextOptions(obj, tl.getDelimiter(), tl.getQuote(), '\n');
if (tl.getEncoding() != null) {
try {
Charset.forName(tl.getEncoding());
} catch (IllegalArgumentException e) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.invalid_encoding, tl.getEncoding()), obj);
}
}
}
private void validateJSONValue(LanguageObject obj, Expression expr) {
if (expr.getType() != DataTypeManagerService.DefaultDataTypes.STRING.getTypeClass()
&& !dataTypeManager.isTransformable(
expr.getType(),
DataTypeManagerService.DefaultDataTypes.STRING.getTypeClass())) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.invalid_json_value, expr, obj), obj);
}
}
private void validateNoSubqueriesOrOuterReferences(Expression expr) {
if (!ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(expr).isEmpty()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.filter_subquery, expr), expr);
}
for (ElementSymbol es : ElementCollectorVisitor.getElements(expr, false)) {
if (es.isExternalReference()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.filter_subquery, es), es);
}
}
}
private void validateNoNestedAggs(LanguageObject aggExp, boolean windowOnly) {
// Check for any nested aggregates (which are not allowed)
if(aggExp != null) {
HashSet<Expression> nestedAggs = new LinkedHashSet<Expression>();
AggregateSymbolCollectorVisitor.getAggregates(aggExp, windowOnly?null:nestedAggs, null, null, nestedAggs, null);
if(!nestedAggs.isEmpty()) {
handleValidationError(Messages.getString(Messages.ERR.ERR_015_012_0039, nestedAggs), nestedAggs);
}
}
}
private String[] validateQName(LanguageObject obj, String name) {
try {
return Name11Checker.getInstance().getQNameParts(name);
} catch (QNameException e) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.xml_invalid_qname, name), obj);
}
return null;
}
private void validateDerivedColumnNames(LanguageObject obj, List<DerivedColumn> cols) {
for (DerivedColumn dc : cols) {
if (dc.getAlias() == null && !(dc.getExpression() instanceof ElementSymbol)) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.expression_requires_name), obj);
}
}
}
@Override
public void visit(XMLAttributes obj) {
validateDerivedColumnNames(obj, obj.getArgs());
for (DerivedColumn dc : obj.getArgs()) {
if (dc.getAlias() == null) {
continue;
}
if ("xmlns".equals(dc.getAlias())) { //$NON-NLS-1$
handleValidationError(Messages.getString(Messages.ValidationVisitor.xml_attributes_reserved), obj);
}
String[] parts = validateQName(obj, dc.getAlias());
if (parts == null) {
continue;
}
if ("xmlns".equals(parts[0])) { //$NON-NLS-1$
handleValidationError(Messages.getString(Messages.ValidationVisitor.xml_attributes_reserved, dc.getAlias()), obj);
}
}
}
@Override
public void visit(XMLElement obj) {
for (Expression expression : obj.getContent()) {
validateXMLContentTypes(expression, obj);
}
validateQName(obj, obj.getName());
}
/**
* @param expression
* @param parent
*/
public void validateXMLContentTypes(Expression expression, LanguageObject parent) {
if (expression.getType() == DefaultDataTypes.OBJECT.getTypeClass() || expression.getType() == null || expression.getType().isArray()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.xml_content_type, expression), parent);
}
}
@Override
public void visit(QueryString obj) {
validateDerivedColumnNames(obj, obj.getArgs());
}
@Override
public void visit(XMLTable obj) {
List<DerivedColumn> passing = obj.getPassing();
validatePassing(obj, obj.getXQueryExpression(), passing);
boolean hasOrdinal = false;
for (XMLColumn xc : obj.getColumns()) {
if (!xc.isOrdinal()) {
if (xc.getDefaultExpression() != null && !EvaluatableVisitor.isFullyEvaluatable(xc.getDefaultExpression(), false)) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.invalid_default, xc.getDefaultExpression()), obj);
}
continue;
}
if (hasOrdinal) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.one_ordinal), obj);
break;
}
hasOrdinal = true;
}
}
@Override
public void visit(ObjectTable obj) {
List<DerivedColumn> passing = obj.getPassing();
TreeSet<String> names = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
for (DerivedColumn dc : passing) {
if (dc.getAlias() == null) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.context_item_not_allowed), obj);
} else if (!names.add(dc.getAlias())) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.duplicate_passing, dc.getAlias()), obj);
}
}
Compilable scriptCompiler = null;
try {
ScriptEngine engine = this.getMetadata().getScriptEngine(obj.getScriptingLanguage());
obj.setScriptEngine(engine);
if (engine instanceof Compilable) {
scriptCompiler = (Compilable)engine;
engine.put(ScriptEngine.FILENAME, SQLConstants.NonReserved.OBJECTTABLE);
obj.setCompiledScript(scriptCompiler.compile(obj.getRowScript()));
}
} catch (Exception e) {
handleValidationError(e.getMessage(), obj);
}
for (ObjectColumn xc : obj.getColumns()) {
if (scriptCompiler != null) {
try {
xc.setCompiledScript(scriptCompiler.compile(xc.getPath()));
} catch (ScriptException e) {
handleValidationError(Messages.gs(Messages.TEIID.TEIID31110, xc.getPath(), e.getMessage()), obj); //$NON-NLS
}
}
if (xc.getDefaultExpression() != null && !EvaluatableVisitor.isFullyEvaluatable(xc.getDefaultExpression(), false)) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.invalid_default, xc.getDefaultExpression()), obj);
}
}
}
@Override
public void visit(XMLQuery obj) {
validatePassing(obj, obj.getXQueryExpression(), obj.getPassing());
}
@Override
public void visit(XMLExists obj) {
validatePassing(obj, obj.getXmlQuery().getXQueryExpression(), obj.getXmlQuery().getPassing());
}
@Override
public void visit(XMLCast obj) {
if (obj.getExpression().getType() != DefaultDataTypes.XML.getTypeClass()
&& obj.getType() != DefaultDataTypes.XML.getTypeClass()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.xmlcast_types, obj), obj);
}
}
private void validatePassing(LanguageObject obj, SaxonXQueryExpression xqe, List<DerivedColumn> passing) {
boolean context = false;
boolean hadError = false;
TreeSet<String> names = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
for (DerivedColumn dc : passing) {
if (dc.getAlias() == null) {
Class<?> type = dc.getExpression().getType();
if (type != DataTypeManagerService.DefaultDataTypes.XML.getTypeClass()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.context_item_type), obj);
}
if (context && !hadError) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.passing_requires_name), obj);
hadError = true;
}
context = true;
} else {
validateXMLContentTypes(dc.getExpression(), obj);
if (!names.add(dc.getAlias())) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.duplicate_passing, dc.getAlias()), obj);
}
}
}
if (xqe.usesContextItem() && !context) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.context_required), obj);
}
}
@Override
public void visit(XMLNamespaces obj) {
boolean hasDefault = false;
for (NamespaceItem item : obj.getNamespaceItems()) {
if (item.getPrefix() != null) {
if (item.getPrefix().equals("xml") || item.getPrefix().equals("xmlns")) { //$NON-NLS-1$ //$NON-NLS-2$
handleValidationError(Messages.getString(Messages.ValidationVisitor.xml_namespaces_reserved), obj);
} else if (!Name11Checker.getInstance().isValidNCName(item.getPrefix())) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.xml_namespaces_invalid, item.getPrefix()), obj);
}
if (item.getUri().length() == 0) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.xml_namespaces_null_uri), obj);
}
continue;
}
if (hasDefault) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.xml_namespaces), obj);
break;
}
hasDefault = true;
}
}
@Override
public void visit(TextTable obj) {
boolean widthSet = false;
Character delimiter = null;
Character quote = null;
boolean usingSelector = false;
for (TextColumn column : obj.getColumns()) {
if (column.isOrdinal())
continue;
if (column.getWidth() != null) {
widthSet = true;
if (column.getWidth() < 0) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.text_table_negative), obj);
}
} else if (widthSet) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.text_table_invalid_width), obj);
}
if (column.getSelector() != null) {
usingSelector = true;
if (obj.getSelector() != null && obj.getSelector().equals(column.getSelector())) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.text_table_selector_required), obj);
}
}
if (column.getPosition() != null && column.getPosition() < 0) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.text_table_negative), obj);
}
}
if (widthSet) {
if (obj.getDelimiter() != null || obj.getHeader() != null || obj.getQuote() != null || usingSelector) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.text_table_width), obj);
}
} else {
if (obj.getHeader() != null && obj.getHeader() < 0) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.text_table_negative), obj);
}
if (!obj.isUsingRowDelimiter()) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.fixed_option), obj);
}
delimiter = obj.getDelimiter();
quote = obj.getQuote();
validateTextOptions(obj, delimiter, quote, obj.getRowDelimiter());
}
if (obj.getSkip() != null && obj.getSkip() < 0) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.text_table_negative), obj);
}
if (usingSelector && obj.getSelector() == null) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.text_table_selector_required), obj);
}
}
private void validateTextOptions(LanguageObject obj, Character delimiter, Character quote, Character newLine) {
if (quote == null) {
quote = '"';
}
if (delimiter == null) {
delimiter = ',';
}
if (newLine == null) {
newLine = '\n';
}
if (quote.equals(delimiter)) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.text_table_delimiter), obj);
}
if (quote.equals(newLine) || delimiter.equals(newLine)) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.text_table_newline), obj);
}
}
@Override
public void visit(XMLParse obj) {
if (obj.getExpression().getType() == DataTypeManagerService.DefaultDataTypes.STRING.getTypeClass())
return;
if(obj.getExpression().getType() == DataTypeManagerService.DefaultDataTypes.CLOB.getTypeClass())
return;
if(obj.getExpression().getType() == DataTypeManagerService.DefaultDataTypes.BLOB.getTypeClass())
return;
if(obj.getExpression().getType() == DataTypeManagerService.DefaultDataTypes.VARBINARY.getTypeClass())
return;
handleValidationError(Messages.getString(Messages.ValidationVisitor.xmlparse_type), obj);
}
@Override
public void visit(ExistsCriteria obj) {
validateSubquery(obj);
}
@Override
public void visit(SubqueryFromClause obj) {
validateSubquery(obj);
}
@Override
public void visit(LoopStatement obj) {
validateSubquery(obj);
}
@Override
public void visit(WithQueryCommand obj) {
validateSubquery(obj);
}
@Override
public void visit(AlterView obj) {
try {
QueryResolver queryResolver = new QueryResolver(getTeiidVersion());
queryResolver.validateProjectedSymbols(obj.getTarget(), getMetadata(), obj.getDefinition());
Validator.validate(obj.getDefinition(), getMetadata(), this);
validateAlterTarget(obj);
} catch (QueryValidatorException e) {
handleValidationError(e.getMessage(), obj.getDefinition());
} catch (Exception e) {
handleException(e);
}
}
private void validateAlterTarget(Alter<?> obj) {
if (getMetadata().getImportedModels().contains(obj.getTarget().getSchema())) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.invalid_alter, obj.getTarget()), obj.getTarget());
}
}
@Override
public void visit(AlterProcedure obj) {
GroupSymbol gs = obj.getTarget();
validateAlterTarget(obj);
try {
if (!gs.isProcedure() || !getMetadata().isVirtualModel(getMetadata().getModelID(gs.getMetadataID()))) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.not_a_procedure, gs), gs);
return;
}
Validator.validate(obj.getDefinition(), getMetadata(), this);
QueryResolver queryResolver = new QueryResolver(getTeiidVersion());
IStoredProcedureInfo<ISPParameter, IQueryNode> info = getMetadata().getStoredProcedureInfoForProcedure(gs.getName());
for (ISPParameter param : info.getParameters()) {
if (param.getParameterType() == SPParameter.RESULT_SET) {
queryResolver.validateProjectedSymbols(gs, param.getResultSetColumns(), obj.getDefinition().getProjectedSymbols());
break;
}
}
} catch (QueryValidatorException e) {
Command command = obj.getDefinition();
if (command instanceof CreateProcedureCommand)
handleValidationError(e.getMessage(), ((CreateProcedureCommand) command).getBlock());
} catch (Exception e) {
handleException(e);
}
}
@Override
public void visit(Block obj) {
if (obj.getLabel() == null) {
return;
}
for (LanguageObject lo : stack) {
if (lo instanceof Labeled) {
Labeled labeled = (Labeled)lo;
if (obj.getLabel().equalsIgnoreCase(labeled.getLabel())) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.duplicate_block_label, obj.getLabel()), obj);
}
}
}
}
private void visit9(CommandStatement obj) {
if (this.createProc == null || this.createProc.getResultSetColumns().isEmpty() || !obj.isReturnable() || !obj.getCommand().returnsResultSet()) {
return;
}
List<? extends Expression> symbols = obj.getCommand().getResultSetColumns();
if (symbols == null && obj.getCommand() instanceof DynamicCommand) {
DynamicCommand cmd = (DynamicCommand)obj.getCommand();
cmd.setAsColumns(this.createProc.getResultSetColumns());
return;
}
try {
QueryResolver queryResolver = new QueryResolver(getTeiidVersion());
queryResolver.validateProjectedSymbols(createProc.getVirtualGroup(), createProc.getResultSetColumns(), symbols);
} catch (Exception e) {
handleValidationError(Messages.gs(Messages.TEIID.TEIID31121, createProc.getVirtualGroup(), obj, e.getMessage()), obj);
}
}
@Override
public void visit(BranchingStatement obj) {
boolean matchedLabel = false;
boolean inLoop = false;
for (LanguageObject lo : stack) {
if (lo instanceof LoopStatement || lo instanceof WhileStatement) {
inLoop = true;
if (obj.getLabel() == null) {
break;
}
matchedLabel |= obj.getLabel().equalsIgnoreCase(((Labeled)lo).getLabel());
} else if (obj.getLabel() != null && lo instanceof Block && obj.getLabel().equalsIgnoreCase(((Block)lo).getLabel())) {
matchedLabel = true;
if (obj.getMode() != BranchingMode.LEAVE) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.invalid_label, obj.getLabel()), obj);
}
}
}
if (obj.getMode() != BranchingMode.LEAVE && !inLoop) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.no_loop), obj);
}
if (obj.getLabel() != null && !matchedLabel) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.unknown_block_label, obj.getLabel()), obj);
}
}
@Override
public void visit(AlterTrigger obj) {
validateAlterTarget(obj);
validateGroupSupportsUpdate(obj.getTarget());
try {
if (obj.getDefinition() != null) {
Validator.validate(obj.getDefinition(), getMetadata(), this);
}
} catch (Exception e) {
handleException(e);
}
}
//TODO: it may be simpler to catch this in the parser
private void validateSubquery(SubqueryContainer<?> subQuery) {
if (subQuery.getCommand() instanceof Query && ((Query)subQuery.getCommand()).getInto() != null) {
handleValidationError(Messages.getString(Messages.ValidationVisitor.subquery_insert), subQuery.getCommand());
}
}
}