/* * 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.translator.jdbc; import java.util.Arrays; import java.util.List; import org.teiid.language.Comparison; import org.teiid.language.Expression; import org.teiid.language.Function; import org.teiid.language.LanguageFactory; import org.teiid.language.Literal; import org.teiid.language.Comparison.Operator; import org.teiid.translator.TypeFacility; /** * A modifier class that can be used to translate the scalar function * <code>locate(search_string, source_string)</code> and * <code>locate(search_string, source_string, start_index)</code> to a function * or expression that can be used at the data source. * <p> * If the default implementation is used, a function name of LOCATE will be used * for the function name. * <p> * If the default implementation is used, the expression will not be modified if: * <li><code>locate(search_string, source_string)</code> is used</li> * <li><code>locate(search_string, source_string, start_index)</code> is used * and <code>start_index</code> is a literal integer greater then 0</li> * <li>the default function parameter order is used or unspecified</li> * <p> * If the default implementation is used, the expression will be modified if: * <li><code>locate(search_string, source_string, start_index)</code> is used * and <code>start_index</code> is a literal integer less then 1</li> * <li><code>locate(search_string, source_string, start_index)</code> is used * and <code>start_index</code> is not a literal integer</li> * <li>the function parameter order is something other than the default</li> * <p> * If the default implementation is used and the expression is modified, it is * modified to ensure that any literal integer value less than 1 is made equal * to 1 and any non literal value is wrapped by a searched case expression * to ensure that a value of less then 1 will be equal to 1 and the parameter * order matches that of what the data source expects. * <p> * For example: * <li><code>locate('a', 'abcdef')</code> --> <code>LOCATE('a', 'abcdef')</code></li> * <li><code>locate('a', 'abcdef', 2)</code> --> <code>LOCATE('a', 'abcdef', 2)</code></li> * <li><code>locate('a', 'abcdef', 0)</code> --> <code>LOCATE('a', 'abcdef', 1)</code></li> * <li><code>locate('a', 'abcdef', intCol)</code> --> <code>LOCATE('a', 'abcdef', CASE WHEN intCol < 1 THEN 1 ELSE intCol END)</code></li> * * @since 6.2 */ public class LocateFunctionModifier extends AliasModifier { public static String LOCATE = "LOCATE"; //$NON-NLS-1$ private LanguageFactory langFactory; private boolean sourceStringFirst; /** * Translates the scalar function LOCATE() to a source specific scalar * function or expression. * * @param langFactory the language factory associated with translation */ public LocateFunctionModifier(LanguageFactory langFactory) { this(langFactory, LOCATE, false); } /** * Translates the scalar function LOCATE() to a source specific scalar * function or expression. * * @param langFactory the language factory associated with translation * @param functionName the function name or alias to be used instead of LOCATE * @param sourceStringFirst */ public LocateFunctionModifier(LanguageFactory langFactory, final String functionName, boolean sourceStringFirst) { super(functionName); this.langFactory = langFactory; this.sourceStringFirst = sourceStringFirst; } /** * Returns a version of <code>function</code> suitable for executing at the * data source. * <p> * For example: * <ul> * <code>locate('a', 'abcdefg') ---> LOCATE('a', 'abcdefg')</code><br /> * <code>locate('a', 'abcdefg', 1) ---> LOCATE('a', 'abcdefg', 1)</code><br /> * <code>locate('a', 'abcdefg', 1) ---> INSTR('abcdefg', 'a', 1)</code><br /> * <code>locate('a', 'abcdefg', -5) ---> INSTR('abcdefg', 'a', 1)</code><br /> * <code>locate('a', 'abcdefg', 1) ---> FINDSTR('a', 'abcdefg', 1)</code><br /> * <code>locate('a', 'abcdefg', myCol) ---> LOCATE('a', 'abcdefg', CASE WHEN myCol < 1 THEN 1 ELSE myCol END)</code> * </ul> * * @param function the LOCATE function that may need to be modified */ public void modify(Function function) { super.modify(function); List<Expression> args = function.getParameters(); Expression searchStr = args.get(0); Expression sourceStr = args.get(1); // if startIndex was given then we may need to do additional work if (args.size() > 2) { args.set(2, ensurePositiveStartIndex(args.get(2))); } if (sourceStringFirst) { args.set(0, sourceStr); args.set(1, searchStr); } } private Expression ensurePositiveStartIndex(Expression startIndex) { if (startIndex instanceof Literal) { Literal literal = (Literal)startIndex; if (literal.getValue() instanceof Integer && ((Integer)literal.getValue() < 1)) { literal.setValue(1); } } else { Comparison whenExpr = langFactory.createCompareCriteria( Operator.LT, startIndex, langFactory.createLiteral(1, Integer.class) ); Literal thenExpr = langFactory.createLiteral(1, Integer.class); startIndex = langFactory.createSearchedCaseExpression(Arrays.asList(langFactory.createSearchedWhenCondition(whenExpr, thenExpr)), startIndex, TypeFacility.RUNTIME_TYPES.INTEGER); } return startIndex; } /** * Get the instance of {@link LanguageFactory} set during construction. * * @return the <code>ILanguageFactory</code> instance */ protected LanguageFactory getLanguageFactory() { return this.langFactory; } }