Open Source Repository

Home /hibernate/hibernate-3.2.6.ga | Repository Home



org/hibernate/dialect/DerbyDialect.java
//$Id: DerbyDialect.java 10989 2006-12-13 18:37:11Z [email protected] $
package org.hibernate.dialect;

import org.hibernate.Hibernate;
import org.hibernate.QueryException;
import org.hibernate.HibernateException;
import org.hibernate.engine.Mapping;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.type.Type;
import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.id.TableHiLoGenerator;
import org.hibernate.sql.CaseFragment;
import org.hibernate.sql.DerbyCaseFragment;

import java.util.List;
import java.util.ArrayList;

/**
 @author Simon Johnston
 *
 * 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
 */
public class DerbyDialect extends DB2Dialect {

  public DerbyDialect() {
    super();
    registerFunction"concat"new VarArgsSQLFunctionHibernate.STRING, "(","||",")" ) );
    registerFunction"trim"new DerbyTrimFunctionEmulation() );
  }

  /**
   * This is different in Cloudscape to DB2.
   */
  public String getIdentityColumnString() {
    return "not null generated always as identity"//$NON-NLS-1
  }

  /**
   * Return the case statement modified for Cloudscape.
   */
  public CaseFragment createCaseFragment() {
    return new DerbyCaseFragment();
  }

  public boolean dropConstraints() {
        return true;
  }

  public Class getNativeIdentifierGeneratorClass() {
    return TableHiLoGenerator.class;
  }

  public boolean supportsSequences() {
    return false;
  }

  public boolean supportsLimit() {
    return false;
  }

  public boolean supportsLimitOffset() {
    return false;
  }

  public String getQuerySequencesString() {
     return null ;
  }

  /**
   * A specialized function template to emulate the ANSI trim function on Derby DB
   * since it does not support the full trim specification.  However, we cannot even
   * fully emulate it because there is not standard 'replace' function either. :(
   */
  public static class DerbyTrimFunctionEmulation implements SQLFunction {
    private static final SQLFunction LEADING_SPACE_TRIM = new SQLFunctionTemplateHibernate.STRING, "ltrim( ?1 )");
    private static final SQLFunction TRAILING_SPACE_TRIM = new SQLFunctionTemplateHibernate.STRING, "rtrim( ?1 )");
    private static final SQLFunction BOTH_SPACE_TRIM = new SQLFunctionTemplateHibernate.STRING, "ltrim( rtrim( ?1 ) )");
    private static final SQLFunction BOTH_SPACE_TRIM_FROM = new SQLFunctionTemplateHibernate.STRING, "ltrim( rtrim( ?2 ) )");

    public Type getReturnType(Type columnType, Mapping mappingthrows QueryException {
      return Hibernate.STRING;
    }

    public boolean hasArguments() {
      return true;
    }

    public boolean hasParenthesesIfNoArguments() {
      return false;
    }

    public String render(List args, SessionFactoryImplementor factorythrows QueryException {
      // according to both the ANSI-SQL and EJB3 specs, trim can either take
      // exactly one parameter or a variable number of parameters between 1 and 4.
      // from the SQL spec:
      //
      // <trim function> ::=
      //      TRIM <left paren> <trim operands> <right paren>
      //
      // <trim operands> ::=
      //      [ [ <trim specification> ] [ <trim character> ] FROM ] <trim source>
      //
      // <trim specification> ::=
      //      LEADING
      //      | TRAILING
      //      | BOTH
      //
      // If only <trim specification> is omitted, BOTH is assumed;
      // if <trim character> is omitted, space is assumed
      if args.size() == ) {
        // we have the form: trim(trimSource)
        //      so we trim leading and trailing spaces
        return BOTH_SPACE_TRIM.renderargs, factory );
      }
      else if "from".equalsIgnoreCase( ( String args.get) ) ) {
        // we have the form: trim(from trimSource).
        //      This is functionally equivalent to trim(trimSource)
        return BOTH_SPACE_TRIM_FROM.renderargs, factory );
      }
      else {
        // otherwise, a trim-specification and/or a trim-character
        // have been specified;  we need to decide which options
        // are present and "do the right thing"
        boolean leading = true;         // should leading trim-characters be trimmed?
        boolean trailing = true;        // should trailing trim-characters be trimmed?
        String trimCharacter;        // the trim-character
        String trimSource;           // the trim-source

        // potentialTrimCharacterArgIndex = 1 assumes that a
        // trim-specification has been specified.  we handle the
        // exception to that explicitly
        int potentialTrimCharacterArgIndex = 1;
        String firstArg = String args.get);
        if "leading".equalsIgnoreCasefirstArg ) ) {
          trailing = false;
        }
        else if "trailing".equalsIgnoreCasefirstArg ) ) {
          leading = false;
        }
        else if "both".equalsIgnoreCasefirstArg ) ) {
        }
        else {
          potentialTrimCharacterArgIndex = 0;
        }

        String potentialTrimCharacter = String args.getpotentialTrimCharacterArgIndex );
        if "from".equalsIgnoreCasepotentialTrimCharacter ) ) {
          trimCharacter = "' '";
          trimSource = String args.getpotentialTrimCharacterArgIndex + );
        }
        else if potentialTrimCharacterArgIndex + >= args.size() ) {
          trimCharacter = "' '";
          trimSource = potentialTrimCharacter;
        }
        else {
          trimCharacter = potentialTrimCharacter;
          if "from".equalsIgnoreCase( ( String args.getpotentialTrimCharacterArgIndex + ) ) ) {
            trimSource = String args.getpotentialTrimCharacterArgIndex + );
          }
          else {
            trimSource = String args.getpotentialTrimCharacterArgIndex + );
          }
        }

        List argsToUse = new ArrayList();
        argsToUse.addtrimSource );
        argsToUse.addtrimCharacter );

        if trimCharacter.equals"' '" ) ) {
          if leading && trailing ) {
            return BOTH_SPACE_TRIM.renderargsToUse, factory );
          }
          else if leading ) {
            return LEADING_SPACE_TRIM.renderargsToUse, factory );
          }
          else {
            return TRAILING_SPACE_TRIM.renderargsToUse, factory );
          }
        }
        else {
          throw new HibernateException"cannot specify trim character when using Derby as Derby does not support the ANSI trim function, not does it support a replace function to properly emmulate it" );
        }
      }
    }
  }


  // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  public boolean supportsLobValueChangePropogation() {
    return false;
  }
}