/* * 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.teradata; import static org.teiid.translator.TypeFacility.RUNTIME_NAMES.*; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.teiid.language.*; import org.teiid.translator.ExecutionContext; import org.teiid.translator.SourceSystemFunctions; import org.teiid.translator.Translator; import org.teiid.translator.TranslatorException; import org.teiid.translator.TypeFacility; import org.teiid.translator.jdbc.AliasModifier; import org.teiid.translator.jdbc.ConvertModifier; import org.teiid.translator.jdbc.FunctionModifier; import org.teiid.translator.jdbc.JDBCExecutionFactory; import org.teiid.translator.jdbc.SQLConversionVisitor; /** * Teradata database Release 12 */ @Translator(name="teradata", description="A translator for Teradata Database") public class TeradataExecutionFactory extends JDBCExecutionFactory { public static String TERADATA = "teradata"; //$NON-NLS-1$ protected ConvertModifier convert = new ConvertModifier(); public TeradataExecutionFactory() { setMaxDependentInPredicates(5); //teradata documentation does not make it clear that there is a hard limit. this value comes from hibernate setMaxInCriteriaSize(1024); } @Override public void start() throws TranslatorException { super.start(); convert.addTypeMapping("byteint", FunctionModifier.BYTE, FunctionModifier.SHORT, FunctionModifier.BOOLEAN); //$NON-NLS-1$ convert.addTypeMapping("double precision", FunctionModifier.DOUBLE); //$NON-NLS-1$ convert.addTypeMapping("numeric(18,0)", FunctionModifier.BIGINTEGER); //$NON-NLS-1$ convert.addTypeMapping("char(1)", FunctionModifier.CHAR); //$NON-NLS-1$ convert.addConvert(FunctionModifier.TIMESTAMP, FunctionModifier.TIME, new CastModifier("TIME")); //$NON-NLS-1$ convert.addConvert(FunctionModifier.TIMESTAMP, FunctionModifier.DATE, new CastModifier("DATE")); //$NON-NLS-1$ convert.addConvert(FunctionModifier.TIME, FunctionModifier.TIMESTAMP, new CastModifier("TIMESTAMP")); //$NON-NLS-1$ convert.addConvert(FunctionModifier.DATE, FunctionModifier.TIMESTAMP, new CastModifier("TIMESTAMP")); //$NON-NLS-1$ convert.addConvert(FunctionModifier.STRING, FunctionModifier.INTEGER, new CastModifier("integer")); //$NON-NLS-1$ convert.addConvert(FunctionModifier.STRING, FunctionModifier.BIGDECIMAL, new CastModifier("decimal(37,5)"));//$NON-NLS-1$ convert.addConvert(FunctionModifier.STRING, FunctionModifier.BIGINTEGER, new CastModifier("numeric(18,0)"));//$NON-NLS-1$ convert.addConvert(FunctionModifier.STRING, FunctionModifier.FLOAT, new CastModifier("float"));//$NON-NLS-1$ convert.addConvert(FunctionModifier.STRING, FunctionModifier.BOOLEAN, new CastModifier("byteint"));//$NON-NLS-1$ convert.addConvert(FunctionModifier.STRING, FunctionModifier.LONG, new CastModifier("numeric(18,0)"));//$NON-NLS-1$ convert.addConvert(FunctionModifier.STRING, FunctionModifier.SHORT, new CastModifier("smallint"));//$NON-NLS-1$ convert.addConvert(FunctionModifier.STRING, FunctionModifier.DOUBLE, new CastModifier("double precision"));//$NON-NLS-1$ convert.addConvert(FunctionModifier.STRING, FunctionModifier.BYTE, new CastModifier("byteint")); //$NON-NLS-1$ convert.addConvert(FunctionModifier.TIMESTAMP, FunctionModifier.STRING, new FunctionModifier() { @Override public List<?> translate(Function function) { return Arrays.asList("cast(cast(", function.getParameters().get(0), " AS FORMAT 'Y4-MM-DDBHH:MI:SSDS(6)') AS VARCHAR(26))"); //$NON-NLS-1$ //$NON-NLS-2$ } }); convert.addConvert(FunctionModifier.TIME, FunctionModifier.STRING, new FunctionModifier() { @Override public List<?> translate(Function function) { return Arrays.asList("cast(cast(", function.getParameters().get(0), " AS FORMAT 'HH:MI:SS') AS VARCHAR(9))"); //$NON-NLS-1$ //$NON-NLS-2$ } }); convert.addConvert(FunctionModifier.DATE, FunctionModifier.STRING, new FunctionModifier() { @Override public List<?> translate(Function function) { return Arrays.asList("cast(cast(", function.getParameters().get(0), " AS FORMAT 'YYYY-MM-DD') AS VARCHAR(11))"); //$NON-NLS-1$ //$NON-NLS-2$ } }); convert.addTypeMapping("varchar(4000)", FunctionModifier.STRING); //$NON-NLS-1$ convert.addNumericBooleanConversions(); registerFunctionModifier(SourceSystemFunctions.CONVERT, convert); registerFunctionModifier(SourceSystemFunctions.SUBSTRING, new AliasModifier("substr")); //$NON-NLS-1$ registerFunctionModifier(SourceSystemFunctions.LOG, new AliasModifier("LN")); //$NON-NLS-1$ registerFunctionModifier(SourceSystemFunctions.LCASE, new AliasModifier("LOWER")); //$NON-NLS-1$ registerFunctionModifier(SourceSystemFunctions.UCASE, new AliasModifier("UPPER")); //$NON-NLS-1$ registerFunctionModifier(SourceSystemFunctions.LENGTH, new AliasModifier("character_length")); //$NON-NLS-1$ registerFunctionModifier(SourceSystemFunctions.CURDATE, new AliasModifier("CURRENT_DATE")); //$NON-NLS-1$ registerFunctionModifier(SourceSystemFunctions.CURTIME, new AliasModifier("CURRENT_TIME")); //$NON-NLS-1$ registerFunctionModifier(SourceSystemFunctions.YEAR, new ExtractModifier("YEAR")); //$NON-NLS-1$ registerFunctionModifier(SourceSystemFunctions.MONTH, new ExtractModifier("MONTH")); //$NON-NLS-1$ registerFunctionModifier(SourceSystemFunctions.DAYOFMONTH, new ExtractModifier("DAY")); //$NON-NLS-1$ registerFunctionModifier(SourceSystemFunctions.HOUR, new ExtractModifier("HOUR")); //$NON-NLS-1$ registerFunctionModifier(SourceSystemFunctions.MINUTE, new ExtractModifier("MINUTE")); //$NON-NLS-1$ registerFunctionModifier(SourceSystemFunctions.SECOND, new ExtractModifier("SECOND")); //$NON-NLS-1$ registerFunctionModifier(SourceSystemFunctions.LOCATE, new LocateModifier(this.convert)); registerFunctionModifier(SourceSystemFunctions.LEFT, new LeftOrRightFunctionModifier(getLanguageFactory(), this.convert)); registerFunctionModifier(SourceSystemFunctions.RIGHT, new LeftOrRightFunctionModifier(getLanguageFactory(), this.convert)); registerFunctionModifier(SourceSystemFunctions.COT, new FunctionModifier() { @Override public List<?> translate(Function function) { function.setName(SourceSystemFunctions.TAN); return Arrays.asList(getLanguageFactory().createFunction(SourceSystemFunctions.DIVIDE_OP, new Expression[] {new Literal(1, TypeFacility.RUNTIME_TYPES.INTEGER), function}, TypeFacility.RUNTIME_TYPES.DOUBLE)); } }); registerFunctionModifier(SourceSystemFunctions.LTRIM, new FunctionModifier() { @Override public List<?> translate(Function function) { ArrayList<Object> target = new ArrayList<Object>(); target.add("TRIM(LEADING FROM ");//$NON-NLS-1$ target.add(function.getParameters().get(0)); target.add(")"); //$NON-NLS-1$ return target; } }); registerFunctionModifier(SourceSystemFunctions.RTRIM, new FunctionModifier() { @Override public List<?> translate(Function function) { ArrayList<Object> target = new ArrayList<Object>(); target.add("TRIM(TRAILING FROM ");//$NON-NLS-1$ target.add(function.getParameters().get(0)); target.add(")"); //$NON-NLS-1$ return target; } }); registerFunctionModifier(SourceSystemFunctions.MOD, new FunctionModifier() { @Override public List<?> translate(Function function) { return Arrays.asList(function.getParameters().get(0), " MOD ", function.getParameters().get(1)); //$NON-NLS-1$ } }); addPushDownFunction(TERADATA, "COSH", FLOAT, FLOAT); //$NON-NLS-1$ addPushDownFunction(TERADATA, "TANH", FLOAT, FLOAT); //$NON-NLS-1$ addPushDownFunction(TERADATA, "ACOSH", FLOAT, FLOAT); //$NON-NLS-1$ addPushDownFunction(TERADATA, "ASINH", FLOAT, FLOAT); //$NON-NLS-1$ addPushDownFunction(TERADATA, "ATANH", FLOAT, FLOAT); //$NON-NLS-1$ addPushDownFunction(TERADATA, "CHAR2HEXINT", STRING, STRING); //$NON-NLS-1$ addPushDownFunction(TERADATA, "INDEX", INTEGER, STRING, STRING); //$NON-NLS-1$ addPushDownFunction(TERADATA, "BYTES", INTEGER, STRING); //$NON-NLS-1$ addPushDownFunction(TERADATA, "OCTET_LENGTH", INTEGER, STRING); //$NON-NLS-1$ addPushDownFunction(TERADATA, "HASHAMP", INTEGER, STRING); //$NON-NLS-1$ addPushDownFunction(TERADATA, "HASHBAKAMP", INTEGER, STRING); //$NON-NLS-1$ addPushDownFunction(TERADATA, "HASHBUCKET", INTEGER, STRING); //$NON-NLS-1$ addPushDownFunction(TERADATA, "HASHROW", INTEGER, STRING); //$NON-NLS-1$ addPushDownFunction(TERADATA, "NULLIFZERO", BIG_DECIMAL, BIG_DECIMAL); //$NON-NLS-1$ addPushDownFunction(TERADATA, "ZEROIFNULL", BIG_DECIMAL, BIG_DECIMAL); //$NON-NLS-1$ registerFunctionModifier(SourceSystemFunctions.COT, new FunctionModifier() { @Override public List<?> translate(Function function) { function.setName(SourceSystemFunctions.TAN); return Arrays.asList(getLanguageFactory().createFunction(SourceSystemFunctions.DIVIDE_OP, new Expression[] {new Literal(1, TypeFacility.RUNTIME_TYPES.INTEGER), function}, TypeFacility.RUNTIME_TYPES.DOUBLE)); } }); registerFunctionModifier(SourceSystemFunctions.LTRIM, new FunctionModifier() { @Override public List<?> translate(Function function) { return Arrays.asList("TRIM(LEADING FROM ", function.getParameters().get(0), ")"); //$NON-NLS-1$ //$NON-NLS-2$ } }); registerFunctionModifier(SourceSystemFunctions.RTRIM, new FunctionModifier() { @Override public List<?> translate(Function function) { return Arrays.asList("TRIM(TRAILING FROM ", function.getParameters().get(0), ")"); //$NON-NLS-1$ //$NON-NLS-2$ } }); registerFunctionModifier(SourceSystemFunctions.MOD, new FunctionModifier() { @Override public List<?> translate(Function function) { return Arrays.asList(function.getParameters().get(0), " MOD ", function.getParameters().get(1)); //$NON-NLS-1$ } }); } @Override public SQLConversionVisitor getSQLConversionVisitor() { return new TeradataSQLConversionVisitor(this); } @Override public List<String> getSupportedFunctions() { List<String> supportedFunctions = new ArrayList<String>(); supportedFunctions.addAll(super.getSupportedFunctions()); supportedFunctions.add(SourceSystemFunctions.ABS); supportedFunctions.add(SourceSystemFunctions.ACOS); supportedFunctions.add(SourceSystemFunctions.ASIN); supportedFunctions.add(SourceSystemFunctions.ATAN); supportedFunctions.add(SourceSystemFunctions.ATAN2); supportedFunctions.add(SourceSystemFunctions.COALESCE); supportedFunctions.add(SourceSystemFunctions.COS); supportedFunctions.add(SourceSystemFunctions.COT); supportedFunctions.add(SourceSystemFunctions.CONVERT); supportedFunctions.add(SourceSystemFunctions.CURDATE); supportedFunctions.add(SourceSystemFunctions.CURTIME); supportedFunctions.add(SourceSystemFunctions.DAYOFMONTH); supportedFunctions.add(SourceSystemFunctions.EXP); supportedFunctions.add(SourceSystemFunctions.HOUR); supportedFunctions.add(SourceSystemFunctions.LEFT); supportedFunctions.add(SourceSystemFunctions.LOCATE); supportedFunctions.add(SourceSystemFunctions.LOG); supportedFunctions.add(SourceSystemFunctions.LCASE); supportedFunctions.add(SourceSystemFunctions.LTRIM); supportedFunctions.add(SourceSystemFunctions.LENGTH); supportedFunctions.add(SourceSystemFunctions.MINUTE); supportedFunctions.add(SourceSystemFunctions.MOD); supportedFunctions.add(SourceSystemFunctions.MONTH); supportedFunctions.add(SourceSystemFunctions.NULLIF); supportedFunctions.add(SourceSystemFunctions.RIGHT); supportedFunctions.add(SourceSystemFunctions.RTRIM); supportedFunctions.add(SourceSystemFunctions.SECOND); supportedFunctions.add(SourceSystemFunctions.SIN); supportedFunctions.add(SourceSystemFunctions.SQRT); supportedFunctions.add(SourceSystemFunctions.SUBSTRING); supportedFunctions.add(SourceSystemFunctions.TAN); supportedFunctions.add(SourceSystemFunctions.TRIM); supportedFunctions.add(SourceSystemFunctions.UCASE); supportedFunctions.add(SourceSystemFunctions.YEAR); return supportedFunctions; } @Override public String translateLiteralDate(Date dateValue) { return "DATE '" + formatDateValue(dateValue, false) + '\''; //$NON-NLS-1$ } @Override public String translateLiteralTime(Time timeValue) { return "TIME '" + formatDateValue(timeValue, false) + '\''; //$NON-NLS-1$ } @Override public String translateLiteralTimestamp(Timestamp timestampValue) { return "TIMESTAMP '" + formatDateValue(timestampValue, false) + '\''; //$NON-NLS-1$ } // Teradata also supports MINUS & ALL set operators // more aggregates available @Override public boolean supportsScalarSubqueries() { return false; } @Override public boolean supportsUnions() { return true; } @Override public boolean supportsIntersect() { return true; } @Override public boolean supportsExcept() { return true; } @Override public boolean supportsInlineViews() { return true; } @Override public boolean supportsAggregatesEnhancedNumeric() { return true; } @Override public boolean supportsCommonTableExpressions() { return false; } @Override public NullOrder getDefaultNullOrder() { return NullOrder.FIRST; } @Override public List<?> translateCommand(Command command, ExecutionContext context) { if (command instanceof QueryExpression) { QueryExpression qe = (QueryExpression)command; //teradata prefers positional ordering if (qe.getOrderBy() != null) { Select select = qe.getProjectedQuery(); List<DerivedColumn> derivedColumns = select.getDerivedColumns(); Map<String, Integer> positions = new HashMap<String, Integer>(); int i = 1; for (DerivedColumn derivedColumn : derivedColumns) { String name = derivedColumn.getAlias(); if (name == null && derivedColumn.getExpression() instanceof ColumnReference) { ColumnReference cr = (ColumnReference)derivedColumn.getExpression(); name = cr.toString(); } positions.put(name, i++); } for (SortSpecification ss : qe.getOrderBy().getSortSpecifications()) { Expression ex = ss.getExpression(); if (!(ex instanceof ColumnReference)) { continue; } ColumnReference cr = (ColumnReference)ex; Integer position = positions.get(cr.toString()); if (position != null) { ss.setExpression(new Literal(position, TypeFacility.RUNTIME_TYPES.INTEGER)); } } } } return super.translateCommand(command, context); } public static class LocateModifier extends FunctionModifier { ConvertModifier convertModifier; public LocateModifier(ConvertModifier convertModifier) { this.convertModifier = convertModifier; } @Override public List<?> translate(Function function) { ArrayList<Object> target = new ArrayList<Object>(); Expression expr1 = function.getParameters().get(0); Expression expr2 = function.getParameters().get(1); if (function.getParameters().size() > 2) { Expression expr3 = function.getParameters().get(2); target.add("position("); //$NON-NLS-1$ target.add(expr1); target.add( " in "); //$NON-NLS-1$ target.add("substr("); //$NON-NLS-1$ target.add(expr2); target.add(","); //$NON-NLS-1$ target.add(expr3); target.add("))"); //$NON-NLS-1$ } else { target.add("position("); //$NON-NLS-1$ target.add(expr1); target.add( " in "); //$NON-NLS-1$ target.add(expr2); target.add(")"); //$NON-NLS-1$ } return target; } } public static class ExtractModifier extends FunctionModifier { private String type; public ExtractModifier(String type) { this.type = type; } @Override public List<?> translate(Function function) { return Arrays.asList("extract(",this.type," from ",function.getParameters().get(0) ,")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } } public static class CastModifier extends FunctionModifier { private String target; public CastModifier(String target) { this.target = target; } @Override public List<?> translate(Function function) { return Arrays.asList("cast(", function.getParameters().get(0), " AS "+this.target+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } } public static class LeftOrRightFunctionModifier extends FunctionModifier { private LanguageFactory langFactory; ConvertModifier convertModifier; public LeftOrRightFunctionModifier(LanguageFactory langFactory, ConvertModifier converModifier) { this.langFactory = langFactory; this.convertModifier = converModifier; } @Override public List<?> translate(Function function) { List<Expression> args = function.getParameters(); ArrayList<Object> target = new ArrayList<Object>(); if (function.getName().equalsIgnoreCase("left")) { //$NON-NLS-1$ //substr(string, 1, length) target.add("substr("); //$NON-NLS-1$ target.add(args.get(0)); target.add(","); //$NON-NLS-1$ target.add(langFactory.createLiteral(Integer.valueOf(1), TypeFacility.RUNTIME_TYPES.INTEGER)); target.add(","); //$NON-NLS-1$ target.add(args.get(1)); target.add(")"); //$NON-NLS-1$ } else if (function.getName().equalsIgnoreCase("right")) { //$NON-NLS-1$ //substr(case_size, character_length(case_size) -4) target.add("substr("); //$NON-NLS-1$ target.add(args.get(0)); target.add(",(character_length("); //$NON-NLS-1$ target.add(args.get(0)); target.add(")-"); //$NON-NLS-1$ target.add(args.get(1)); target.add("+1))"); //$NON-NLS-1$ // offset for 1 based index } return target; } } @Override protected boolean usesDatabaseVersion() { return true; } @Override public String getHibernateDialectClassName() { if (getVersion().getMajorVersion() >= 14) { return "org.hibernate.dialect.Teradata14Dialect"; //$NON-NLS-1$ } return "org.hibernate.dialect.TeradataDialect"; //$NON-NLS-1$ } @Override protected boolean supportsBooleanExpressions() { return false; } }