/* * 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.eval; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.sql.Blob; import java.sql.Clob; import java.sql.SQLException; import java.sql.SQLXML; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import net.sf.saxon.Configuration; import net.sf.saxon.om.Item; import net.sf.saxon.om.NodeInfo; import net.sf.saxon.query.QueryResult; import net.sf.saxon.trans.XPathException; import net.sf.saxon.type.ValidationException; import net.sf.saxon.value.StringValue; import org.teiid.api.exception.query.ExpressionEvaluationException; import org.teiid.api.exception.query.FunctionExecutionException; import org.teiid.api.exception.query.QueryValidatorException; import org.teiid.client.SourceWarning; import org.teiid.common.buffer.BlockedException; import org.teiid.core.ComponentNotFoundException; import org.teiid.core.TeiidComponentException; import org.teiid.core.TeiidProcessingException; import org.teiid.core.TeiidRuntimeException; import org.teiid.core.types.*; import org.teiid.core.types.XMLType.Type; import org.teiid.core.types.basic.StringToSQLXMLTransform; import org.teiid.core.util.EquivalenceUtil; import org.teiid.jdbc.TeiidSQLException; import org.teiid.language.Like.MatchMode; import org.teiid.metadata.FunctionMethod.PushDown; import org.teiid.query.QueryPlugin; import org.teiid.query.function.FunctionDescriptor; import org.teiid.query.function.FunctionLibrary; import org.teiid.query.function.JSONFunctionMethods.JSONBuilder; import org.teiid.query.function.source.XMLSystemFunctions; import org.teiid.query.function.source.XMLSystemFunctions.XmlConcat; import org.teiid.query.metadata.TempMetadataID; import org.teiid.query.processor.ProcessorDataManager; import org.teiid.query.processor.relational.XMLTableNode; import org.teiid.query.sql.LanguageObject; import org.teiid.query.sql.lang.*; import org.teiid.query.sql.proc.ExceptionExpression; import org.teiid.query.sql.symbol.*; import org.teiid.query.sql.symbol.XMLNamespaces.NamespaceItem; import org.teiid.query.sql.util.ValueIterator; import org.teiid.query.sql.util.ValueIteratorSource; import org.teiid.query.sql.util.VariableContext; import org.teiid.query.sql.visitor.ValueIteratorProviderCollectorVisitor; import org.teiid.query.util.CommandContext; import org.teiid.query.xquery.saxon.SaxonXQueryExpression; import org.teiid.query.xquery.saxon.SaxonXQueryExpression.Result; import org.teiid.query.xquery.saxon.SaxonXQueryExpression.RowProcessor; import org.teiid.query.xquery.saxon.XQueryEvaluator; import org.teiid.translator.SourceSystemFunctions; import org.teiid.translator.WSConnection.Util; public class Evaluator { private final class XMLQueryRowProcessor implements RowProcessor { XmlConcat concat; //just used to get a writer Type type; private javax.xml.transform.Result result; boolean hasItem; private XMLQueryRowProcessor(boolean exists) throws TeiidProcessingException { if (!exists) { concat = new XmlConcat(context.getBufferManager()); result = new StreamResult(concat.getWriter()); } } @Override public void processRow(NodeInfo row) { if (concat == null) { hasItem = true; return; } if (type == null) { type = SaxonXQueryExpression.getType(row); } else { type = Type.CONTENT; } try { QueryResult.serialize(row, result, SaxonXQueryExpression.DEFAULT_OUTPUT_PROPERTIES); } catch (XPathException e) { throw new TeiidRuntimeException(e); } } } private final class SequenceReader extends Reader { private LinkedList<Reader> readers; private Reader current = null; public SequenceReader(LinkedList<Reader> readers) { this.readers = readers; } @Override public void close() throws IOException { for (Reader reader : readers) { try { reader.close(); } catch (IOException e) { } } } @Override public int read(char[] cbuf, int off, int len) throws IOException { if (current == null && !readers.isEmpty()) { current = readers.removeFirst(); } if (current == null) { return -1; } int read = current.read(cbuf, off, len); if (read == -1) { current.close(); current = null; read = 0; } if (read < len) { int nextRead = read(cbuf, off + read, len - read); if (nextRead > 0) { read += nextRead; } } return read; } } public static class NameValuePair<T> { public String name; public T value; public NameValuePair(String name, T value) { this.name = name; this.value = value; } } public final static char[] REGEX_RESERVED = new char[] {'$', '(', ')', '*', '+', '.', '?', '[', '\\', ']', '^', '{', '|', '}'}; //in sorted order private final static MatchCriteria.PatternTranslator LIKE_TO_REGEX = new MatchCriteria.PatternTranslator(new char[] {'%', '_'}, new String[] {".*", "."}, REGEX_RESERVED, '\\', Pattern.DOTALL); //$NON-NLS-1$ //$NON-NLS-2$ private final static char[] SIMILAR_REGEX_RESERVED = new char[] {'$', '.', '\\', '^'}; //in sorted order public final static MatchCriteria.PatternTranslator SIMILAR_TO_REGEX = new MatchCriteria.PatternTranslator( new char[] {'%', '(', ')', '*', '?', '+', '[', ']', '_', '{', '|', '}'}, new String[] {"([a]|[^a])*", "(", ")", "*", "?", "+", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ "[", "]", "([a]|[^a])", "{", "|", "}"}, SIMILAR_REGEX_RESERVED, '\\', 0); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ protected Map elements; protected ProcessorDataManager dataMgr; protected CommandContext context; public static boolean evaluate(Criteria criteria) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { return new Evaluator(Collections.emptyMap(), null, null).evaluate(criteria, Collections.emptyList()); } public static Object evaluate(Expression expression) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { return new Evaluator(Collections.emptyMap(), null, null).evaluate(expression, Collections.emptyList()); } public Evaluator(Map elements, ProcessorDataManager dataMgr, CommandContext context) { this.context = context; this.dataMgr = dataMgr; this.elements = elements; } public void initialize(CommandContext context, ProcessorDataManager dataMgr) { this.context = context; this.dataMgr = dataMgr; } public boolean evaluate(Criteria criteria, List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { return Boolean.TRUE.equals(evaluateTVL(criteria, tuple)); } public Boolean evaluateTVL(Criteria criteria, List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { return internalEvaluateTVL(criteria, tuple); } private Boolean internalEvaluateTVL(Criteria criteria, List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { if(criteria instanceof CompoundCriteria) { return evaluate((CompoundCriteria)criteria, tuple); } else if(criteria instanceof NotCriteria) { return evaluate((NotCriteria)criteria, tuple); } else if(criteria instanceof CompareCriteria) { return evaluate((CompareCriteria)criteria, tuple); } else if(criteria instanceof MatchCriteria) { return evaluate((MatchCriteria)criteria, tuple); } else if(criteria instanceof AbstractSetCriteria) { return evaluate((AbstractSetCriteria)criteria, tuple); } else if(criteria instanceof IsNullCriteria) { return Boolean.valueOf(evaluate((IsNullCriteria)criteria, tuple)); } else if(criteria instanceof SubqueryCompareCriteria) { return evaluate((SubqueryCompareCriteria)criteria, tuple); } else if(criteria instanceof ExistsCriteria) { return Boolean.valueOf(evaluate((ExistsCriteria)criteria, tuple)); } else if (criteria instanceof ExpressionCriteria) { return (Boolean)evaluate(((ExpressionCriteria)criteria).getExpression(), tuple); } else if (criteria instanceof XMLExists) { return (Boolean) evaluateXMLQuery(tuple, ((XMLExists)criteria).getXmlQuery(), true); } else if (criteria instanceof IsDistinctCriteria) { IsDistinctCriteria idc = (IsDistinctCriteria)criteria; TempMetadataID left = (TempMetadataID)idc.getLeftRowValue().getMetadataID(); TempMetadataID right = (TempMetadataID)idc.getRightRowValue().getMetadataID(); VariableContext vc = this.context.getVariableContext(); List<TempMetadataID> cols = left.getElements(); List<TempMetadataID> colsOther = right.getElements(); if (cols.size() != colsOther.size()) { return !idc.isNegated(); } for (int i = 0; i < cols.size(); i++) { Object l = vc.getValue(new ElementSymbol(cols.get(i).getName(), idc.getLeftRowValue())); Object r = vc.getValue(new ElementSymbol(colsOther.get(i).getName(), idc.getRightRowValue())); if (l == null) { if (r != null) { if (idc.isNegated()) { return false; } return true; } } else if (r == null) { if (idc.isNegated()) { return false; } return true; } try { Boolean b = compare(CompareCriteria.EQ, l, r); if (b == null) { continue; //shouldn't happen } if (!b) { if (idc.isNegated()) { return false; } return true; } } catch (Exception e) { //we'll consider this a difference //more than likely they are different types if (idc.isNegated()) { return false; } return true; } } if (idc.isNegated()) { return true; } return false; } else { throw new ExpressionEvaluationException(QueryPlugin.Event.TEIID30311, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30311, criteria)); } } private Boolean evaluate(CompoundCriteria criteria, List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { List<Criteria> subCrits = criteria.getCriteria(); boolean and = criteria.getOperator() == CompoundCriteria.AND; Boolean result = and?Boolean.TRUE:Boolean.FALSE; for (int i = 0; i < subCrits.size(); i++) { Criteria subCrit = subCrits.get(i); Boolean value = internalEvaluateTVL(subCrit, tuple); if (value == null) { result = null; } else if (!value.booleanValue()) { if (and) { return Boolean.FALSE; } } else if (!and) { return Boolean.TRUE; } } return result; } private Boolean evaluate(NotCriteria criteria, List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { Criteria subCrit = criteria.getCriteria(); Boolean result = internalEvaluateTVL(subCrit, tuple); if (result == null) { return null; } if (result.booleanValue()) { return Boolean.FALSE; } return Boolean.TRUE; } private Boolean evaluate(CompareCriteria criteria, List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { // Evaluate left expression Object leftValue = null; try { leftValue = evaluate(criteria.getLeftExpression(), tuple); } catch(ExpressionEvaluationException e) { throw new ExpressionEvaluationException(QueryPlugin.Event.TEIID30312, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30312, "left", criteria)); //$NON-NLS-1$ } // Shortcut if null if(leftValue == null) { return null; } // Evaluate right expression Object rightValue = null; try { rightValue = evaluate(criteria.getRightExpression(), tuple); } catch(ExpressionEvaluationException e) { throw new ExpressionEvaluationException(QueryPlugin.Event.TEIID30312, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30312, "right", criteria)); //$NON-NLS-1$ } // Shortcut if null if(rightValue == null) { return null; } // Compare two non-null values using specified operator return compare(criteria.getOperator(), leftValue, rightValue); } private Boolean evaluate(MatchCriteria criteria, List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { boolean result = false; // Evaluate left expression Object value = null; try { value = evaluate(criteria.getLeftExpression(), tuple); } catch(ExpressionEvaluationException e) { throw new ExpressionEvaluationException(QueryPlugin.Event.TEIID30312, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30312, "left", criteria)); //$NON-NLS-1$ } // Shortcut if null if(value == null) { return null; } CharSequence leftValue = null; if (value instanceof CharSequence) { leftValue = (CharSequence)value; } else { try { leftValue = ((Sequencable)value).getCharSequence(); } catch (SQLException err) { throw new ExpressionEvaluationException(QueryPlugin.Event.TEIID30316, err, err.getMessage()); } } // Evaluate right expression String rightValue = null; try { rightValue = (String) evaluate(criteria.getRightExpression(), tuple); } catch(ExpressionEvaluationException e) { throw new ExpressionEvaluationException(QueryPlugin.Event.TEIID30312, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30312, "right", criteria)); //$NON-NLS-1$ } // Shortcut if null if(rightValue == null) { return null; } result = match(rightValue, criteria.getEscapeChar(), leftValue, criteria.getMode()); return Boolean.valueOf(result ^ criteria.isNegated()); } private boolean match(String pattern, char escape, CharSequence search, MatchMode mode) throws ExpressionEvaluationException { Pattern patternRegex = null; switch (mode) { case LIKE: patternRegex = LIKE_TO_REGEX.translate(pattern, escape); break; case SIMILAR: patternRegex = SIMILAR_TO_REGEX.translate(pattern, escape); break; case REGEX: patternRegex = MatchCriteria.getPattern(pattern, pattern, 0); break; default: throw new AssertionError(); } Matcher matcher = patternRegex.matcher(search); return matcher.find(); } private Boolean evaluate(AbstractSetCriteria criteria, List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { // Evaluate expression Object leftValue = null; try { leftValue = evaluate(criteria.getExpression(), tuple); } catch(ExpressionEvaluationException e) { throw new ExpressionEvaluationException(QueryPlugin.Event.TEIID30323, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30323, criteria)); } Boolean result = Boolean.FALSE; ValueIterator valueIter = null; if (criteria instanceof SetCriteria) { SetCriteria set = (SetCriteria)criteria; // Shortcut if null if(leftValue == null) { if (!set.getValues().isEmpty()) { return null; } return criteria.isNegated(); } if (set.isAllConstants()) { boolean exists = set.getValues().contains(new Constant(leftValue, criteria.getExpression().getType())); if (!exists) { if (set.getValues().contains(Constant.NULL_CONSTANT)) { return null; } return criteria.isNegated(); } return !criteria.isNegated(); } valueIter = new CollectionValueIterator(((SetCriteria)criteria).getValues()); } else if (criteria instanceof DependentSetCriteria){ DependentSetCriteria ref = (DependentSetCriteria)criteria; VariableContext vc = getContext(criteria).getVariableContext(); ValueIteratorSource vis = (ValueIteratorSource)vc.getGlobalValue(ref.getContextSymbol()); if(leftValue == null) { return null; } Set<Object> values; try { values = vis.getCachedSet(ref.getValueExpression()); } catch (TeiidProcessingException e) { throw new ExpressionEvaluationException(e); } if (values != null) { return values.contains(leftValue); } vis.setUnused(true); //there are too many values to justify a linear search or holding //them in memory return true; } else if (criteria instanceof SubquerySetCriteria) { try { valueIter = evaluateSubquery((SubquerySetCriteria)criteria, tuple); } catch (TeiidProcessingException e) { throw new ExpressionEvaluationException(e); } } else { throw new AssertionError("unknown set criteria type"); //$NON-NLS-1$ } while(valueIter.hasNext()) { if(leftValue == null) { return null; } Object possibleValue = valueIter.next(); Object value = null; if(possibleValue instanceof Expression) { try { value = evaluate((Expression) possibleValue, tuple); } catch(ExpressionEvaluationException e) { throw new ExpressionEvaluationException(QueryPlugin.Event.TEIID30323, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30323, possibleValue)); } } else { value = possibleValue; } if(value != null) { if(Constant.COMPARATOR.compare(leftValue, value) == 0) { return Boolean.valueOf(!criteria.isNegated()); } // else try next value } else { result = null; } } if (result == null) { return null; } return Boolean.valueOf(criteria.isNegated()); } private boolean evaluate(IsNullCriteria criteria, List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { // Evaluate expression Object value = null; try { value = evaluate(criteria.getExpression(), tuple); } catch(ExpressionEvaluationException e) { throw new ExpressionEvaluationException(QueryPlugin.Event.TEIID30323, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30323, criteria)); } return (value == null ^ criteria.isNegated()); } private Boolean evaluate(SubqueryCompareCriteria criteria, List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { // Evaluate expression Object leftValue = null; try { leftValue = evaluate(criteria.getLeftExpression(), tuple); } catch(ExpressionEvaluationException e) { throw new ExpressionEvaluationException(QueryPlugin.Event.TEIID30323, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30323, criteria)); } // Need to be careful to initialize this variable carefully for the case // where valueIterator has no values, and the block below is not entered. // If there are no rows, and ALL is the predicate quantifier, the result // should be true. If SOME is the predicate quantifier, or no quantifier // is used, the result should be false. Boolean result = Boolean.FALSE; if (criteria.getPredicateQuantifier() == SubqueryCompareCriteria.ALL){ result = Boolean.TRUE; } ValueIterator valueIter; if (criteria.getCommand() != null) { try { valueIter = evaluateSubquery(criteria, tuple); } catch (TeiidProcessingException e) { throw new ExpressionEvaluationException(e); } } else { Object array = evaluate(criteria.getArrayExpression(), tuple); final Object[] vals; if (array instanceof Object[]) { vals = (Object[])array; } else { ArrayImpl arrayImpl = (ArrayImpl)array; vals = arrayImpl.getValues(); } valueIter = new ValueIterator() { int index = 0; @Override public void reset() { index = 0; } @Override public boolean hasNext() { return index < vals.length; } @Override public Object next() { if (!hasNext()) { throw new NoSuchElementException(); } return vals[index++]; } }; } while(valueIter.hasNext()) { Object value = valueIter.next(); // Shortcut if null if(leftValue == null) { return null; } if(value != null) { Boolean comp = compare(criteria.getOperator(), leftValue, value); switch(criteria.getPredicateQuantifier()) { case SubqueryCompareCriteria.ALL: if (Boolean.FALSE.equals(comp)){ return Boolean.FALSE; } break; case SubqueryCompareCriteria.SOME: if (Boolean.TRUE.equals(comp)){ return Boolean.TRUE; } break; default: throw new ExpressionEvaluationException(QueryPlugin.Event.TEIID30326, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30326, criteria.getPredicateQuantifier())); } } else { // value is null switch(criteria.getPredicateQuantifier()) { case SubqueryCompareCriteria.ALL: if (Boolean.TRUE.equals(result)){ result = null; } break; case SubqueryCompareCriteria.SOME: if (Boolean.FALSE.equals(result)){ result = null; } break; default: throw new ExpressionEvaluationException(QueryPlugin.Event.TEIID30326, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30326, criteria.getPredicateQuantifier())); } } } //end value iteration return result; } public static Boolean compare(int operator, Object leftValue, Object value) throws AssertionError { int compare = 0; //TODO: we follow oracle style array comparison //semantics. each element is treated as an individual comparison, //so null implies unknown. h2 (and likely other dbms) allow for null //array element equality if (leftValue instanceof ArrayImpl) { ArrayImpl av = (ArrayImpl)leftValue; try { compare = av.compareTo((ArrayImpl)value, true, Constant.COMPARATOR); } catch (ArrayImpl.NullException e) { return null; } } else { compare = Constant.COMPARATOR.compare(leftValue, value); } // Compare two non-null values using specified operator Boolean result = null; switch(operator) { case CompareCriteria.EQ: result = Boolean.valueOf(compare == 0); break; case CompareCriteria.NE: result = Boolean.valueOf(compare != 0); break; case CompareCriteria.LT: result = Boolean.valueOf(compare < 0); break; case CompareCriteria.LE: result = Boolean.valueOf(compare <= 0); break; case CompareCriteria.GT: result = Boolean.valueOf(compare > 0); break; case CompareCriteria.GE: result = Boolean.valueOf(compare >= 0); break; default: throw new AssertionError(); } return result; } private boolean evaluate(ExistsCriteria criteria, List<?> tuple) throws BlockedException, TeiidComponentException, ExpressionEvaluationException { ValueIterator valueIter; try { valueIter = evaluateSubquery(criteria, tuple); } catch (TeiidProcessingException e) { throw new ExpressionEvaluationException(e); } if(valueIter.hasNext()) { return !criteria.isNegated(); } return criteria.isNegated(); } public Object evaluate(Expression expression, List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { try { return internalEvaluate(expression, tuple); } catch (ExpressionEvaluationException e) { throw new ExpressionEvaluationException(QueryPlugin.Event.TEIID30328, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30328, new Object[] {expression, e.getMessage()})); } } protected Object internalEvaluate(Expression expression, List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { if(expression instanceof DerivedExpression) { if (elements != null) { // Try to evaluate by lookup in the elements map (may work for both ElementSymbol and ExpressionSymbol Integer index = (Integer) elements.get(expression); if(index != null) { return tuple.get(index.intValue()); } } // Otherwise this should be an ExpressionSymbol and we just need to dive in and evaluate the expression itself if (expression instanceof ExpressionSymbol) { ExpressionSymbol exprSyb = (ExpressionSymbol) expression; Expression expr = exprSyb.getExpression(); return internalEvaluate(expr, tuple); } return getContext(expression).getFromContext(expression); } if(expression instanceof Constant) { return ((Constant) expression).getValue(); } else if(expression instanceof Function) { return evaluate((Function) expression, tuple); } else if(expression instanceof CaseExpression) { return evaluate((CaseExpression) expression, tuple); } else if(expression instanceof SearchedCaseExpression) { return evaluate((SearchedCaseExpression) expression, tuple); } else if(expression instanceof Reference) { Reference ref = (Reference)expression; if (ref.isPositional() && ref.getExpression() == null) { return getContext(ref).getVariableContext().getGlobalValue(ref.getContextSymbol()); } Object result = getContext(ref.getExpression()).getFromContext(ref.getExpression()); if (ref.getConstraint() != null) { try { ref.getConstraint().validate(result); } catch (QueryValidatorException e) { throw new ExpressionEvaluationException(e); } } return result; } else if(expression instanceof Criteria) { return evaluate((Criteria) expression, tuple); } else if(expression instanceof ScalarSubquery) { return evaluate((ScalarSubquery) expression, tuple); } else if (expression instanceof Criteria) { return evaluate((Criteria)expression, tuple); } else if (expression instanceof TextLine){ return evaluateTextLine(tuple, (TextLine)expression); } else if (expression instanceof XMLElement){ return evaluateXMLElement(tuple, (XMLElement)expression); } else if (expression instanceof XMLForest){ return evaluateXMLForest(tuple, (XMLForest)expression); } else if (expression instanceof JSONObject){ return evaluateJSONObject(tuple, (JSONObject)expression, null); } else if (expression instanceof XMLSerialize){ return evaluateXMLSerialize(tuple, (XMLSerialize)expression); } else if (expression instanceof XMLQuery) { return evaluateXMLQuery(tuple, (XMLQuery)expression, false); } else if (expression instanceof QueryString) { return evaluateQueryString(tuple, (QueryString)expression); } else if (expression instanceof XMLParse){ return evaluateXMLParse(tuple, (XMLParse)expression); } else if (expression instanceof Array) { Array array = (Array)expression; List<Expression> exprs = array.getExpressions(); Object[] result = (Object[]) java.lang.reflect.Array.newInstance(array.getComponentType(), exprs.size()); for (int i = 0; i < exprs.size(); i++) { Object eval = internalEvaluate(exprs.get(i), tuple); if (eval instanceof ArrayImpl) { eval = ((ArrayImpl)eval).getValues(); } result[i] = eval; } return new ArrayImpl(result); } else if (expression instanceof ExceptionExpression) { return evaluate(tuple, (ExceptionExpression)expression); } else if (expression instanceof XMLCast) { return evaluate(tuple, (XMLCast)expression); } else { throw new TeiidComponentException(QueryPlugin.Event.TEIID30329, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30329, expression.getClass().getName())); } } private Object evaluate(List<?> tuple, XMLCast expression) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { Object val = internalEvaluate(expression.getExpression(), tuple); if (val == null) { return new Constant(null, expression.getType()); } Configuration config = new Configuration(); XMLType value = (XMLType)val; Type t = value.getType(); try { Item i = null; switch (t) { case CONTENT: //content could map to an array value, but we aren't handling that case here yet - only in xmltable case COMMENT: case PI: throw new FunctionExecutionException(); case TEXT: i = new StringValue(value.getString()); break; case UNKNOWN: case DOCUMENT: case ELEMENT: StreamSource ss = value.getSource(StreamSource.class); try { i = config.buildDocument(ss); } finally { if (ss.getInputStream() != null) { ss.getInputStream().close(); } if (ss.getReader() != null) { ss.getReader().close(); } } break; default: throw new AssertionError("Unknown xml value type " + t); //$NON-NLS-1$ } return XMLTableNode.getValue(expression.getType(), i, config, context); } catch (IOException e) { throw new FunctionExecutionException(e); } catch (ValidationException e) { throw new FunctionExecutionException(e); } catch (TransformationException e) { throw new FunctionExecutionException(e); } catch (XPathException e) { throw new FunctionExecutionException(e); } catch (SQLException e) { throw new FunctionExecutionException(e); } } private Object evaluate(List<?> tuple, ExceptionExpression ee) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { String msg = (String) internalEvaluate(ee.getMessage(), tuple); String sqlState = ee.getDefaultSQLState(); if (ee.getSqlState() != null) { sqlState = (String) internalEvaluate(ee.getSqlState(), tuple); } Integer errorCode = null; if (ee.getErrorCode() != null) { errorCode = (Integer) internalEvaluate(ee.getErrorCode(), tuple); } Exception parent = null; if (ee.getParent() != null) { parent = (Exception) internalEvaluate(ee.getParent(), tuple); } Exception result = new TeiidSQLException(parent, msg, sqlState, errorCode!=null?errorCode:0); result.setStackTrace(SourceWarning.EMPTY_STACK_TRACE); return result; } private Object evaluateXMLParse(List<?> tuple, final XMLParse xp) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { Object value = internalEvaluate(xp.getExpression(), tuple); if (value == null) { return null; } XMLType.Type type = Type.DOCUMENT; SQLXMLImpl result = null; try { if (value instanceof String) { String string = (String)value; result = new SQLXMLImpl(string); result.setCharset(Streamable.CHARSET); if (!xp.isWellFormed()) { Reader r = new StringReader(string); type = validate(xp, r); } } else if (value instanceof BinaryType) { BinaryType string = (BinaryType)value; result = new SQLXMLImpl(string.getBytesDirect()); result.setCharset(Streamable.CHARSET); if (!xp.isWellFormed()) { Reader r = result.getCharacterStream(); type = validate(xp, r); } } else { InputStreamFactory isf = null; Streamable<?> s = (Streamable<?>)value; isf = getInputStreamFactory(s); result = new SQLXMLImpl(isf); if (!xp.isWellFormed()) { Reader r = result.getCharacterStream(); type = validate(xp, r); } } } catch (TransformationException e) { throw new ExpressionEvaluationException(e); } catch (SQLException e) { throw new ExpressionEvaluationException(QueryPlugin.Event.TEIID30331, e, e.getMessage()); } if (!xp.isDocument()) { type = Type.CONTENT; } XMLType xml = new XMLType(result); xml.setType(type); return xml; } public static InputStreamFactory getInputStreamFactory(Streamable<?> s) { if (s.getReference() instanceof Streamable<?>) { return getInputStreamFactory((Streamable<?>) s.getReference()); } if (s.getReference() instanceof BaseLob) { BaseLob bl = (BaseLob) s.getReference(); try { InputStreamFactory isf = bl.getStreamFactory(); if (isf != null) { return isf; } } catch (SQLException e) { } } if (s instanceof ClobType) { return new InputStreamFactory.ClobInputStreamFactory((Clob)s.getReference()); } else if (s instanceof BlobType){ return new InputStreamFactory.BlobInputStreamFactory((Blob)s.getReference()); } return new InputStreamFactory.SQLXMLInputStreamFactory((SQLXML)s.getReference()); } private Type validate(final XMLParse xp, Reader r) throws TransformationException { if (!xp.isDocument()) { LinkedList<Reader> readers = new LinkedList<Reader>(); readers.add(new StringReader("<r>")); //$NON-NLS-1$ readers.add(r); readers.add(new StringReader("</r>")); //$NON-NLS-1$ r = new SequenceReader(readers); } return StringToSQLXMLTransform.isXml(r); } //TODO: exception if length is too long? private Object evaluateQueryString(List<?> tuple, QueryString queryString) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { Evaluator.NameValuePair<Object>[] pairs = getNameValuePairs(tuple, queryString.getArgs(), false, true); String path = (String)internalEvaluate(queryString.getPath(), tuple); if (path == null) { path = ""; //$NON-NLS-1$ } boolean appendedAny = false; StringBuilder result = new StringBuilder(); for (Evaluator.NameValuePair<Object> nameValuePair : pairs) { if (nameValuePair.value == null) { continue; } if (appendedAny) { result.append('&'); } appendedAny = true; result.append(Util.httpURLEncode(nameValuePair.name)).append('=').append(Util.httpURLEncode((String)nameValuePair.value)); } if (!appendedAny) { return path; } result.insert(0, '?'); result.insert(0, path); return result.toString(); } /** * * @param tuple * @param xmlQuery * @param exists - check only for the existence of a non-empty result * @return Boolean if exists is true, otherwise an XMLType value * @throws BlockedException * @throws TeiidComponentException * @throws FunctionExecutionException */ private Object evaluateXMLQuery(List<?> tuple, XMLQuery xmlQuery, boolean exists) throws BlockedException, TeiidComponentException, FunctionExecutionException { boolean emptyOnEmpty = xmlQuery.getEmptyOnEmpty() == null || xmlQuery.getEmptyOnEmpty(); Result result = null; try { XMLQueryRowProcessor rp = null; if (xmlQuery.getXQueryExpression().isStreaming()) { rp = new XMLQueryRowProcessor(exists); } try { result = evaluateXQuery(xmlQuery.getXQueryExpression(), xmlQuery.getPassing(), tuple, rp); if (result == null) { return null; } if (exists) { if (result.iter.next() == null) { return false; } return true; } } catch (TeiidRuntimeException e) { if (e.getCause() instanceof XPathException) { throw (XPathException)e.getCause(); } throw e; } if (rp != null) { if (exists) { return rp.hasItem; } XMLType.Type type = rp.type; if (type == null) { if (!emptyOnEmpty) { return null; } type = Type.CONTENT; } XMLType val = rp.concat.close(context); val.setType(rp.type); return val; } return xmlQuery.getXQueryExpression().createXMLType(result.iter, this.context.getBufferManager(), emptyOnEmpty, context); } catch (TeiidProcessingException e) { throw new FunctionExecutionException(QueryPlugin.Event.TEIID30333, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30333, e.getMessage())); } catch (XPathException e) { throw new FunctionExecutionException(QueryPlugin.Event.TEIID30333, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30333, e.getMessage())); } finally { if (result != null) { result.close(); } } } private Object evaluateXMLSerialize(List<?> tuple, XMLSerialize xs) throws ExpressionEvaluationException, BlockedException, TeiidComponentException, FunctionExecutionException { XMLType value = (XMLType) internalEvaluate(xs.getExpression(), tuple); if (value == null) { return null; } try { if (!xs.isDocument()) { return XMLSystemFunctions.serialize(xs, value); } if (value.getType() == Type.UNKNOWN) { Type type = StringToSQLXMLTransform.isXml(value.getCharacterStream()); value.setType(type); } if (value.getType() == Type.DOCUMENT || value.getType() == Type.ELEMENT) { return XMLSystemFunctions.serialize(xs, value); } } catch (SQLException e) { throw new FunctionExecutionException(QueryPlugin.Event.TEIID30334, e); } catch (TransformationException e) { throw new FunctionExecutionException(QueryPlugin.Event.TEIID30335, e); } throw new FunctionExecutionException(QueryPlugin.Event.TEIID30336, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30336)); } private static TextLine.ValueExtractor<NameValuePair<Object>> defaultExtractor = new TextLine.ValueExtractor<NameValuePair<Object>>() { public Object getValue(NameValuePair<Object> t) { return t.value; } }; private Object evaluateTextLine(List<?> tuple, TextLine function) throws ExpressionEvaluationException, BlockedException, TeiidComponentException, FunctionExecutionException { List<DerivedColumn> args = function.getExpressions(); Evaluator.NameValuePair<Object>[] nameValuePairs = getNameValuePairs(tuple, args, true, true); try { return new ArrayImpl(TextLine.evaluate(Arrays.asList(nameValuePairs), defaultExtractor, function)); } catch (TransformationException e) { throw new ExpressionEvaluationException(e); } catch (TeiidProcessingException e) { throw new ExpressionEvaluationException(e); } } private Object evaluateXMLForest(List<?> tuple, XMLForest function) throws ExpressionEvaluationException, BlockedException, TeiidComponentException, FunctionExecutionException { List<DerivedColumn> args = function.getArgs(); Evaluator.NameValuePair<Object>[] nameValuePairs = getNameValuePairs(tuple, args, true, true); try { return XMLSystemFunctions.xmlForest(context, namespaces(function.getNamespaces()), nameValuePairs); } catch (TeiidProcessingException e) { throw new FunctionExecutionException(e); } } private Object evaluateJSONObject(List<?> tuple, JSONObject function, JSONBuilder builder) throws ExpressionEvaluationException, BlockedException, TeiidComponentException, FunctionExecutionException { List<DerivedColumn> args = function.getArgs(); Evaluator.NameValuePair<Object>[] nameValuePairs = getNameValuePairs(tuple, args, false, false); boolean returnValue = false; try { if (builder == null) { returnValue = true; //preevaluate subqueries to prevent blocked exceptions for (SubqueryContainer<?> container : ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(function)) { evaluateSubquery(container, tuple); } builder = new JSONBuilder(context.getBufferManager()); } builder.start(false); for (NameValuePair<Object> nameValuePair : nameValuePairs) { addValue(tuple, builder, nameValuePair.name, nameValuePair.value); } builder.end(false); if (returnValue) { ClobType result = builder.close(context); builder = null; return result; } return null; } catch (TeiidProcessingException e) { throw new FunctionExecutionException(e); } finally { if (returnValue && builder != null) { builder.remove(); } } } private void addValue(List<?> tuple, JSONBuilder builder, String name, Object value) throws TeiidProcessingException, ExpressionEvaluationException, BlockedException, TeiidComponentException, FunctionExecutionException { try { if (value instanceof JSONObject) { builder.startValue(name); evaluateJSONObject(tuple, (JSONObject)value, builder); return; } if (value instanceof Function) { Function f = (Function)value; if (f.getName().equalsIgnoreCase(FunctionLibrary.JSONARRAY)) { builder.startValue(name); jsonArray(context, f, f.getArgs(), builder, this, tuple); return; } } builder.addValue(name, internalEvaluate((Expression)value, tuple)); } catch (BlockedException e) { throw e; } } public static ClobType jsonArray(CommandContext context, Function f, Object[] vals, JSONBuilder builder, Evaluator eval, List<?> tuple) throws TeiidProcessingException, BlockedException, TeiidComponentException { boolean returnValue = false; try { if (builder == null) { returnValue = true; if (eval != null) { //preevaluate subqueries to prevent blocked exceptions for (SubqueryContainer<?> container : ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(f)) { eval.evaluateSubquery(container, tuple); } } builder = new JSONBuilder(context.getBufferManager()); } builder.start(true); for (Object object : vals) { if (eval != null) { eval.addValue(tuple, builder, null, object); } else { builder.addValue(object); } } builder.end(true); if (returnValue) { ClobType result = builder.close(context); builder = null; return result; } return null; } finally { if (returnValue && builder != null) { builder.remove(); } } } private Object evaluateXMLElement(List<?> tuple, XMLElement function) throws ExpressionEvaluationException, BlockedException, TeiidComponentException, FunctionExecutionException { List<Expression> content = function.getContent(); List<Object> values = new ArrayList<Object>(content.size()); for (Expression exp : content) { values.add(internalEvaluate(exp, tuple)); } try { Evaluator.NameValuePair<Object>[] attributes = null; if (function.getAttributes() != null) { attributes = getNameValuePairs(tuple, function.getAttributes().getArgs(), true, true); } return XMLSystemFunctions.xmlElement(context, function.getName(), namespaces(function.getNamespaces()), attributes, values); } catch (TeiidProcessingException e) { throw new FunctionExecutionException(e); } } private Result evaluateXQuery(SaxonXQueryExpression xquery, List<DerivedColumn> cols, List<?> tuple, RowProcessor processor) throws BlockedException, TeiidComponentException, TeiidProcessingException { Map<String, Object> parameters = new HashMap<String, Object>(); evaluateParameters(cols, tuple, parameters); Object contextItem = null; if (parameters.containsKey(null)) { contextItem = parameters.remove(null); if (contextItem == null) { return null; } } return XQueryEvaluator.evaluateXQuery(xquery, contextItem, parameters, processor, context); } /** * Evaluate the parameters and return the context item if it exists */ public void evaluateParameters(List<DerivedColumn> cols, List<?> tuple, Map<String, Object> parameters) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { for (DerivedColumn passing : cols) { Object value = evaluateParameter(tuple, passing); if (passing.getAlias() == null) { parameters.put(null, value); } else { parameters.put(passing.getAlias(), value); } } } private Object evaluateParameter(List<?> tuple, DerivedColumn passing) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { if (passing.getExpression() instanceof Function) { Function f = (Function)passing.getExpression(); //narrow optimization of json based documents to allow for lower overhead streaming if (f.getName().equalsIgnoreCase(SourceSystemFunctions.JSONTOXML)) { String rootName = (String)this.evaluate(f.getArg(0), tuple); Object lob = this.evaluate(f.getArg(1), tuple); if (rootName == null || lob == null) { return null; } try { if (lob instanceof Blob) { return XMLSystemFunctions.jsonToXml(context, rootName, (Blob)lob, true); } return XMLSystemFunctions.jsonToXml(context, rootName, (Clob)lob, true); } catch (IOException e) { throw new FunctionExecutionException(QueryPlugin.Event.TEIID30384, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30384, f.getFunctionDescriptor().getName())); } catch (SQLException e) { throw new FunctionExecutionException(QueryPlugin.Event.TEIID30384, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30384, f.getFunctionDescriptor().getName())); } catch (TeiidProcessingException e) { throw new FunctionExecutionException(QueryPlugin.Event.TEIID30384, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30384, f.getFunctionDescriptor().getName())); } } } else if (passing.getExpression() instanceof XMLParse) { XMLParse xmlParse = (XMLParse)passing.getExpression(); xmlParse.setWellFormed(true); } Object value = this.evaluate(passing.getExpression(), tuple); return value; } private Evaluator.NameValuePair<Object>[] getNameValuePairs(List<?> tuple, List<DerivedColumn> args, boolean xmlNames, boolean eval) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { Evaluator.NameValuePair<Object>[] nameValuePairs = new Evaluator.NameValuePair[args.size()]; for (int i = 0; i < args.size(); i++) { DerivedColumn symbol = args.get(i); String name = symbol.getAlias(); Expression ex = symbol.getExpression(); if (name == null && ex instanceof ElementSymbol) { name = ((ElementSymbol)ex).getShortName(); } if (name != null) { if (xmlNames) { name = XMLSystemFunctions.escapeName(name, true); } } else if (!xmlNames) { name = "expr" + (i+1); //$NON-NLS-1$ } nameValuePairs[i] = new Evaluator.NameValuePair<Object>(name, eval?internalEvaluate(ex, tuple):ex); } return nameValuePairs; } private Evaluator.NameValuePair<String>[] namespaces(XMLNamespaces namespaces) { if (namespaces == null) { return null; } List<NamespaceItem> args = namespaces.getNamespaceItems(); Evaluator.NameValuePair<String>[] nameValuePairs = new Evaluator.NameValuePair[args.size()]; for(int i=0; i < args.size(); i++) { NamespaceItem item = args.get(i); nameValuePairs[i] = new Evaluator.NameValuePair<String>(item.getPrefix(), item.getUri()); } return nameValuePairs; } private Object evaluate(CaseExpression expr, List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { Object exprVal = internalEvaluate(expr.getExpression(), tuple); for (int i = 0; i < expr.getWhenCount(); i++) { if (EquivalenceUtil.areEqual(exprVal, internalEvaluate(expr.getWhenExpression(i), tuple))) { return internalEvaluate(expr.getThenExpression(i), tuple); } } if (expr.getElseExpression() != null) { return internalEvaluate(expr.getElseExpression(), tuple); } return null; } private Object evaluate(SearchedCaseExpression expr, List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { for (int i = 0; i < expr.getWhenCount(); i++) { if (evaluate(expr.getWhenCriteria(i), tuple)) { return internalEvaluate(expr.getThenExpression(i), tuple); } } if (expr.getElseExpression() != null) { return internalEvaluate(expr.getElseExpression(), tuple); } return null; } private Object evaluate(Function function, List<?> tuple) throws BlockedException, TeiidComponentException, ExpressionEvaluationException { // Get function based on resolved function info FunctionDescriptor fd = function.getFunctionDescriptor(); // Evaluate args Expression[] args = function.getArgs(); Object[] values = null; int start = 0; if (fd.requiresContext()) { values = new Object[args.length+1]; values[0] = context; start = 1; } else { values = new Object[args.length]; } for(int i=0; i < args.length; i++) { values[i+start] = internalEvaluate(args[i], tuple); } if (fd.getPushdown() == PushDown.MUST_PUSHDOWN) { try { return evaluatePushdown(function, tuple, values); } catch (TeiidProcessingException e) { throw new ExpressionEvaluationException(e); } } if (fd.getProcedure() != null) { try { return evaluateProcedure(function, tuple, values); } catch (TeiidProcessingException e) { throw new ExpressionEvaluationException(e); } } // Check for special lookup function if(function.getName().equalsIgnoreCase(FunctionLibrary.LOOKUP)) { if(dataMgr == null) { throw new ComponentNotFoundException(QueryPlugin.Event.TEIID30342, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30342)); } String codeTableName = (String) values[0]; String returnElementName = (String) values[1]; String keyElementName = (String) values[2]; try { return dataMgr.lookupCodeValue(context, codeTableName, returnElementName, keyElementName, values[3]); } catch (TeiidProcessingException e) { throw new ExpressionEvaluationException(e); } } // Execute function return fd.invokeFunction(values, context, null); } protected Object evaluatePushdown(Function function, List<?> tuple, Object[] values) throws FunctionExecutionException, TeiidComponentException, TeiidProcessingException { throw new FunctionExecutionException(QueryPlugin.Event.TEIID30341, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30341, function.getFunctionDescriptor().getFullName())); } private Object evaluate(ScalarSubquery scalarSubquery, List<?> tuple) throws ExpressionEvaluationException, BlockedException, TeiidComponentException { Object result = null; ValueIterator valueIter; try { valueIter = evaluateSubquery(scalarSubquery, tuple); } catch (TeiidProcessingException e) { throw new ExpressionEvaluationException(e); } if(valueIter.hasNext()) { result = valueIter.next(); if(valueIter.hasNext()) { // The subquery should be scalar, but has produced // more than one result value - this is an exception case throw new ExpressionEvaluationException(QueryPlugin.Event.TEIID30345, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30345, scalarSubquery.getCommand())); } } return result; } /** * @param container * @param tuple * @return * @throws TeiidProcessingException * @throws BlockedException * @throws TeiidComponentException */ protected ValueIterator evaluateSubquery(SubqueryContainer<?> container, List<?> tuple) throws TeiidProcessingException, BlockedException, TeiidComponentException { throw new UnsupportedOperationException("Subquery evaluation not possible with a base Evaluator"); //$NON-NLS-1$ } private CommandContext getContext(LanguageObject expression) throws TeiidComponentException { if (context == null) { throw new TeiidComponentException(QueryPlugin.Event.TEIID30328, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30328, expression, QueryPlugin.Util.getString("Evaluator.no_value"))); //$NON-NLS-1$ } return context; } protected Object evaluateProcedure(Function function, List<?> tuple, Object[] values) throws TeiidComponentException, TeiidProcessingException { throw new UnsupportedOperationException("Procedure evaluation not possible with a base Evaluator"); //$NON-NLS-1$ } }