Exemple #1
0
  private final int[] executeStatic() {
    List<Query> queries = new ArrayList<Query>();
    QueryCollector collector = new QueryCollector();

    Configuration local =
        configuration.derive(
            Utils.combine(
                configuration.executeListenerProviders(),
                new DefaultExecuteListenerProvider(collector)));

    for (int i = 0; i < records.length; i++) {
      Configuration previous = ((AttachableInternal) records[i]).configuration();

      try {
        records[i].attach(local);
        executeAction(i);
      } catch (QueryCollectorSignal e) {
        Query query = e.getQuery();

        if (query.isExecutable()) {
          queries.add(query);
        }
      } finally {
        records[i].attach(previous);
      }
    }

    // Resulting statements can be batch executed in their requested order
    int[] result = create.batch(queries).execute();
    updateChangedFlag();
    return result;
  }
Exemple #2
0
  private final QueryPartInternal delegate(Configuration configuration) {
    // These casts are safe for RowImpl
    RowN r = (RowN) row;
    RowN min = (RowN) minValue;
    RowN max = (RowN) maxValue;

    // These dialects don't support the SYMMETRIC keyword at all
    if (symmetric
        && asList(ASE, CUBRID, DB2, DERBY, FIREBIRD, H2, MYSQL, ORACLE, SQLITE, SQLSERVER, SYBASE)
            .contains(configuration.getDialect())) {
      if (not) {
        return (QueryPartInternal) r.notBetween(min, max).and(r.notBetween(max, min));
      } else {
        return (QueryPartInternal) r.between(min, max).or(r.between(max, min));
      }
    }

    // These dialects either don't support row value expressions, or they
    // Can't handle row value expressions with the BETWEEN predicate
    else if (row.size() > 1
        && asList(CUBRID, DERBY, FIREBIRD, MYSQL, ORACLE, SQLITE, SQLSERVER, SYBASE)
            .contains(configuration.getDialect())) {
      Condition result = r.ge(min).and(r.le(max));

      if (not) {
        result = result.not();
      }

      return (QueryPartInternal) result;
    } else {
      return new Native();
    }
  }
  private final QueryPartInternal delegate(Configuration ctx) {
    if (query != null) {
      return (QueryPartInternal) query;
    } else {
      switch (ctx.dialect()) {

          // [#869] Postgres supports this syntax natively
        case POSTGRES:
          {
            return (QueryPartInternal) array;
          }

          // [#869] H2 and HSQLDB can simulate this syntax by unnesting
          // the array in a subselect
        case H2:
        case HSQLDB:

          // [#1048] All other dialects simulate unnesting of arrays using
          // UNION ALL-connected subselects
        default:
          {
            return (QueryPartInternal) create(ctx).select().from(table(array));
          }
      }
    }
  }
Exemple #4
0
  AbstractContext(Configuration configuration, PreparedStatement stmt) {
    super(configuration);
    this.stmt = stmt;

    VisitListenerProvider[] providers = configuration.visitListenerProviders();
    boolean userInternalVisitListener = false;

    this.visitListeners = new VisitListener[providers.length + (userInternalVisitListener ? 1 : 0)];

    for (int i = 0; i < providers.length; i++) this.visitListeners[i] = providers[i].provide();

    if (this.visitListeners.length > 0) {
      this.visitContext = new DefaultVisitContext();
      this.visitParts = new ArrayDeque<QueryPart>();
      this.visitClauses = new ArrayDeque<Clause>();
    } else {
      this.visitContext = null;
      this.visitParts = null;
      this.visitClauses = null;
    }

    forcedParamType =
        SettingsTools.getStatementType(settings()) == StatementType.STATIC_STATEMENT
            ? ParamType.INLINED
            : null;
  }
Exemple #5
0
  @Override
  final Field<BigDecimal> getFunction0(Configuration configuration) {
    switch (configuration.getDialect()) {
      case ASE:
      case CUBRID:
      case DB2:
      case DERBY:
      case FIREBIRD:
      case H2:
      case HSQLDB:
      case INGRES:
      case MYSQL:
      case ORACLE:
      case POSTGRES:
      case SQLSERVER:
      case SYBASE:
        return Factory.exp(one());

      case SQLITE:
        return inline(Math.E, BigDecimal.class);

        // The Euler number doesn't seem to exist in any dialect...
      default:
        return function("e", getDataType());
    }
  }
  @SuppressFBWarnings("SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE")
  public static void execute(
      Configuration jooqConf,
      IndexStorage.CollectionSchema colSchema,
      @Nonnull DatabaseInterface databaseInterface) {

    ConnectionProvider provider = jooqConf.connectionProvider();
    Connection connection = provider.acquire();
    Statement st = null;
    try {
      st = connection.createStatement();
      st.executeUpdate(databaseInterface.dropSchemaStatement(colSchema.getName()));

      DSLContext dsl = DSL.using(jooqConf);
      int deleted =
          dsl.deleteFrom(CollectionsTable.COLLECTIONS)
              .where(CollectionsTable.COLLECTIONS.NAME.eq(colSchema.getCollection()))
              .execute();
      assert deleted == 1;
    } catch (SQLException ex) {
      throw new ToroImplementationException(ex);
    } finally {
      AutoCloser.close(st);
    }
  }
Exemple #7
0
  @Override
  protected void configure() {
    bindFactory(new DSLContextFactory(configuration)).to(DSLContext.class).in(RequestScoped.class);

    // Configuration and ConnectionProvider are single instance, used everywhere

    bind(configuration).to(Configuration.class);

    bind(configuration.connectionProvider()).to(ConnectionProvider.class);
  }
Exemple #8
0
    @Override
    final Field<T> getFunction0(Configuration configuration) {
      SQLDialect family = configuration.family();

      switch (family) {
        case POSTGRES:
          {
            String field = method + "('" + getQualifiedName(configuration) + "')";
            return field(field, getDataType());
          }

        case H2:
          {
            String field = method + "(" + getQualifiedName(configuration, true) + ")";
            return field(field, getDataType());
          }

        case FIREBIRD:
        case DERBY:
        case HSQLDB:
          {
            if ("nextval".equals(method)) {
              String field = "next value for " + getQualifiedName(configuration);
              return field(field, getDataType());
            } else if (family == FIREBIRD) {
              return field("gen_id(" + getQualifiedName(configuration) + ", 0)", getDataType());
            } else {
              throw new SQLDialectNotSupportedException(
                  "The sequence's current value functionality is not supported for the "
                      + family
                      + " dialect.");
            }
          }

        case CUBRID:
          {
            String field = getQualifiedName(configuration) + ".";

            if ("nextval".equals(method)) {
              field += "next_value";
            } else {
              field += "current_value";
            }

            return field(field, getDataType());
          }

          // Default is needed for hashCode() and toString()
        default:
          {
            String field = getQualifiedName(configuration) + "." + method;
            return field(field, getDataType());
          }
      }
    }
Exemple #9
0
  @Override
  public final int[] execute() {

    // [#1180] Run batch queries with BatchMultiple, if no bind variables
    // should be used...
    if (executeStaticStatements(configuration.settings())) {
      return executeStatic();
    } else {
      return executePrepared();
    }
  }
Exemple #10
0
  @Override
  final Field<Integer> getFunction0(Configuration configuration) {
    switch (configuration.family()) {
      case SQLITE:
        switch (datePart) {
          case YEAR:
            return field("{strftime}('%Y', {0})", SQLDataType.INTEGER, field);
          case MONTH:
            return field("{strftime}('%m', {0})", SQLDataType.INTEGER, field);
          case DAY:
            return field("{strftime}('%d', {0})", SQLDataType.INTEGER, field);
          case HOUR:
            return field("{strftime}('%H', {0})", SQLDataType.INTEGER, field);
          case MINUTE:
            return field("{strftime}('%M', {0})", SQLDataType.INTEGER, field);
          case SECOND:
            return field("{strftime}('%S', {0})", SQLDataType.INTEGER, field);
          default:
            throw new SQLDialectNotSupportedException("DatePart not supported: " + datePart);
        }

      case DERBY:
        switch (datePart) {
          case YEAR:
            return function("year", SQLDataType.INTEGER, field);
          case MONTH:
            return function("month", SQLDataType.INTEGER, field);
          case DAY:
            return function("day", SQLDataType.INTEGER, field);
          case HOUR:
            return function("hour", SQLDataType.INTEGER, field);
          case MINUTE:
            return function("minute", SQLDataType.INTEGER, field);
          case SECOND:
            return function("second", SQLDataType.INTEGER, field);
          default:
            throw new SQLDialectNotSupportedException("DatePart not supported: " + datePart);
        }

      case MARIADB:
      case MYSQL:
      case POSTGRES:
      case HSQLDB:
      case H2:

        // A default implementation is necessary for hashCode() and toString()
      default:
        return field("{extract}({" + datePart.toSQL() + " from} {0})", SQLDataType.INTEGER, field);
    }
  }
Exemple #11
0
  private final Table<Record> table(Configuration configuration) {
    switch (configuration.getDialect()) {
      case ORACLE:
        {
          if (array.getDataType().getType().isArray()) {
            return simulate().as(alias);
          } else {
            return new OracleArrayTable().as(alias);
          }
        }

      case H2:
        {
          return new H2ArrayTable().as(alias);
        }

        // [#756] These dialects need special care when aliasing unnested
        // arrays
      case HSQLDB:
      case POSTGRES:
        {
          return new PostgresHSQLDBTable().as(alias);
        }

        // Other dialects can simulate unnested arrays using UNION ALL
      default:
        {
          if (array.getDataType().getType().isArray() && array instanceof Param) {
            return simulate();
          } else {
            throw new SQLDialectNotSupportedException(
                "ARRAY TABLE is not supported for " + configuration.getDialect());
          }
        }
    }
  }
Exemple #12
0
  private final QueryPart delegate(Configuration configuration) {
    switch (configuration.family()) {
      case CUBRID:

        // There is a bug in CUBRID preventing reuse of "level" in the
        // predicate http://jira.cubrid.org/browse/ENGINE-119
        Field<Integer> level = from.add(level()).sub(one());
        return table(
            "({select} {0} {as} {1} {from} {2} {connect by} {level} <= {3})",
            level, name("generate_series"), new Dual(), to.add(one()).sub(from));

      case POSTGRES:
      default:
        return table("{generate_series}({0}, {1})", from, to);
    }
  }
Exemple #13
0
  @Override
  final Field<BigDecimal> getFunction0(Configuration configuration) {
    switch (configuration.dialect().family()) {
      case ASE:
      case CUBRID:
      case HSQLDB:
      case INGRES:
      case MARIADB:
      case MYSQL:
      case POSTGRES:
      case SQLSERVER:
      case SYBASE:
        return DSL.exp(argument.mul(two())).sub(one()).div(DSL.exp(argument.mul(two())).add(one()));

      default:
        return function("tanh", SQLDataType.NUMERIC, argument);
    }
  }
Exemple #14
0
  AbstractContext(Configuration configuration, PreparedStatement stmt) {
    super(configuration);

    this.stmt = stmt;
    this.visitClauses = new ArrayDeque<Clause>();

    VisitListenerProvider[] providers = configuration.visitListenerProviders();

    this.visitListeners = new VisitListener[providers.length + 1];
    this.visitContext = new DefaultVisitContext();
    this.visitParts = new ArrayDeque<QueryPart>();

    for (int i = 0; i < providers.length; i++) {
      this.visitListeners[i] = providers[i].provide();
    }

    this.visitListeners[providers.length] = new InternalVisitListener();
  }
Exemple #15
0
  @SuppressWarnings("unchecked")
  @Override
  final Field<T> getFunction0(Configuration configuration) {

    // In any dialect, a single argument is always the greatest
    if (getArguments().length == 1) {
      return (Field<T>) getArguments()[0];
    }

    switch (configuration.dialect()) {
        // This implementation has O(2^n) complexity. Better implementations
        // are very welcome
        // [#1049] TODO Fix this!

      case ASE:
      case DERBY:
      case SQLSERVER:
      case SYBASE:
        {
          Field<T> first = (Field<T>) getArguments()[0];
          Field<T> other = (Field<T>) getArguments()[1];

          if (getArguments().length > 2) {
            Field<?>[] remaining = new Field[getArguments().length - 2];
            System.arraycopy(getArguments(), 2, remaining, 0, remaining.length);

            return DSL.decode()
                .when(first.greaterThan(other), DSL.greatest(first, remaining))
                .otherwise(DSL.greatest(other, remaining));
          } else {
            return DSL.decode().when(first.greaterThan(other), first).otherwise(other);
          }
        }

      case FIREBIRD:
        return function("maxvalue", getDataType(), getArguments());

      case SQLITE:
        return function("max", getDataType(), getArguments());

      default:
        return function("greatest", getDataType(), getArguments());
    }
  }
  private final void init() {
    int columnCount = 0;

    try {
      columnCount = meta.getColumnCount();
    }

    // This happens in Oracle for empty cursors returned from stored
    // procedures / functions
    catch (SQLException e) {
      log.warn("Cannot fetch column count for cursor : " + e.getMessage());
      fields.add(field("dummy"));
    }

    try {
      for (int i = 1; i <= columnCount; i++) {
        String name = meta.getColumnLabel(i);
        int precision = meta.getPrecision(i);
        int scale = meta.getScale(i);
        DataType<?> dataType = SQLDataType.OTHER;
        String type = meta.getColumnTypeName(i);

        try {
          dataType =
              FieldTypeHelper.getDialectDataType(
                  configuration.getDialect(), type, precision, scale);
        }

        // [#650, #667] TODO This should not happen. All types
        // should be known at this point
        catch (SQLDialectNotSupportedException ignore) {
          log.warn("Not supported by dialect", ignore.getMessage());
        }

        fields.add(field(name, dataType));
      }
    } catch (SQLException e) {
      throw Util.translate(null, e);
    }

    meta = null;
  }
Exemple #17
0
  @Override
  final Field<T> getFunction0(Configuration configuration) {
    switch (configuration.family()) {
      case H2:
      case HSQLDB:
        return field("{nvl}({0}, {1})", getDataType(), arg1, arg2);

      case DERBY:
      case POSTGRES:
        return field("{coalesce}({0}, {1})", getDataType(), arg1, arg2);

      case MARIADB:
      case MYSQL:
      case SQLITE:
        return field("{ifnull}({0}, {1})", getDataType(), arg1, arg2);

      default:
        return DSL.when(arg1.isNotNull(), arg1).otherwise(arg2);
    }
  }
Exemple #18
0
  private Table<?> pivot(Configuration configuration) {
    switch (configuration.dialect()) {

        /* [pro] xx
        xx xxxxxx xxx xxxxxx xxxxxxx xxx xxx xxxxx xxxxxx
        xxxx xxxxxxx
        xxxx xxxxxxxxxx
        xxxx xxxxxxxxxx x
            xxxxxx xxx xxxxxxxxxxxxxxxxxxx
        x

        xx [/pro] */
        // Some other dialects can simulate it. This implementation is
        // EXPERIMENTAL and not officially supported
      default:
        {
          return new DefaultPivotTable();
        }
    }
  }
Exemple #19
0
  @Override
  final Field<String> getFunction0(Configuration configuration) {
    Field<?>[] args = getArguments();

    // [#861] Most dialects don't ship with a two-argument replace function:
    switch (configuration.dialect().family()) {
      case ASE:
        {
          if (args.length == 2) {
            return function("str_replace", VARCHAR, args[0], args[1], val(null));
          } else {
            return function("str_replace", VARCHAR, args);
          }
        }

      case DB2:
      case FIREBIRD:
      case HSQLDB:
      case INGRES:
      case MARIADB:
      case MYSQL:
      case POSTGRES:
      case SQLITE:
      case SQLSERVER:
      case SYBASE:
        {
          if (args.length == 2) {
            return function("replace", VARCHAR, args[0], args[1], val(""));
          } else {
            return function("replace", VARCHAR, args);
          }
        }

      default:
        {
          return function("replace", VARCHAR, args);
        }
    }
  }
Exemple #20
0
  @SuppressWarnings("unchecked")
  @Override
  final Field<T> getFunction0(Configuration configuration) {

    // In any dialect, a single argument is always the least
    if (getArguments().length == 1) {
      return (Field<T>) getArguments()[0];
    }

    switch (configuration.family()) {
        // This implementation has O(2^n) complexity. Better implementations
        // are very welcome

      case DERBY:
        {
          Field<T> first = (Field<T>) getArguments()[0];
          Field<T> other = (Field<T>) getArguments()[1];

          if (getArguments().length > 2) {
            Field<?>[] remaining = new Field<?>[getArguments().length - 2];
            System.arraycopy(getArguments(), 2, remaining, 0, remaining.length);

            return DSL.when(first.lessThan(other), DSL.least(first, remaining))
                .otherwise(DSL.least(other, remaining));
          } else {
            return DSL.when(first.lessThan(other), first).otherwise(other);
          }
        }

      case FIREBIRD:
        return function("minvalue", getDataType(), getArguments());

      case SQLITE:
        return function("min", getDataType(), getArguments());

      default:
        return function("least", getDataType(), getArguments());
    }
  }
Exemple #21
0
  @Override
  final Field<String> getFunction0(Configuration configuration) {
    switch (configuration.family()) {

        // This beautiful expression was contributed by "Ludo", here:
        // http://stackoverflow.com/questions/6576343/how-to-simulate-lpad-rpad-with-sqlite
      case SQLITE:
        {
          return DSL.field(
              "{0} || substr("
                  + "replace("
                  + "replace("
                  + "substr("
                  + "quote("
                  + "zeroblob((({1} - length({0}) - 1 + length({2})) / length({2}) + 1) / 2)"
                  + "), 3"
                  + "), '\''', ''"
                  + "), '0', {2}"
                  + "), 1, ({1} - length({0}))"
                  + ")",
              String.class, field, length, character);
        }

        // According to the Firebird documentation, LPAD outcomes should be
        // cast to truncate large results...
      case FIREBIRD:
        {
          return field(
              "cast(rpad({0}, {1}, {2}) as varchar(4000))",
              SQLDataType.VARCHAR, field, length, character);
        }

      default:
        {
          return function("rpad", SQLDataType.VARCHAR, field, length, character);
        }
    }
  }
  @SuppressWarnings("unchecked")
  @Override
  final Field<T> getFunction0(Configuration configuration) {
    SQLDialect dialect = configuration.getDialect();

    // ---------------------------------------------------------------------
    // XXX: Bitwise operators
    // ---------------------------------------------------------------------

    // DB2, H2 and HSQLDB know functions, instead of operators
    if (BIT_AND == operator && asList(DB2, H2, HSQLDB, ORACLE).contains(dialect)) {
      return function("bitand", getDataType(), getArguments());
    } else if (BIT_AND == operator && FIREBIRD == dialect) {
      return function("bin_and", getDataType(), getArguments());
    } else if (BIT_XOR == operator && asList(DB2, H2, HSQLDB).contains(dialect)) {
      return function("bitxor", getDataType(), getArguments());
    } else if (BIT_XOR == operator && FIREBIRD == dialect) {
      return function("bin_xor", getDataType(), getArguments());
    } else if (BIT_OR == operator && asList(DB2, H2, HSQLDB).contains(dialect)) {
      return function("bitor", getDataType(), getArguments());
    } else if (BIT_OR == operator && FIREBIRD == dialect) {
      return function("bin_or", getDataType(), getArguments());
    }

    // Oracle has to simulate or/xor
    else if (BIT_OR == operator && ORACLE == dialect) {
      return lhs.sub(bitAnd(lhsAsNumber(), rhsAsNumber())).add(rhsAsNumber());
    }

    // ~(a & b) & (a | b)
    else if (BIT_XOR == operator && asList(ORACLE, SQLITE).contains(dialect)) {
      return (Field<T>)
          bitAnd(bitNot(bitAnd(lhsAsNumber(), rhsAsNumber())), bitOr(lhsAsNumber(), rhsAsNumber()));
    }

    // Many dialects don't support shifts. Use multiplication/division instead
    else if (SHL == operator
        && asList(ASE, DB2, H2, HSQLDB, INGRES, ORACLE, SQLSERVER, SYBASE).contains(dialect)) {
      return lhs.mul(Factory.power(two(), rhsAsNumber()));
    } else if (SHR == operator
        && asList(ASE, DB2, H2, HSQLDB, INGRES, ORACLE, SQLSERVER, SYBASE).contains(dialect)) {
      return lhs.div(Factory.power(two(), rhsAsNumber()));
    }

    // Some dialects support shifts as functions
    else if (SHL == operator && FIREBIRD == dialect) {
      return function("bin_shl", getDataType(), getArguments());
    } else if (SHR == operator && FIREBIRD == dialect) {
      return function("bin_shr", getDataType(), getArguments());
    }

    // These operators are not supported in any dialect
    else if (BIT_NAND == operator) {
      return (Field<T>) bitNot(bitAnd(lhsAsNumber(), rhsAsNumber()));
    } else if (BIT_NOR == operator) {
      return (Field<T>) bitNot(bitOr(lhsAsNumber(), rhsAsNumber()));
    } else if (BIT_XNOR == operator) {
      return (Field<T>) bitNot(bitXor(lhsAsNumber(), rhsAsNumber()));
    }

    // ---------------------------------------------------------------------
    // XXX: Date time arithmetic operators
    // ---------------------------------------------------------------------

    // [#585] Date time arithmetic for numeric or interval RHS
    else if (asList(ADD, SUBTRACT).contains(operator)
        && lhs.getDataType().isDateTime()
        && (rhs.get(0).getDataType().isNumeric() || rhs.get(0).getDataType().isInterval())) {

      return new DateExpression();
    }

    // ---------------------------------------------------------------------
    // XXX: Other operators
    // ---------------------------------------------------------------------

    // Use the default operator expression for all other cases
    else {
      return new DefaultExpression();
    }
  }
Exemple #23
0
 @Override
 public Object data(Object key, Object value) {
   return delegate.data(key, value);
 }
Exemple #24
0
 @Override
 public ConnectionProvider connectionProvider() {
   return new MockConnectionProvider(delegate.connectionProvider(), provider);
 }
Exemple #25
0
 @Override
 public ExecutorProvider executorProvider() {
   return delegate.executorProvider();
 }
Exemple #26
0
 @Override
 public TransactionProvider transactionProvider() {
   return delegate.transactionProvider();
 }
Exemple #27
0
 @Override
 public RecordMapperProvider recordMapperProvider() {
   return delegate.recordMapperProvider();
 }
Exemple #28
0
 @Override
 public RecordListenerProvider[] recordListenerProviders() {
   return delegate.recordListenerProviders();
 }
    /** Return the expression to be rendered when the RHS is an interval type */
    private final Field<T> getIntervalExpression(Configuration configuration) {
      SQLDialect dialect = configuration.getDialect();
      int sign = (operator == ADD) ? 1 : -1;

      switch (dialect) {
        case ASE:
        case SYBASE:
        case SQLSERVER:
          {
            if (rhs.get(0).getType() == YearToMonth.class) {
              return field(
                  "{dateadd}(mm, {0}, {1})", getDataType(), val(sign * rhsAsYTM().intValue()), lhs);
            } else {
              // SQL Server needs this cast.
              Field<Timestamp> lhsAsTS = lhs.cast(Timestamp.class);
              DayToSecond interval = rhsAsDTS();

              // Be careful with 32-bit INT arithmetic. Sybase ASE
              // may fatally overflow when using micro-second precision
              if (interval.getNano() != 0) {
                return field(
                    "{dateadd}(ss, {0}, {dateadd}(us, {1}, {2}))",
                    getDataType(),
                    val(sign * (long) interval.getTotalSeconds()),
                    val(sign * interval.getMicro()),
                    lhsAsTS);
              } else {
                return field(
                    "{dateadd}(ss, {0}, {1})",
                    getDataType(), val(sign * (long) interval.getTotalSeconds()), lhsAsTS);
              }
            }
          }

        case CUBRID:
        case MYSQL:
          {
            Interval interval = rhsAsInterval();

            if (operator == SUBTRACT) {
              interval = interval.neg();
            }

            if (rhs.get(0).getType() == YearToMonth.class) {
              return field(
                  "{date_add}({0}, {interval} {1} {year_month})",
                  getDataType(), lhs, val(interval, String.class));
            } else {
              if (dialect == MYSQL) {
                return field(
                    "{date_add}({0}, {interval} {1} {day_microsecond})",
                    getDataType(), lhs, val(interval, String.class));
              } else {
                return field(
                    "{date_add}({0}, {interval} {1} {day_millisecond})",
                    getDataType(), lhs, val(interval, String.class));
              }
            }
          }

        case DB2:
          {
            if (rhs.get(0).getType() == YearToMonth.class) {
              if (operator == ADD) {
                return lhs.add(field("{0} month", val(rhsAsYTM().intValue())));
              } else {
                return lhs.sub(field("{0} month", val(rhsAsYTM().intValue())));
              }
            } else {
              // DB2 needs this cast if lhs is of type DATE.
              DataType<T> type = lhs.getDataType();

              if (operator == ADD) {
                return lhs.cast(Timestamp.class)
                    .add(field("{0} microseconds", val(rhsAsDTS().getTotalMicro())))
                    .cast(type);
              } else {
                return lhs.cast(Timestamp.class)
                    .sub(field("{0} microseconds", val(rhsAsDTS().getTotalMicro())))
                    .cast(type);
              }
            }
          }

        case DERBY:
        case HSQLDB:
          {
            if (rhs.get(0).getType() == YearToMonth.class) {
              return field(
                  "{fn {timestampadd}({sql_tsi_month}, {0}, {1}) }",
                  getDataType(), val(sign * rhsAsYTM().intValue()), lhs);
            } else {
              return field(
                  "{fn {timestampadd}({sql_tsi_second}, {0}, {1}) }",
                  getDataType(), val(sign * (long) rhsAsDTS().getTotalSeconds()), lhs);
            }
          }

        case FIREBIRD:
          {
            if (rhs.get(0).getType() == YearToMonth.class) {
              return field(
                  "{dateadd}({month}, {0}, {1})",
                  getDataType(), val(sign * rhsAsYTM().intValue()), lhs);
            } else {
              return field(
                  "{dateadd}({millisecond}, {0}, {1})",
                  getDataType(), val(sign * (long) rhsAsDTS().getTotalMilli()), lhs);
            }
          }

        case H2:
          {
            if (rhs.get(0).getType() == YearToMonth.class) {
              return field(
                  "{dateadd}('month', {0}, {1})",
                  getDataType(), val(sign * rhsAsYTM().intValue()), lhs);
            } else {
              return field(
                  "{dateadd}('ms', {0}, {1})",
                  getDataType(), val(sign * (long) rhsAsDTS().getTotalMilli()), lhs);
            }
          }

        case INGRES:
          {
            throw new SQLDialectNotSupportedException(
                "Date time arithmetic not supported in Ingres. Contributions welcome!");
          }

        case SQLITE:
          {
            String prefix = (sign > 0) ? "+" : "-";

            if (rhs.get(0).getType() == YearToMonth.class) {
              return field(
                  "{datetime}({0}, '" + prefix + rhsAsYTM().intValue() + " months')",
                  getDataType(),
                  lhs);
            } else {
              return field(
                  "{datetime}({0}, '" + prefix + rhsAsDTS().getTotalSeconds() + " seconds')",
                  getDataType(),
                  lhs);
            }
          }

        case ORACLE:
        case POSTGRES:
        default:
          return new DefaultExpression();
      }
    }
    /** Return the expression to be rendered when the RHS is a number type */
    private final Field<T> getNumberExpression(Configuration configuration) {
      switch (configuration.getDialect()) {
        case ASE:
        case FIREBIRD:
        case SQLSERVER:
        case SYBASE:
          {
            if (operator == ADD) {
              return field("{dateadd}(day, {0}, {1})", getDataType(), rhsAsNumber(), lhs);
            } else {
              return field("{dateadd}(day, {0}, {1})", getDataType(), rhsAsNumber().neg(), lhs);
            }
          }

        case DB2:
        case HSQLDB:
          {
            if (operator == ADD) {
              return lhs.add(field("{0} day", rhsAsNumber()));
            } else {
              return lhs.sub(field("{0} day", rhsAsNumber()));
            }
          }

        case DERBY:
          {
            if (operator == ADD) {
              return field(
                  "{fn {timestampadd}({sql_tsi_day}, {0}, {1}) }",
                  getDataType(), rhsAsNumber(), lhs);
            } else {
              return field(
                  "{fn {timestampadd}({sql_tsi_day}, {0}, {1}) }",
                  getDataType(), rhsAsNumber().neg(), lhs);
            }
          }

        case CUBRID:
        case MYSQL:
          {
            if (operator == ADD) {
              return field(
                  "{date_add}({0}, {interval} {1} {day})", getDataType(), lhs, rhsAsNumber());
            } else {
              return field(
                  "{date_add}({0}, {interval} {1} {day})", getDataType(), lhs, rhsAsNumber().neg());
            }
          }

          // Ingres is not working yet
        case INGRES:
          {
            if (operator == ADD) {
              return lhs.add(field("{date}({0} || ' days')", Object.class, rhsAsNumber()));
            } else {
              return lhs.sub(field("{date}({0} || ' days')", Object.class, rhsAsNumber()));
            }
          }

        case POSTGRES:
          {

            // This seems to be the most reliable way to avoid issues
            // with incompatible data types and timezones
            // ? + CAST (? || ' days' as interval)
            if (operator == ADD) {
              return lhs.add(rhsAsNumber().concat(" day").cast(DayToSecond.class));
            } else {
              return lhs.sub(rhsAsNumber().concat(" day").cast(DayToSecond.class));
            }
          }

        case SQLITE:
          if (operator == ADD) {
            return field("{datetime}({0}, '+" + rhsAsNumber() + " day')", getDataType(), lhs);
          } else {
            return field("{datetime}({0}, '-" + rhsAsNumber() + " day')", getDataType(), lhs);
          }

          // These dialects can add / subtract days using +/- operators
        case H2:
        case ORACLE:
        default:
          return new DefaultExpression();
      }
    }