/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Middleware LLC. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program 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 distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA * */ package com.querydsl.jpa.support; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; import org.hibernate.HibernateException; import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.function.SQLFunctionTemplate; import org.hibernate.dialect.function.VarArgsSQLFunction; import org.hibernate.type.StandardBasicTypes; /** * A dialect for the Teradata database created by MCR as part of the dialect * certification process. * * @author Jay Nance */ public class TeradataDialect extends Dialect { /** * Constructor */ public TeradataDialect() { super(); // registerColumnType data types registerColumnType(Types.NUMERIC, "NUMERIC($p,$s)"); registerColumnType(Types.DOUBLE, "DOUBLE PRECISION"); registerColumnType(Types.BIGINT, "NUMERIC(18,0)"); registerColumnType(Types.BIT, "BYTEINT"); registerColumnType(Types.TINYINT, "BYTEINT"); registerColumnType(Types.VARBINARY, "VARBYTE($l)"); registerColumnType(Types.BINARY, "BYTEINT"); registerColumnType(Types.LONGVARCHAR, "LONG VARCHAR"); registerColumnType(Types.CHAR, "CHAR(1)"); registerColumnType(Types.DECIMAL, "DECIMAL"); registerColumnType(Types.INTEGER, "INTEGER"); registerColumnType(Types.SMALLINT, "SMALLINT"); registerColumnType(Types.FLOAT, "FLOAT"); registerColumnType(Types.VARCHAR, "VARCHAR($l)"); registerColumnType(Types.DATE, "DATE"); registerColumnType(Types.TIME, "TIME"); registerColumnType(Types.TIMESTAMP, "TIMESTAMP"); registerColumnType(Types.BOOLEAN, "BYTEINT"); // hibernate seems to // ignore this type... registerColumnType(Types.BLOB, "BLOB"); registerColumnType(Types.CLOB, "CLOB"); registerFunction("year", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "extract(year from ?1)")); registerFunction("length", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "character_length(?1)")); registerFunction("concat", new VarArgsSQLFunction(StandardBasicTypes.STRING, "(", "||", ")")); registerFunction("substring", new SQLFunctionTemplate(StandardBasicTypes.STRING, "substring(?1 from ?2 for ?3)")); registerFunction("locate", new SQLFunctionTemplate(StandardBasicTypes.STRING, "position(?1 in ?2)")); registerFunction("mod", new SQLFunctionTemplate(StandardBasicTypes.STRING, "?1 mod ?2")); registerFunction("str", new SQLFunctionTemplate(StandardBasicTypes.STRING, "cast(?1 as varchar(255))")); // bit_length feels a bit broken to me. We have to cast to char in order // to // pass when a numeric value is supplied. But of course the answers // given will // be wildly different for these two datatypes. 1234.5678 will be 9 // bytes as // a char string but will be 8 or 16 bytes as a true numeric. // Jay Nance 2006-09-22 registerFunction("bit_length", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "octet_length(cast(?1 as char))*4")); // The preference here would be // SQLFunctionTemplate( Hibernate.TIMESTAMP, "current_timestamp(?1)", // false) // but this appears not to work. // Jay Nance 2006-09-22 registerFunction("current_timestamp", new SQLFunctionTemplate(StandardBasicTypes.TIMESTAMP, "current_timestamp")); registerFunction("current_time", new SQLFunctionTemplate(StandardBasicTypes.TIME, "current_time")); registerFunction("current_date", new SQLFunctionTemplate(StandardBasicTypes.DATE, "current_date")); // IBID for current_time and current_date registerKeyword("account"); registerKeyword("alias"); registerKeyword("class"); registerKeyword("column"); registerKeyword("first"); registerKeyword("map"); registerKeyword("month"); registerKeyword("password"); registerKeyword("role"); registerKeyword("summary"); registerKeyword("title"); registerKeyword("type"); registerKeyword("value"); registerKeyword("year"); // Tell hibernate to use getBytes instead of getBinaryStream getDefaultProperties().setProperty(Environment.USE_STREAMS_FOR_BINARY, "false"); // No batch statements getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, NO_BATCH); } /** * Does this dialect support the <tt>FOR UPDATE</tt> syntax? * * @return empty string ... Teradata does not support * <tt>FOR UPDATE<tt> syntax */ @Override public String getForUpdateString() { return ""; } @Override public boolean supportsIdentityColumns() { return false; } @Override public boolean supportsSequences() { return false; } @Override public String getAddColumnString() { return "Add"; } public boolean supportsTemporaryTables() { return true; } public String getCreateTemporaryTableString() { return "create global temporary table"; } public String getCreateTemporaryTablePostfix() { return " on commit preserve rows"; } public Boolean performTemporaryTableDDLInIsolation() { return Boolean.TRUE; } public boolean dropTemporaryTableAfterUse() { return false; } /** * Get the name of the database type associated with the given * <tt>java.sql.Types</tt> typecode. * * @param code * <tt>java.sql.Types</tt> typecode * @param length * the length or precision of the column * @param precision * the precision of the column * @param scale * the scale of the column * * @return the database type name * * @throws HibernateException */ public String getTypeName(int code, int length, int precision, int scale) throws HibernateException { /* * We might want a special case for 19,2. This is very common for money * types and here it is converted to 18,1 */ float f = precision > 0 ? (float) scale / (float) precision : 0; int p = (precision > 18 ? 18 : precision); int s = (precision > 18 ? (int) (18.0 * f) : (scale > 18 ? 18 : scale)); return super.getTypeName(code, length, p, s); } @Override public boolean supportsCascadeDelete() { return false; } @Override public boolean supportsCircularCascadeDeleteConstraints() { return false; } @Override public boolean areStringComparisonsCaseInsensitive() { return true; } @Override public boolean supportsEmptyInList() { return false; } @Override public String getSelectClauseNullString(int sqlType) { String v = "null"; switch (sqlType) { case Types.BIT: case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: case Types.BIGINT: case Types.FLOAT: case Types.REAL: case Types.DOUBLE: case Types.NUMERIC: case Types.DECIMAL: v = "cast(null as decimal)"; break; case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: v = "cast(null as varchar(255))"; break; case Types.DATE: case Types.TIME: case Types.TIMESTAMP: v = "cast(null as timestamp)"; break; case Types.BINARY: case Types.VARBINARY: case Types.LONGVARBINARY: case Types.NULL: case Types.OTHER: case Types.JAVA_OBJECT: case Types.DISTINCT: case Types.STRUCT: case Types.ARRAY: case Types.BLOB: case Types.CLOB: case Types.REF: case Types.DATALINK: case Types.BOOLEAN: break; } return v; } @Override public String getCreateMultisetTableString() { return "create multiset table "; } @Override public boolean supportsLobValueChangePropogation() { return false; } @Override public boolean doesReadCommittedCauseWritersToBlockReaders() { return true; } @Override public boolean doesRepeatableReadCauseReadersToBlockWriters() { return true; } @Override public boolean supportsBindAsCallableArgument() { return false; } @Override public boolean supportsNotNullUnique() { return false; } @Override public boolean supportsExpectedLobUsagePattern() { return true; } @Override public boolean supportsUnboundedLobLocatorMaterialization() { return false; } public boolean supportsDropPreProcess() { return true; } public String performDropPreProcess(Statement stmt, String dropSql) throws SQLException { String alterStr = "alter"; String tableStr = "table"; String dropStr = "drop"; String constraintStr = "constraint"; java.util.StringTokenizer st = new java.util.StringTokenizer(dropSql); if (alterStr.equalsIgnoreCase(st.nextToken()) && tableStr.equalsIgnoreCase(st.nextToken())) { String tableName = st.nextToken(); if ((tableName.startsWith("\"")) && (!tableName.endsWith("\""))) { String next = null; while (true) { next = st.nextToken(); tableName += " " + next; if (next.endsWith("\\\"")) { continue; } if (next.endsWith("\"")) { break; } } } if (dropStr.equalsIgnoreCase(st.nextToken()) && constraintStr.equalsIgnoreCase(st.nextToken())) { String constraintName = st.nextToken(); // Table name might have whitespace characters within name so // just take whatever lies between // "alter table " and "drop constraint" int idxStart = dropSql.indexOf(tableStr, 0) + 5; int idxEnd = dropSql.lastIndexOf(dropStr); tableName = dropSql.substring(idxStart, idxEnd).trim(); if (tableName.startsWith("\"") && tableName.endsWith("\"")) { tableName = tableName.substring(1, tableName.length() - 1); } String arrStr = null; String queryStr = "sel IndexId, ChildTable, IndexName from dbc.RI_Distinct_ChildrenV where IndexName = '" + constraintName + "'"; java.sql.ResultSet rs = stmt.executeQuery(queryStr); if (rs.next()) { arrStr = "drop table \"" + tableName + "_" + rs.getString(1) + "\""; rs.close(); return arrStr; } } } return null; } public void performDropPostProcess(Statement stmt, String dropSql) throws SQLException { if (dropSql == null) { return; } stmt.executeUpdate(dropSql); } }