/* * Hibernate, Relational Persistence for Idiomatic Java * * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.dialect; import java.lang.reflect.Method; import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.sql.Types; import java.util.Locale; import org.hibernate.MappingException; import org.hibernate.dialect.function.AnsiTrimFunction; import org.hibernate.dialect.function.DerbyConcatFunction; import org.hibernate.dialect.pagination.AbstractLimitHandler; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.LimitHelper; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.spi.RowSelection; import org.hibernate.hql.spi.id.IdTableSupportStandardImpl; import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy; import org.hibernate.hql.spi.id.local.AfterUseAction; import org.hibernate.hql.spi.id.local.LocalTemporaryTableBulkIdStrategy; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.sql.CaseFragment; import org.hibernate.sql.DerbyCaseFragment; import org.jboss.logging.Logger; /** * Hibernate Dialect for Cloudscape 10 - aka Derby. This implements both an * override for the identity column generator as well as for the case statement * issue documented at: * http://www.jroller.com/comments/kenlars99/Weblog/cloudscape_soon_to_be_derby * * @author Simon Johnston * * @deprecated HHH-6073 */ @Deprecated public class DerbyDialect extends DB2Dialect { @SuppressWarnings("deprecation") private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, DerbyDialect.class.getName() ); private int driverVersionMajor; private int driverVersionMinor; private final LimitHandler limitHandler; /** * Constructs a DerbyDialect */ @SuppressWarnings("deprecation") public DerbyDialect() { super(); if ( this.getClass() == DerbyDialect.class ) { LOG.deprecatedDerbyDialect(); } registerFunction( "concat", new DerbyConcatFunction() ); registerFunction( "trim", new AnsiTrimFunction() ); registerColumnType( Types.BLOB, "blob" ); registerDerbyKeywords(); determineDriverVersion(); if ( driverVersionMajor > 10 || ( driverVersionMajor == 10 && driverVersionMinor >= 7 ) ) { registerColumnType( Types.BOOLEAN, "boolean" ); } this.limitHandler = new DerbyLimitHandler(); } private void determineDriverVersion() { try { // locate the derby sysinfo class and query its version info final Class sysinfoClass = ReflectHelper.classForName( "org.apache.derby.tools.sysinfo", this.getClass() ); final Method majorVersionGetter = sysinfoClass.getMethod( "getMajorVersion", ReflectHelper.NO_PARAM_SIGNATURE ); final Method minorVersionGetter = sysinfoClass.getMethod( "getMinorVersion", ReflectHelper.NO_PARAM_SIGNATURE ); driverVersionMajor = (Integer) majorVersionGetter.invoke( null, ReflectHelper.NO_PARAMS ); driverVersionMinor = (Integer) minorVersionGetter.invoke( null, ReflectHelper.NO_PARAMS ); } catch ( Exception e ) { LOG.unableToLoadDerbyDriver( e.getMessage() ); driverVersionMajor = -1; driverVersionMinor = -1; } } private boolean isTenPointFiveReleaseOrNewer() { return driverVersionMajor > 10 || ( driverVersionMajor == 10 && driverVersionMinor >= 5 ); } @Override public String getCrossJoinSeparator() { return ", "; } @Override public CaseFragment createCaseFragment() { return new DerbyCaseFragment(); } @Override public boolean dropConstraints() { return true; } @Override public boolean supportsSequences() { // technically sequence support was added in 10.6.1.0... // // The problem though is that I am not exactly sure how to differentiate 10.6.1.0 from any other 10.6.x release. // // http://db.apache.org/derby/docs/10.0/publishedapi/org/apache/derby/tools/sysinfo.html seems incorrect. It // states that derby's versioning scheme is major.minor.maintenance, but obviously 10.6.1.0 has 4 components // to it, not 3. // // Let alone the fact that it states that versions with the matching major.minor are 'feature // compatible' which is clearly not the case here (sequence support is a new feature...) return driverVersionMajor > 10 || ( driverVersionMajor == 10 && driverVersionMinor >= 6 ); } @Override public String getQuerySequencesString() { if ( supportsSequences() ) { return "select SEQUENCENAME from SYS.SYSSEQUENCES"; } else { return null; } } @Override public String getSequenceNextValString(String sequenceName) { if ( supportsSequences() ) { return "values next value for " + sequenceName; } else { throw new MappingException( "Derby does not support sequence prior to release 10.6.1.0" ); } } @Override public boolean supportsLimit() { return isTenPointFiveReleaseOrNewer(); } @Override public boolean supportsCommentOn() { //HHH-4531 return false; } @Override @SuppressWarnings("deprecation") public boolean supportsLimitOffset() { return isTenPointFiveReleaseOrNewer(); } @Override public String getForUpdateString() { return " for update with rs"; } @Override public String getWriteLockString(int timeout) { return " for update with rs"; } @Override public String getReadLockString(int timeout) { return " for read only with rs"; } @Override public LimitHandler getLimitHandler() { return limitHandler; } @Override public boolean supportsTuplesInSubqueries() { return false; } /** * {@inheritDoc} * <p/> * From Derby 10.5 Docs: * <pre> * Query * [ORDER BY clause] * [result offset clause] * [fetch first clause] * [FOR UPDATE clause] * [WITH {RR|RS|CS|UR}] * </pre> */ @Override public String getLimitString(String query, final int offset, final int limit) { final StringBuilder sb = new StringBuilder(query.length() + 50); final String normalizedSelect = query.toLowerCase(Locale.ROOT).trim(); final int forUpdateIndex = normalizedSelect.lastIndexOf( "for update") ; if ( hasForUpdateClause( forUpdateIndex ) ) { sb.append( query.substring( 0, forUpdateIndex-1 ) ); } else if ( hasWithClause( normalizedSelect ) ) { sb.append( query.substring( 0, getWithIndex( query ) - 1 ) ); } else { sb.append( query ); } if ( offset == 0 ) { sb.append( " fetch first " ); } else { sb.append( " offset " ).append( offset ).append( " rows fetch next " ); } sb.append( limit ).append( " rows only" ); if ( hasForUpdateClause( forUpdateIndex ) ) { sb.append( ' ' ); sb.append( query.substring( forUpdateIndex ) ); } else if ( hasWithClause( normalizedSelect ) ) { sb.append( ' ' ).append( query.substring( getWithIndex( query ) ) ); } return sb.toString(); } @Override public boolean supportsVariableLimit() { // we bind the limit and offset values directly into the sql... return false; } private boolean hasForUpdateClause(int forUpdateIndex) { return forUpdateIndex >= 0; } private boolean hasWithClause(String normalizedSelect){ return normalizedSelect.startsWith( "with ", normalizedSelect.length() - 7 ); } private int getWithIndex(String querySelect) { int i = querySelect.lastIndexOf( "with " ); if ( i < 0 ) { i = querySelect.lastIndexOf( "WITH " ); } return i; } // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override public boolean supportsLobValueChangePropogation() { return false; } @Override public boolean supportsUnboundedLobLocatorMaterialization() { return false; } private final class DerbyLimitHandler extends AbstractLimitHandler { /** * {@inheritDoc} * <p/> * From Derby 10.5 Docs: * <pre> * Query * [ORDER BY clause] * [result offset clause] * [fetch first clause] * [FOR UPDATE clause] * [WITH {RR|RS|CS|UR}] * </pre> */ @Override public String processSql(String sql, RowSelection selection) { final StringBuilder sb = new StringBuilder( sql.length() + 50 ); final String normalizedSelect = sql.toLowerCase(Locale.ROOT).trim(); final int forUpdateIndex = normalizedSelect.lastIndexOf( "for update" ); if (hasForUpdateClause( forUpdateIndex )) { sb.append( sql.substring( 0, forUpdateIndex - 1 ) ); } else if (hasWithClause( normalizedSelect )) { sb.append( sql.substring( 0, getWithIndex( sql ) - 1 ) ); } else { sb.append( sql ); } if (LimitHelper.hasFirstRow( selection )) { sb.append( " offset " ).append( selection.getFirstRow() ).append( " rows fetch next " ); } else { sb.append( " fetch first " ); } sb.append( getMaxOrLimit( selection ) ).append(" rows only" ); if (hasForUpdateClause( forUpdateIndex )) { sb.append( ' ' ); sb.append( sql.substring( forUpdateIndex ) ); } else if (hasWithClause( normalizedSelect )) { sb.append( ' ' ).append( sql.substring( getWithIndex( sql ) ) ); } return sb.toString(); } @Override public boolean supportsLimit() { return isTenPointFiveReleaseOrNewer(); } @Override @SuppressWarnings("deprecation") public boolean supportsLimitOffset() { return isTenPointFiveReleaseOrNewer(); } @Override public boolean supportsVariableLimit() { return false; } } @Override public IdentifierHelper buildIdentifierHelper( IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData) throws SQLException { builder.applyIdentifierCasing( dbMetaData ); builder.applyReservedWords( dbMetaData ); builder.applyReservedWords( getKeywords() ); builder.setNameQualifierSupport( getNameQualifierSupport() ); return builder.build(); } protected void registerDerbyKeywords() { registerKeyword( "ADD" ); registerKeyword( "ALL" ); registerKeyword( "ALLOCATE" ); registerKeyword( "ALTER" ); registerKeyword( "AND" ); registerKeyword( "ANY" ); registerKeyword( "ARE" ); registerKeyword( "AS" ); registerKeyword( "ASC" ); registerKeyword( "ASSERTION" ); registerKeyword( "AT" ); registerKeyword( "AUTHORIZATION" ); registerKeyword( "AVG" ); registerKeyword( "BEGIN" ); registerKeyword( "BETWEEN" ); registerKeyword( "BIT" ); registerKeyword( "BOOLEAN" ); registerKeyword( "BOTH" ); registerKeyword( "BY" ); registerKeyword( "CALL" ); registerKeyword( "CASCADE" ); registerKeyword( "CASCADED" ); registerKeyword( "CASE" ); registerKeyword( "CAST" ); registerKeyword( "CHAR" ); registerKeyword( "CHARACTER" ); registerKeyword( "CHECK" ); registerKeyword( "CLOSE" ); registerKeyword( "COLLATE" ); registerKeyword( "COLLATION" ); registerKeyword( "COLUMN" ); registerKeyword( "COMMIT" ); registerKeyword( "CONNECT" ); registerKeyword( "CONNECTION" ); registerKeyword( "CONSTRAINT" ); registerKeyword( "CONSTRAINTS" ); registerKeyword( "CONTINUE" ); registerKeyword( "CONVERT" ); registerKeyword( "CORRESPONDING" ); registerKeyword( "COUNT" ); registerKeyword( "CREATE" ); registerKeyword( "CURRENT" ); registerKeyword( "CURRENT_DATE" ); registerKeyword( "CURRENT_TIME" ); registerKeyword( "CURRENT_TIMESTAMP" ); registerKeyword( "CURRENT_USER" ); registerKeyword( "CURSOR" ); registerKeyword( "DEALLOCATE" ); registerKeyword( "DEC" ); registerKeyword( "DECIMAL" ); registerKeyword( "DECLARE" ); registerKeyword( "DEFERRABLE" ); registerKeyword( "DEFERRED" ); registerKeyword( "DELETE" ); registerKeyword( "DESC" ); registerKeyword( "DESCRIBE" ); registerKeyword( "DIAGNOSTICS" ); registerKeyword( "DISCONNECT" ); registerKeyword( "DISTINCT" ); registerKeyword( "DOUBLE" ); registerKeyword( "DROP" ); registerKeyword( "ELSE" ); registerKeyword( "END" ); registerKeyword( "ENDEXEC" ); registerKeyword( "ESCAPE" ); registerKeyword( "EXCEPT" ); registerKeyword( "EXCEPTION" ); registerKeyword( "EXEC" ); registerKeyword( "EXECUTE" ); registerKeyword( "EXISTS" ); registerKeyword( "EXPLAIN" ); registerKeyword( "EXTERNAL" ); registerKeyword( "FALSE" ); registerKeyword( "FETCH" ); registerKeyword( "FIRST" ); registerKeyword( "FLOAT" ); registerKeyword( "FOR" ); registerKeyword( "FOREIGN" ); registerKeyword( "FOUND" ); registerKeyword( "FROM" ); registerKeyword( "FULL" ); registerKeyword( "FUNCTION" ); registerKeyword( "GET" ); registerKeyword( "GET_CURRENT_CONNECTION" ); registerKeyword( "GLOBAL" ); registerKeyword( "GO" ); registerKeyword( "GOTO" ); registerKeyword( "GRANT" ); registerKeyword( "GROUP" ); registerKeyword( "HAVING" ); registerKeyword( "HOUR" ); registerKeyword( "IDENTITY" ); registerKeyword( "IMMEDIATE" ); registerKeyword( "IN" ); registerKeyword( "INDICATOR" ); registerKeyword( "INITIALLY" ); registerKeyword( "INNER" ); registerKeyword( "INOUT" ); registerKeyword( "INPUT" ); registerKeyword( "INSENSITIVE" ); registerKeyword( "INSERT" ); registerKeyword( "INT" ); registerKeyword( "INTEGER" ); registerKeyword( "INTERSECT" ); registerKeyword( "INTO" ); registerKeyword( "IS" ); registerKeyword( "ISOLATION" ); registerKeyword( "JOIN" ); registerKeyword( "KEY" ); registerKeyword( "LAST" ); registerKeyword( "LEFT" ); registerKeyword( "LIKE" ); registerKeyword( "LONGINT" ); registerKeyword( "LOWER" ); registerKeyword( "LTRIM" ); registerKeyword( "MATCH" ); registerKeyword( "MAX" ); registerKeyword( "MIN" ); registerKeyword( "MINUTE" ); registerKeyword( "NATIONAL" ); registerKeyword( "NATURAL" ); registerKeyword( "NCHAR" ); registerKeyword( "NVARCHAR" ); registerKeyword( "NEXT" ); registerKeyword( "NO" ); registerKeyword( "NOT" ); registerKeyword( "NULL" ); registerKeyword( "NULLIF" ); registerKeyword( "NUMERIC" ); registerKeyword( "OF" ); registerKeyword( "ON" ); registerKeyword( "ONLY" ); registerKeyword( "OPEN" ); registerKeyword( "OPTION" ); registerKeyword( "OR" ); registerKeyword( "ORDER" ); registerKeyword( "OUT" ); registerKeyword( "OUTER" ); registerKeyword( "OUTPUT" ); registerKeyword( "OVERLAPS" ); registerKeyword( "PAD" ); registerKeyword( "PARTIAL" ); registerKeyword( "PREPARE" ); registerKeyword( "PRESERVE" ); registerKeyword( "PRIMARY" ); registerKeyword( "PRIOR" ); registerKeyword( "PRIVILEGES" ); registerKeyword( "PROCEDURE" ); registerKeyword( "PUBLIC" ); registerKeyword( "READ" ); registerKeyword( "REAL" ); registerKeyword( "REFERENCES" ); registerKeyword( "RELATIVE" ); registerKeyword( "RESTRICT" ); registerKeyword( "REVOKE" ); registerKeyword( "RIGHT" ); registerKeyword( "ROLLBACK" ); registerKeyword( "ROWS" ); registerKeyword( "RTRIM" ); registerKeyword( "SCHEMA" ); registerKeyword( "SCROLL" ); registerKeyword( "SECOND" ); registerKeyword( "SELECT" ); registerKeyword( "SESSION_USER" ); registerKeyword( "SET" ); registerKeyword( "SMALLINT" ); registerKeyword( "SOME" ); registerKeyword( "SPACE" ); registerKeyword( "SQL" ); registerKeyword( "SQLCODE" ); registerKeyword( "SQLERROR" ); registerKeyword( "SQLSTATE" ); registerKeyword( "SUBSTR" ); registerKeyword( "SUBSTRING" ); registerKeyword( "SUM" ); registerKeyword( "SYSTEM_USER" ); registerKeyword( "TABLE" ); registerKeyword( "TEMPORARY" ); registerKeyword( "TIMEZONE_HOUR" ); registerKeyword( "TIMEZONE_MINUTE" ); registerKeyword( "TO" ); registerKeyword( "TRAILING" ); registerKeyword( "TRANSACTION" ); registerKeyword( "TRANSLATE" ); registerKeyword( "TRANSLATION" ); registerKeyword( "TRUE" ); registerKeyword( "UNION" ); registerKeyword( "UNIQUE" ); registerKeyword( "UNKNOWN" ); registerKeyword( "UPDATE" ); registerKeyword( "UPPER" ); registerKeyword( "USER" ); registerKeyword( "USING" ); registerKeyword( "VALUES" ); registerKeyword( "VARCHAR" ); registerKeyword( "VARYING" ); registerKeyword( "VIEW" ); registerKeyword( "WHENEVER" ); registerKeyword( "WHERE" ); registerKeyword( "WITH" ); registerKeyword( "WORK" ); registerKeyword( "WRITE" ); registerKeyword( "XML" ); registerKeyword( "XMLEXISTS" ); registerKeyword( "XMLPARSE" ); registerKeyword( "XMLSERIALIZE" ); registerKeyword( "YEAR" ); } /** * {@inheritDoc} * <p> * From Derby docs: * <pre> * The DECLARE GLOBAL TEMPORARY TABLE statement defines a temporary table for the current connection. * </pre> * * {@link DB2Dialect} returns a {@link org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy} that * will make temporary tables created at startup and hence unavailable for subsequent connections.<br/> * see HHH-10238. * </p> */ @Override public MultiTableBulkIdStrategy getDefaultMultiTableBulkIdStrategy() { return new LocalTemporaryTableBulkIdStrategy(new IdTableSupportStandardImpl() { @Override public String generateIdTableName(String baseName) { return "session." + super.generateIdTableName( baseName ); } @Override public String getCreateIdTableCommand() { return "declare global temporary table"; } @Override public String getCreateIdTableStatementOptions() { return "not logged"; } }, AfterUseAction.CLEAN, null); } }