[jOOQ/jOOQ#8754] Remove more SQL templating

Removes SQL templating in expression, date, and timestamp related
functions as well as in a few other classes.
This commit is contained in:
Knut Wannheden 2019-06-20 17:03:34 +02:00
parent 070bb2cf99
commit 5076b5eeff
17 changed files with 484 additions and 324 deletions

View File

@ -15929,7 +15929,7 @@ public class DSL {
*/
@Support({ H2, HSQLDB, POSTGRES })
public static Field<Date> toDate(Field<String> value, Field<String> format) {
return DSL.field("{to_date}({0}, {1})", SQLDataType.DATE, nullSafe(value), nullSafe(format));
return function("to_date", SQLDataType.DATE, nullSafe(value), nullSafe(format));
}
/**
@ -15973,7 +15973,7 @@ public class DSL {
*/
@Support({ H2, HSQLDB, POSTGRES })
public static Field<Timestamp> toTimestamp(Field<String> value, Field<String> format) {
return DSL.field("{to_timestamp}({0}, {1})", SQLDataType.TIMESTAMP, nullSafe(value), nullSafe(format));
return function("to_timestamp", SQLDataType.TIMESTAMP, nullSafe(value), nullSafe(format));
}
@ -17580,7 +17580,7 @@ public class DSL {
*/
@Support({ CUBRID })
public static Field<String> sysConnectByPath(Field<?> field, String separator) {
return field("{sys_connect_by_path}({0}, {1})", String.class, field, inline(separator));
return function("sys_connect_by_path", String.class, field, inline(separator));
}
/**

View File

@ -38,18 +38,31 @@
package org.jooq.impl;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.keyword;
import static org.jooq.impl.DSL.sql;
import static org.jooq.impl.Keywords.F_ADD_MONTHS;
import static org.jooq.impl.Keywords.F_DATEADD;
import static org.jooq.impl.Keywords.F_DATE_ADD;
import static org.jooq.impl.Keywords.F_STRFTIME;
import static org.jooq.impl.Keywords.F_TIMESTAMPADD;
import static org.jooq.impl.Keywords.K_AS;
import static org.jooq.impl.Keywords.K_CAST;
import static org.jooq.impl.Keywords.K_DAY;
import static org.jooq.impl.Keywords.K_HOUR;
import static org.jooq.impl.Keywords.K_INTERVAL;
import static org.jooq.impl.Keywords.K_MINUTE;
import static org.jooq.impl.Keywords.K_MONTH;
import static org.jooq.impl.Keywords.K_SECOND;
import static org.jooq.impl.Keywords.K_YEAR;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.DatePart;
import org.jooq.Field;
import org.jooq.QueryPart;
import org.jooq.Keyword;
/**
* @author Lukas Eder
*/
final class DateAdd<T> extends AbstractFunction<T> {
final class DateAdd<T> extends AbstractField<T> {
/**
* Generated UID
@ -61,7 +74,7 @@ final class DateAdd<T> extends AbstractFunction<T> {
private final DatePart datePart;
DateAdd(Field<T> date, Field<? extends Number> interval, DatePart datePart) {
super("dateadd", date.getDataType());
super(DSL.name("dateadd"), date.getDataType());
this.date = date;
this.interval = interval;
@ -69,10 +82,11 @@ final class DateAdd<T> extends AbstractFunction<T> {
}
@Override
final QueryPart getFunction0(Configuration configuration) {
String keyword = null;
public final void accept(Context<?> ctx) {
Keyword keyword = null;
String string = null;
switch (configuration.family()) {
switch (ctx.family()) {
@ -81,59 +95,63 @@ final class DateAdd<T> extends AbstractFunction<T> {
case MARIADB:
case MYSQL: {
switch (datePart) {
case YEAR: keyword = "year"; break;
case MONTH: keyword = "month"; break;
case DAY: keyword = "day"; break;
case HOUR: keyword = "hour"; break;
case MINUTE: keyword = "minute"; break;
case SECOND: keyword = "second"; break;
case YEAR: keyword = K_YEAR; break;
case MONTH: keyword = K_MONTH; break;
case DAY: keyword = K_DAY; break;
case HOUR: keyword = K_HOUR; break;
case MINUTE: keyword = K_MINUTE; break;
case SECOND: keyword = K_SECOND; break;
default: throwUnsupported();
}
return DSL.field("{date_add}({0}, {interval} {1} {2})", getDataType(), date, interval, keyword(keyword));
ctx.visit(F_DATE_ADD).sql('(').visit(date).sql(", ").visit(K_INTERVAL).sql(' ').visit(interval).sql(' ').visit(keyword).sql(')');
break;
}
case DERBY:
case HSQLDB: {
switch (datePart) {
case YEAR: keyword = "sql_tsi_year"; break;
case MONTH: keyword = "sql_tsi_month"; break;
case DAY: keyword = "sql_tsi_day"; break;
case HOUR: keyword = "sql_tsi_hour"; break;
case MINUTE: keyword = "sql_tsi_minute"; break;
case SECOND: keyword = "sql_tsi_second"; break;
case YEAR: keyword = DSL.keyword("sql_tsi_year"); break;
case MONTH: keyword = DSL.keyword("sql_tsi_month"); break;
case DAY: keyword = DSL.keyword("sql_tsi_day"); break;
case HOUR: keyword = DSL.keyword("sql_tsi_hour"); break;
case MINUTE: keyword = DSL.keyword("sql_tsi_minute"); break;
case SECOND: keyword = DSL.keyword("sql_tsi_second"); break;
default: throwUnsupported();
}
return DSL.field("{fn {timestampadd}({0}, {1}, {2}) }", getDataType(), keyword(keyword), interval, date);
ctx.sql("{fn ").visit(F_TIMESTAMPADD).sql('(').visit(keyword).sql(", ").visit(interval).sql(", ").visit(date).sql(") }");
break;
}
case FIREBIRD: {
switch (datePart) {
case YEAR: keyword = "year"; break;
case MONTH: keyword = "month"; break;
case DAY: keyword = "day"; break;
case HOUR: keyword = "hour"; break;
case MINUTE: keyword = "minute"; break;
case SECOND: keyword = "second"; break;
case YEAR: keyword = K_YEAR; break;
case MONTH: keyword = K_MONTH; break;
case DAY: keyword = K_DAY; break;
case HOUR: keyword = K_HOUR; break;
case MINUTE: keyword = K_MINUTE; break;
case SECOND: keyword = K_SECOND; break;
default: throwUnsupported();
}
return DSL.field("{dateadd}({0}, {1}, {2})", getDataType(), keyword(keyword), interval, date);
ctx.visit(F_DATEADD).sql('(').visit(keyword).sql(", ").visit(interval).sql(", ").visit(date).sql(')');
break;
}
case H2: {
switch (datePart) {
case YEAR: keyword = "year"; break;
case MONTH: keyword = "month"; break;
case DAY: keyword = "day"; break;
case HOUR: keyword = "hour"; break;
case MINUTE: keyword = "minute"; break;
case SECOND: keyword = "second"; break;
case YEAR: string = "year"; break;
case MONTH: string = "month"; break;
case DAY: string = "day"; break;
case HOUR: string = "hour"; break;
case MINUTE: string = "minute"; break;
case SECOND: string = "second"; break;
default: throwUnsupported();
}
return DSL.field("{dateadd}({0}, {1}, {2})", getDataType(), inline(keyword), interval, date);
ctx.visit(F_DATEADD).sql('(').visit(inline(string)).sql(", ").visit(interval).sql(", ").visit(date).sql(')');
break;
}
@ -146,35 +164,37 @@ final class DateAdd<T> extends AbstractFunction<T> {
case POSTGRES: {
switch (datePart) {
case YEAR: keyword = " year"; break;
case MONTH: keyword = " month"; break;
case DAY: keyword = " day"; break;
case HOUR: keyword = " hour"; break;
case MINUTE: keyword = " minute"; break;
case SECOND: keyword = " second"; break;
case YEAR: string = " year"; break;
case MONTH: string = " month"; break;
case DAY: string = " day"; break;
case HOUR: string = " hour"; break;
case MINUTE: string = " minute"; break;
case SECOND: string = " second"; break;
default: throwUnsupported();
}
// [#3824] Ensure that the output for DATE arithmetic will also
// be of type DATE, not TIMESTAMP
if (getDataType().isDate())
return DSL.field("({0} + ({1} || {2})::interval)::date", getDataType(), date, interval, inline(keyword));
ctx.sql('(').visit(date).sql(" + (").visit(interval).sql(" || ").visit(inline(string)).sql(")::interval)::date");
else
return DSL.field("({0} + ({1} || {2})::interval)", getDataType(), date, interval, inline(keyword));
ctx.sql('(').visit(date).sql(" + (").visit(interval).sql(" || ").visit(inline(string)).sql(")::interval)");
break;
}
case SQLITE: {
switch (datePart) {
case YEAR: keyword = " year"; break;
case MONTH: keyword = " month"; break;
case DAY: keyword = " day"; break;
case HOUR: keyword = " hour"; break;
case MINUTE: keyword = " minute"; break;
case SECOND: keyword = " second"; break;
case YEAR: string = " year"; break;
case MONTH: string = " month"; break;
case DAY: string = " day"; break;
case HOUR: string = " hour"; break;
case MINUTE: string = " minute"; break;
case SECOND: string = " second"; break;
default: throwUnsupported();
}
return DSL.field("{strftime}('%Y-%m-%d %H:%M:%f', {0}, {1})", getDataType(), date, interval.concat(inline(keyword)));
ctx.visit(F_STRFTIME).sql("('%Y-%m-%d %H:%M:%f', ").visit(date).sql(", ").visit(interval.concat(inline(string))).sql(')');
break;
}
@ -285,6 +305,17 @@ final class DateAdd<T> extends AbstractFunction<T> {
@ -298,8 +329,6 @@ final class DateAdd<T> extends AbstractFunction<T> {
}
return null;
}
private final void throwUnsupported() {

View File

@ -38,15 +38,21 @@
package org.jooq.impl;
import static org.jooq.impl.DSL.function;
import static org.jooq.impl.DSL.keyword;
import static org.jooq.impl.Keywords.F_DATEDIFF;
import static org.jooq.impl.Keywords.F_DAYS_BETWEEN;
import static org.jooq.impl.Keywords.F_STRFTIME;
import static org.jooq.impl.Keywords.F_TIMESTAMPDIFF;
import static org.jooq.impl.Keywords.K_DAY;
import static org.jooq.impl.Tools.castIfNeeded;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.Field;
/**
* @author Lukas Eder
*/
final class DateDiff<T> extends AbstractFunction<Integer> {
final class DateDiff<T> extends AbstractField<Integer> {
/**
* Generated UID
@ -57,38 +63,43 @@ final class DateDiff<T> extends AbstractFunction<Integer> {
private final Field<T> date2;
DateDiff(Field<T> date1, Field<T> date2) {
super("datediff", SQLDataType.INTEGER, date1, date2);
super(DSL.name("datediff"), SQLDataType.INTEGER);
this.date1 = date1;
this.date2 = date2;
}
@Override
final Field<Integer> getFunction0(Configuration configuration) {
switch (configuration.family()) {
public final void accept(Context<?> ctx) {
switch (ctx.family()) {
case MARIADB:
case MYSQL:
return function("datediff", getDataType(), date1, date2);
ctx.visit(F_DATEDIFF).sql('(').visit(date1).sql(", ").visit(date2).sql(')');
break;
case DERBY:
return DSL.field("{fn {timestampdiff}({sql_tsi_day}, {0}, {1}) }", getDataType(), date2, date1);
ctx.sql("{fn ").visit(F_TIMESTAMPDIFF).sql('(').visit(keyword("sql_tsi_day")).sql(", ").visit(date2).sql(", ").visit(date1).sql(") }");
break;
case FIREBIRD:
return DSL.field("{datediff}(day, {0}, {1})", getDataType(), date2, date1);
ctx.visit(F_DATEDIFF).sql('(').visit(K_DAY).sql(", ").visit(date2).sql(", ").visit(date1).sql(')');
break;
case H2:
case HSQLDB:
return DSL.field("{datediff}('day', {0}, {1})", getDataType(), date2, date1);
ctx.visit(F_DATEDIFF).sql("('day', ").visit(date2).sql(", ").visit(date1).sql(')');
break;
case SQLITE:
return DSL.field("({strftime}('%s', {0}) - {strftime}('%s', {1})) / 86400", getDataType(), date1, date2);
ctx.sql('(').visit(F_STRFTIME).sql("('%s', ").visit(date1).sql(") - ").visit(F_STRFTIME).sql("('%s', ").visit(date2).sql(")) / 86400");
break;
@ -99,7 +110,8 @@ final class DateDiff<T> extends AbstractFunction<Integer> {
// [#4481] Parentheses are important in case this expression is
// placed in the context of other arithmetic
return DSL.field("({0} - {1})", getDataType(), date1, date2);
ctx.sql('(').visit(date1).sql(" - ").visit(date2).sql(')');
break;
@ -122,9 +134,13 @@ final class DateDiff<T> extends AbstractFunction<Integer> {
default:
// Default implementation for equals() and hashCode()
ctx.visit(castIfNeeded(date1.sub(date2), Integer.class));
break;
}
// Default implementation for equals() and hashCode()
return castIfNeeded(date1.sub(date2), Integer.class);
}
}

View File

@ -59,6 +59,7 @@ import static org.jooq.SQLDialect.SQLITE;
// ...
import static org.jooq.impl.DSL.function;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.keyword;
import static org.jooq.impl.DSL.two;
import static org.jooq.impl.DSL.val;
import static org.jooq.impl.ExpressionOperator.ADD;
@ -71,6 +72,21 @@ import static org.jooq.impl.ExpressionOperator.BIT_XOR;
import static org.jooq.impl.ExpressionOperator.SHL;
import static org.jooq.impl.ExpressionOperator.SHR;
import static org.jooq.impl.ExpressionOperator.SUBTRACT;
import static org.jooq.impl.Keywords.F_DATEADD;
import static org.jooq.impl.Keywords.F_DATE_ADD;
import static org.jooq.impl.Keywords.F_STRFTIME;
import static org.jooq.impl.Keywords.F_TIMESTAMPADD;
import static org.jooq.impl.Keywords.K_AS;
import static org.jooq.impl.Keywords.K_CAST;
import static org.jooq.impl.Keywords.K_DAY;
import static org.jooq.impl.Keywords.K_DAY_MICROSECOND;
import static org.jooq.impl.Keywords.K_DAY_MILLISECOND;
import static org.jooq.impl.Keywords.K_DAY_TO_SECOND;
import static org.jooq.impl.Keywords.K_INTERVAL;
import static org.jooq.impl.Keywords.K_MILLISECOND;
import static org.jooq.impl.Keywords.K_MONTH;
import static org.jooq.impl.Keywords.K_YEAR_MONTH;
import static org.jooq.impl.Keywords.K_YEAR_TO_MONTH;
import static org.jooq.impl.Tools.castIfNeeded;
import java.sql.Timestamp;
@ -92,7 +108,7 @@ import org.jooq.types.Interval;
import org.jooq.types.YearToMonth;
import org.jooq.types.YearToSecond;
final class Expression<T> extends AbstractFunction<T> {
final class Expression<T> extends AbstractField<T> {
/**
* Generated UID
@ -106,14 +122,16 @@ final class Expression<T> extends AbstractFunction<T> {
private final Field<T> lhs;
private final QueryPartList<Field<?>> rhs;
private final Field<?>[] arguments;
private final ExpressionOperator operator;
Expression(ExpressionOperator operator, Field<T> lhs, Field<?>... rhs) {
super(operator.toSQL(), lhs.getDataType(), Tools.combine(lhs, rhs));
super(DSL.name(operator.toSQL()), lhs.getDataType());
this.operator = operator;
this.lhs = lhs;
this.rhs = new QueryPartList<Field<?>>(rhs);
this.arguments = Tools.combine(lhs, rhs);
}
@Override
@ -138,8 +156,8 @@ final class Expression<T> extends AbstractFunction<T> {
@SuppressWarnings("unchecked")
@Override
final Field<T> getFunction0(Configuration configuration) {
SQLDialect family = configuration.family();
public final void accept(Context<?> ctx) {
SQLDialect family = ctx.family();
// ---------------------------------------------------------------------
// XXX: Bitwise operators
@ -147,17 +165,17 @@ final class Expression<T> extends AbstractFunction<T> {
// DB2, H2 and HSQLDB know functions, instead of operators
if (BIT_AND == operator && SUPPORT_BIT_AND.contains(family))
return function("bitand", getDataType(), getArguments());
ctx.visit(function("bitand", getDataType(), arguments));
else if (BIT_AND == operator && FIREBIRD == family)
return function("bin_and", getDataType(), getArguments());
ctx.visit(function("bin_and", getDataType(), arguments));
else if (BIT_XOR == operator && SUPPORT_BIT_OR_XOR.contains(family))
return function("bitxor", getDataType(), getArguments());
ctx.visit(function("bitxor", getDataType(), arguments));
else if (BIT_XOR == operator && FIREBIRD == family)
return function("bin_xor", getDataType(), getArguments());
ctx.visit(function("bin_xor", getDataType(), arguments));
else if (BIT_OR == operator && SUPPORT_BIT_OR_XOR.contains(family))
return function("bitor", getDataType(), getArguments());
ctx.visit(function("bitor", getDataType(), arguments));
else if (BIT_OR == operator && FIREBIRD == family)
return function("bin_or", getDataType(), getArguments());
ctx.visit(function("bin_or", getDataType(), arguments));
@ -167,9 +185,9 @@ final class Expression<T> extends AbstractFunction<T> {
// ~(a & b) & (a | b)
else if (BIT_XOR == operator && EMULATE_BIT_XOR.contains(family))
return (Field<T>) DSL.bitAnd(
ctx.visit(DSL.bitAnd(
DSL.bitNot(DSL.bitAnd(lhsAsNumber(), rhsAsNumber())),
DSL.bitOr(lhsAsNumber(), rhsAsNumber()));
DSL.bitOr(lhsAsNumber(), rhsAsNumber())));
@ -180,26 +198,26 @@ final class Expression<T> extends AbstractFunction<T> {
// Many dialects don't support shifts. Use multiplication/division instead
else if (SHL == operator && EMULATE_SHR_SHL.contains(family))
return lhs.mul((Field<? extends Number>) castIfNeeded(DSL.power(two(), rhsAsNumber()), lhs));
ctx.visit(lhs.mul((Field<? extends Number>) castIfNeeded(DSL.power(two(), rhsAsNumber()), lhs)));
// [#3962] This emulation is expensive. If this is emulated, BitCount should
// use division instead of SHR directly
else if (SHR == operator && EMULATE_SHR_SHL.contains(family))
return lhs.div((Field<? extends Number>) castIfNeeded(DSL.power(two(), rhsAsNumber()), lhs));
ctx.visit(lhs.div((Field<? extends Number>) castIfNeeded(DSL.power(two(), rhsAsNumber()), lhs)));
// Some dialects support shifts as functions
else if (SHL == operator && FIREBIRD == family)
return function("bin_shl", getDataType(), getArguments());
ctx.visit(function("bin_shl", getDataType(), arguments));
else if (SHR == operator && FIREBIRD == family)
return function("bin_shr", getDataType(), getArguments());
ctx.visit(function("bin_shr", getDataType(), arguments));
// These operators are not supported in any dialect
else if (BIT_NAND == operator)
return (Field<T>) DSL.bitNot(DSL.bitAnd(lhsAsNumber(), rhsAsNumber()));
ctx.visit(DSL.bitNot(DSL.bitAnd(lhsAsNumber(), rhsAsNumber())));
else if (BIT_NOR == operator)
return (Field<T>) DSL.bitNot(DSL.bitOr(lhsAsNumber(), rhsAsNumber()));
ctx.visit(DSL.bitNot(DSL.bitOr(lhsAsNumber(), rhsAsNumber())));
else if (BIT_XNOR == operator)
return (Field<T>) DSL.bitNot(DSL.bitXor(lhsAsNumber(), rhsAsNumber()));
ctx.visit(DSL.bitNot(DSL.bitXor(lhsAsNumber(), rhsAsNumber())));
// ---------------------------------------------------------------------
// XXX: Date time arithmetic operators
@ -210,7 +228,7 @@ final class Expression<T> extends AbstractFunction<T> {
lhs.getDataType().isDateTime() &&
(rhs.get(0).getDataType().isNumeric() ||
rhs.get(0).getDataType().isInterval()))
return new DateExpression<T>(lhs, operator, rhs.get(0));
ctx.visit(new DateExpression<T>(lhs, operator, rhs.get(0)));
// ---------------------------------------------------------------------
// XXX: Other operators
@ -218,7 +236,7 @@ final class Expression<T> extends AbstractFunction<T> {
// Use the default operator expression for all other cases
else
return new DefaultExpression<T>(lhs, operator, rhs);
ctx.visit(new DefaultExpression<T>(lhs, operator, rhs));
}
/**
@ -243,7 +261,7 @@ final class Expression<T> extends AbstractFunction<T> {
/**
* Return the expression to be rendered when the RHS is an interval type
*/
private static class DateExpression<T> extends AbstractFunction<T> {
private static class DateExpression<T> extends AbstractField<T> {
/**
* Generated UID
@ -255,7 +273,7 @@ final class Expression<T> extends AbstractFunction<T> {
private final Field<?> rhs;
DateExpression(Field<T> lhs, ExpressionOperator operator, Field<?> rhs) {
super(operator.toSQL(), lhs.getDataType());
super(DSL.name(operator.toSQL()), lhs.getDataType());
this.lhs = lhs;
this.operator = operator;
@ -272,11 +290,11 @@ final class Expression<T> extends AbstractFunction<T> {
}
@Override
final Field<T> getFunction0(Configuration configuration) {
public final void accept(Context<?> ctx) {
if (rhs.getDataType().isInterval())
return getIntervalExpression(configuration);
acceptIntervalExpression(ctx);
else
return getNumberExpression(configuration);
acceptNumberExpression(ctx);
}
private final Field<T> getYTSExpression() {
@ -290,9 +308,8 @@ final class Expression<T> extends AbstractFunction<T> {
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private final Field<T> getIntervalExpression(Configuration configuration) {
SQLDialect dialect = configuration.dialect();
SQLDialect family = dialect.family();
private final void acceptIntervalExpression(Context<?> ctx) {
SQLDialect family = ctx.family();
int sign = (operator == ADD) ? 1 : -1;
switch (family) {
@ -303,8 +320,10 @@ final class Expression<T> extends AbstractFunction<T> {
case CUBRID:
case MARIADB:
case MYSQL: {
if (rhs.getType() == YearToSecond.class)
return getYTSExpression();
if (rhs.getType() == YearToSecond.class) {
ctx.visit(getYTSExpression());
break;
}
Interval interval = rhsAsInterval();
@ -312,62 +331,74 @@ final class Expression<T> extends AbstractFunction<T> {
interval = interval.neg();
if (rhs.getType() == YearToMonth.class)
return DSL.field("{date_add}({0}, {interval} {1} {year_month})", getDataType(), lhs, Tools.field(interval, SQLDataType.VARCHAR));
else if (dialect == CUBRID)
return DSL.field("{date_add}({0}, {interval} {1} {day_millisecond})", getDataType(), lhs, Tools.field(interval, SQLDataType.VARCHAR));
ctx.visit(F_DATE_ADD).sql('(').visit(lhs).sql(", ").visit(K_INTERVAL).sql(' ')
.visit(Tools.field(interval, SQLDataType.VARCHAR)).sql(' ').visit(K_YEAR_MONTH).sql(')');
else if (family == CUBRID)
ctx.visit(F_DATE_ADD).sql('(').visit(lhs).sql(", ").visit(K_INTERVAL).sql(' ')
.visit(Tools.field(interval, SQLDataType.VARCHAR)).sql(' ').visit(K_DAY_MILLISECOND).sql(')');
// [#6820] Workaround for bugs:
// https://bugs.mysql.com/bug.php?id=88573
// https://jira.mariadb.org/browse/MDEV-14452
else
return DSL.field("{date_add}({0}, {interval} {1} {day_microsecond})", getDataType(), lhs,
Tools.field(TRUNC_TO_MICROS.matcher("" + interval).replaceAll("$1"), SQLDataType.VARCHAR)
);
ctx.visit(F_DATE_ADD).sql('(').visit(lhs).sql(", ").visit(K_INTERVAL).sql(' ')
.visit(Tools.field(TRUNC_TO_MICROS.matcher("" + interval).replaceAll("$1"), SQLDataType.VARCHAR)).sql(' ').visit(K_DAY_MICROSECOND).sql(')');
break;
}
case DERBY:
case HSQLDB: {
if (rhs.getType() == YearToSecond.class)
return getYTSExpression();
if (rhs.getType() == YearToSecond.class) {
ctx.visit(getYTSExpression());
break;
}
Field<T> result;
boolean needsCast = getDataType().getType() != Timestamp.class;
if (needsCast)
ctx.visit(K_CAST).sql('(');
if (rhs.getType() == YearToMonth.class)
result = DSL.field("{fn {timestampadd}({sql_tsi_month}, {0}, {1}) }",
getDataType(), p(sign * rhsAsYTM().intValue()), lhs);
ctx.sql("{fn ").visit(F_TIMESTAMPADD).sql('(').visit(keyword("sql_tsi_month")).sql(", ")
.visit(p(sign * rhsAsYTM().intValue())).sql(", ").visit(lhs).sql(") }");
else
result = DSL.field("{fn {timestampadd}({sql_tsi_second}, {0}, {fn {timestampadd}({sql_tsi_milli_second}, {1}, {2}) }) }",
getDataType(),
p(sign * (long) rhsAsDTS().getTotalSeconds()),
p(sign * (long) rhsAsDTS().getMilli()),
lhs);
ctx.sql("{fn ").visit(F_TIMESTAMPADD).sql('(').visit(keyword("sql_tsi_second")).sql(", ")
.visit(p(sign * (long) rhsAsDTS().getTotalSeconds())).sql(", {fn ")
.visit(F_TIMESTAMPADD).sql('(').visit(keyword("sql_tsi_milli_second")).sql(", ")
.visit(p(sign * (long) rhsAsDTS().getMilli())).sql(", ").visit(lhs).sql(") }) }");
// [#1883] TIMESTAMPADD returns TIMESTAMP columns. If this
// is a DATE column, cast it to DATE
return castNonTimestamps(configuration, result);
if (needsCast)
ctx.sql(' ').visit(K_AS).sql(' ').visit(keyword(getDataType().getCastTypeName(ctx.configuration()))).sql(')');
break;
}
case FIREBIRD: {
if (rhs.getType() == YearToSecond.class)
return getYTSExpression();
ctx.visit(getYTSExpression());
else if (rhs.getType() == YearToMonth.class)
return DSL.field("{dateadd}({month}, {0}, {1})", getDataType(), p(sign * rhsAsYTM().intValue()), lhs);
ctx.visit(F_DATEADD).sql('(').visit(K_MONTH).sql(", ").visit(p(sign * rhsAsYTM().intValue())).sql(", ").visit(lhs).sql(')');
else
return DSL.field("{dateadd}({millisecond}, {0}, {1})", getDataType(), p(sign * (long) rhsAsDTS().getTotalMilli()), lhs);
ctx.visit(F_DATEADD).sql('(').visit(K_MILLISECOND).sql(", ").visit(p(sign * (long) rhsAsDTS().getTotalMilli())).sql(", ").visit(lhs).sql(')');
break;
}
case H2: {
if (rhs.getType() == YearToSecond.class)
return getYTSExpression();
ctx.visit(getYTSExpression());
else if (rhs.getType() == YearToMonth.class)
return DSL.field("{dateadd}('month', {0}, {1})", getDataType(), p(sign * rhsAsYTM().intValue()), lhs);
ctx.visit(F_DATEADD).sql("('month', ").visit(p(sign * rhsAsYTM().intValue())).sql(", ").visit(lhs).sql(')');
else
return DSL.field("{dateadd}('ms', {0}, {1})", getDataType(), p(sign * (long) rhsAsDTS().getTotalMilli()), lhs);
ctx.visit(F_DATEADD).sql("('ms', ").visit(p(sign * (long) rhsAsDTS().getTotalMilli())).sql(", ").visit(lhs).sql(')');
break;
}
case SQLITE: {
if (rhs.getType() == YearToSecond.class)
return getYTSExpression();
if (rhs.getType() == YearToSecond.class) {
ctx.visit(getYTSExpression());
break;
}
boolean ytm = rhs.getType() == YearToMonth.class;
Field<?> interval = p(ytm ? rhsAsYTM().intValue() : rhsAsDTS().getTotalSeconds());
@ -376,7 +407,8 @@ final class Expression<T> extends AbstractFunction<T> {
interval = interval.neg();
interval = interval.concat(inline(ytm ? " months" : " seconds"));
return DSL.field("{strftime}('%Y-%m-%d %H:%M:%f', {0}, {1})", getDataType(), lhs, interval);
ctx.visit(F_STRFTIME).sql("('%Y-%m-%d %H:%M:%f', ").visit(lhs).sql(", ").visit(interval).sql(')');
break;
}
@ -531,11 +563,13 @@ final class Expression<T> extends AbstractFunction<T> {
case POSTGRES:
default:
return new DefaultExpression<T>(lhs, operator, new QueryPartList<Field<?>>(Arrays.asList(rhs)));
ctx.visit(new DefaultExpression<T>(lhs, operator, new QueryPartList<Field<?>>(Arrays.asList(rhs))));
break;
}
}
@ -554,8 +588,10 @@ final class Expression<T> extends AbstractFunction<T> {
* Return the expression to be rendered when the RHS is a number type
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private final Field<T> getNumberExpression(Configuration configuration) {
switch (configuration.family()) {
private final void acceptNumberExpression(Context<?> ctx) {
switch (ctx.family()) {
@ -578,9 +614,10 @@ final class Expression<T> extends AbstractFunction<T> {
case FIREBIRD: {
if (operator == ADD)
return DSL.field("{dateadd}(day, {0}, {1})", getDataType(), rhsAsNumber(), lhs);
ctx.visit(F_DATEADD).sql('(').visit(K_DAY).sql(", ").visit(rhsAsNumber()).sql(", ").visit(lhs).sql(')');
else
return DSL.field("{dateadd}(day, {0}, {1})", getDataType(), rhsAsNumber().neg(), lhs);
ctx.visit(F_DATEADD).sql('(').visit(K_DAY).sql(", ").visit(rhsAsNumber().neg()).sql(", ").visit(lhs).sql(')');
break;
}
@ -588,22 +625,28 @@ final class Expression<T> extends AbstractFunction<T> {
case HSQLDB: {
if (operator == ADD)
return lhs.add(DSL.field("{0} day", rhsAsNumber()));
ctx.visit(lhs.add(DSL.field("{0} day", rhsAsNumber())));
else
return lhs.sub(DSL.field("{0} day", rhsAsNumber()));
ctx.visit(lhs.sub(DSL.field("{0} day", rhsAsNumber())));
break;
}
case DERBY: {
Field<T> result;
boolean needsCast = getDataType().getType() != Timestamp.class;
if (needsCast)
ctx.visit(K_CAST).sql('(');
if (operator == ADD)
result = DSL.field("{fn {timestampadd}({sql_tsi_day}, {0}, {1}) }", getDataType(), rhsAsNumber(), lhs);
ctx.sql("{fn ").visit(F_TIMESTAMPADD).sql('(').visit(keyword("sql_tsi_day")).sql(", ").visit(rhsAsNumber()).sql(", ").visit(lhs).sql(") }");
else
result = DSL.field("{fn {timestampadd}({sql_tsi_day}, {0}, {1}) }", getDataType(), rhsAsNumber().neg(), lhs);
ctx.sql("{fn ").visit(F_TIMESTAMPADD).sql('(').visit(keyword("sql_tsi_day")).sql(", ").visit(rhsAsNumber().neg()).sql(", ").visit(lhs).sql(") }");
// [#1883] TIMESTAMPADD returns TIMESTAMP columns. If this
// is a DATE column, cast it to DATE
return castNonTimestamps(configuration, result);
if (needsCast)
ctx.sql(' ').visit(K_AS).sql(' ').visit(keyword(getDataType().getCastTypeName(ctx.configuration()))).sql(')');
break;
}
@ -614,9 +657,10 @@ final class Expression<T> extends AbstractFunction<T> {
case MARIADB:
case MYSQL: {
if (operator == ADD)
return DSL.field("{date_add}({0}, {interval} {1} {day})", getDataType(), lhs, rhsAsNumber());
ctx.visit(F_DATE_ADD).sql('(').visit(lhs).sql(", ").visit(K_INTERVAL).sql(' ').visit(rhsAsNumber()).sql(' ').visit(K_DAY).sql(')');
else
return DSL.field("{date_add}({0}, {interval} {1} {day})", getDataType(), lhs, rhsAsNumber().neg());
ctx.visit(F_DATE_ADD).sql('(').visit(lhs).sql(", ").visit(K_INTERVAL).sql(' ').visit(rhsAsNumber().neg()).sql(' ').visit(K_DAY).sql(')');
break;
}
@ -636,6 +680,8 @@ final class Expression<T> extends AbstractFunction<T> {
case POSTGRES: {
@ -644,16 +690,19 @@ final class Expression<T> extends AbstractFunction<T> {
// with incompatible data types and timezones
// ? + CAST (? || ' days' as interval)
if (operator == ADD)
return new DateAdd(lhs, rhsAsNumber(), DatePart.DAY);
ctx.visit(new DateAdd(lhs, rhsAsNumber(), DatePart.DAY));
else
return new DateAdd(lhs, rhsAsNumber().neg(), DatePart.DAY);
ctx.visit(new DateAdd(lhs, rhsAsNumber().neg(), DatePart.DAY));
break;
}
case SQLITE:
if (operator == ADD)
return DSL.field("{strftime}('%Y-%m-%d %H:%M:%f', {0}, {1})", getDataType(), lhs, rhsAsNumber().concat(inline(" day")));
ctx.visit(F_STRFTIME).sql("('%Y-%m-%d %H:%M:%f', ").visit(lhs).sql(", ").visit(rhsAsNumber().concat(inline(" day"))).sql(')');
else
return DSL.field("{strftime}('%Y-%m-%d %H:%M:%f', {0}, {1})", getDataType(), lhs, rhsAsNumber().neg().concat(inline(" day")));
ctx.visit(F_STRFTIME).sql("('%Y-%m-%d %H:%M:%f', ").visit(lhs).sql(", ").visit(rhsAsNumber().neg().concat(inline(" day"))).sql(')');
break;
@ -667,7 +716,8 @@ final class Expression<T> extends AbstractFunction<T> {
case H2:
default:
return new DefaultExpression<T>(lhs, operator, new QueryPartList<Field<?>>(Arrays.asList(rhs)));
ctx.visit(new DefaultExpression<T>(lhs, operator, new QueryPartList<Field<?>>(Arrays.asList(rhs))));
break;
}
}

View File

@ -38,24 +38,42 @@
package org.jooq.impl;
// ...
import static org.jooq.impl.DSL.function;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.keyword;
import static org.jooq.impl.DSL.one;
import static org.jooq.impl.Keywords.F_DATEDIFF;
import static org.jooq.impl.Keywords.F_DATEPART;
import static org.jooq.impl.Keywords.F_DAYOFWEEK;
import static org.jooq.impl.Keywords.F_EXTRACT;
import static org.jooq.impl.Keywords.F_STRFTIME;
import static org.jooq.impl.Keywords.F_TO_CHAR;
import static org.jooq.impl.Keywords.F_TO_NUMBER;
import static org.jooq.impl.Keywords.F_TRUNC;
import static org.jooq.impl.Keywords.K_DATE;
import static org.jooq.impl.Keywords.K_DAY;
import static org.jooq.impl.Keywords.K_FROM;
import static org.jooq.impl.Keywords.K_HOUR;
import static org.jooq.impl.Keywords.K_INT;
import static org.jooq.impl.Keywords.K_MINUTE;
import static org.jooq.impl.Keywords.K_MONTH;
import static org.jooq.impl.Keywords.K_SECOND;
import static org.jooq.impl.Keywords.K_YEAR;
import static org.jooq.impl.SQLDataType.INTEGER;
import static org.jooq.impl.SQLDataType.VARCHAR;
import static org.jooq.impl.Tools.castIfNeeded;
import java.sql.Date;
import java.sql.Timestamp;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.DatePart;
import org.jooq.Field;
/**
* @author Lukas Eder
*/
final class Extract extends AbstractFunction<Integer> {
final class Extract extends AbstractField<Integer> {
private static final long serialVersionUID = 3748640920856031034L;
@ -63,48 +81,67 @@ final class Extract extends AbstractFunction<Integer> {
private final DatePart datePart;
Extract(Field<?> field, DatePart datePart) {
super("extract", INTEGER, field);
super(DSL.name("extract"), INTEGER);
this.field = field;
this.datePart = datePart;
}
@Override
final Field<Integer> getFunction0(Configuration configuration) {
switch (configuration.family()) {
public final void accept(Context<?> ctx) {
switch (ctx.family()) {
case SQLITE:
switch (datePart) {
case QUARTER:
case DECADE:
case CENTURY:
case MILLENNIUM:
return getDefaultEmulation();
case YEAR:
return DSL.field("{strftime}('%Y', {0})", INTEGER, field);
ctx.visit(F_STRFTIME).sql("('%Y', ").visit(field).sql(')');
return;
case MONTH:
return DSL.field("{strftime}('%m', {0})", INTEGER, field);
ctx.visit(F_STRFTIME).sql("('%m', ").visit(field).sql(')');
return;
case DAY:
return DSL.field("{strftime}('%d', {0})", INTEGER, field);
ctx.visit(F_STRFTIME).sql("('%d', ").visit(field).sql(')');
return;
case HOUR:
return DSL.field("{strftime}('%H', {0})", INTEGER, field);
ctx.visit(F_STRFTIME).sql("('%H', ").visit(field).sql(')');
return;
case MINUTE:
return DSL.field("{strftime}('%M', {0})", INTEGER, field);
ctx.visit(F_STRFTIME).sql("('%M', ").visit(field).sql(')');
return;
case SECOND:
return DSL.field("{strftime}('%S', {0})", INTEGER, field);
ctx.visit(F_STRFTIME).sql("('%S', ").visit(field).sql(')');
return;
// See: https://www.sqlite.org/lang_datefunc.html
case EPOCH:
return DSL.field("{strftime}('%s', {0})", INTEGER, field);
ctx.visit(F_STRFTIME).sql("('%s', ").visit(field).sql(')');
return;
case ISO_DAY_OF_WEEK:
return dowSun0ToISO(DSL.field("{strftime}('%w', {0})", INTEGER, field));
ctx.visit(dowSun0ToISO(function("strftime", INTEGER, inline("%w"), field)));
return;
case DAY_OF_WEEK:
return DSL.field("{strftime}('%w', {0})", INTEGER, field).add(one());
ctx.visit(F_STRFTIME).sql("('%w', ").visit(field).sql(") + ").visit(one());
return;
case DAY_OF_YEAR:
return DSL.field("{strftime}('%j', {0})", INTEGER, field);
default:
return getNativeFunction();
ctx.visit(F_STRFTIME).sql("('%j', ").visit(field).sql(')');
return;
}
break;
@ -134,48 +171,26 @@ final class Extract extends AbstractFunction<Integer> {
case DERBY:
switch (datePart) {
case DECADE:
case CENTURY:
case MILLENNIUM:
return getDefaultEmulation();
case YEAR:
return function("year", INTEGER, field);
ctx.visit(K_YEAR).sql('(').visit(field).sql(')');
return;
case MONTH:
return function("month", INTEGER, field);
ctx.visit(K_MONTH).sql('(').visit(field).sql(')');
return;
case DAY:
return function("day", INTEGER, field);
ctx.visit(K_DAY).sql('(').visit(field).sql(')');
return;
case HOUR:
return function("hour", INTEGER, field);
ctx.visit(K_HOUR).sql('(').visit(field).sql(')');
return;
case MINUTE:
return function("minute", INTEGER, field);
ctx.visit(K_MINUTE).sql('(').visit(field).sql(')');
return;
case SECOND:
return function("second", INTEGER, field);
ctx.visit(K_SECOND).sql('(').visit(field).sql(')');
return;
}
return getDefaultEmulation();
break;
@ -277,81 +292,68 @@ final class Extract extends AbstractFunction<Integer> {
case MARIADB:
case MYSQL:
switch (datePart) {
case DECADE:
case CENTURY:
case MILLENNIUM:
return getDefaultEmulation();
case DAY_OF_WEEK:
return DSL.field("{dayofweek}({0})", INTEGER, field);
ctx.visit(F_DAYOFWEEK).sql('(').visit(field).sql(')');
return;
case DAY_OF_YEAR:
return DSL.field("{dayofyear}({0})", INTEGER, field);
ctx.visit(keyword("dayofyear")).sql('(').visit(field).sql(')');
return;
case EPOCH:
return DSL.field("{unix_timestamp}({0})", INTEGER, field);
ctx.visit(keyword("unix_timestamp")).sql('(').visit(field).sql(')');
return;
case ISO_DAY_OF_WEEK:
return DSL.field("{weekday}({0})", INTEGER, field).add(one());
ctx.visit(keyword("weekday")).sql('(').visit(field).sql(") + 1");
return;
case QUARTER:
return DSL.field("{quarter}({0})", INTEGER, field);
default:
return getNativeFunction();
ctx.visit(datePart.toKeyword()).sql('(').visit(field).sql(')');
return;
}
break;
case POSTGRES:
switch (datePart) {
case DAY_OF_WEEK:
return DSL.field("({extract}({dow from} {0}) + 1)", INTEGER, field);
ctx.sql('(').visit(F_EXTRACT).sql('(').visit(keyword("dow")).sql(' ').visit(K_FROM).sql(' ').visit(field).sql(") + 1)");
return;
case DAY_OF_YEAR:
return DSL.field("{extract}({doy from} {0})", INTEGER, field);
ctx.visit(F_EXTRACT).sql('(').visit(keyword("doy")).sql(' ').visit(K_FROM).sql(' ').visit(field).sql(')');
return;
case ISO_DAY_OF_WEEK:
return DSL.field("{extract}({isodow from} {0})", INTEGER, field);
default:
return getNativeFunction();
ctx.visit(F_EXTRACT).sql('(').visit(keyword("isodow")).sql(' ').visit(K_FROM).sql(' ').visit(field).sql(')');
return;
}
break;
case HSQLDB:
switch (datePart) {
case DECADE:
case CENTURY:
case MILLENNIUM:
case TIMEZONE:
return getDefaultEmulation();
case EPOCH:
return DSL.field("{unix_timestamp}({0})", INTEGER, field);
ctx.visit(keyword("unix_timestamp")).sql('(').visit(field).sql(')');
return;
case ISO_DAY_OF_WEEK:
return dowSun1ToISO(DSL.field("{extract}({day_of_week from} {0})", INTEGER, field));
ctx.visit(dowSun1ToISO(DSL.field("{extract}({day_of_week from} {0})", INTEGER, field)));
return;
case QUARTER:
return DSL.field("{quarter}({0})", INTEGER, field);
case WEEK:
return DSL.field("{week}({0})", INTEGER, field);
default:
return getNativeFunction();
ctx.visit(datePart.toKeyword()).sql('(').visit(field).sql(')');
return;
}
break;
case H2:
switch (datePart) {
case DECADE:
case CENTURY:
case MILLENNIUM:
case TIMEZONE:
return getDefaultEmulation();
case QUARTER:
return DSL.field("{quarter}({0})", INTEGER, field);
ctx.visit(datePart.toKeyword()).sql('(').visit(field).sql(')');
return;
case WEEK:
return DSL.field("{iso_week}({0})", INTEGER, field);
default:
return getNativeFunction();
ctx.visit(keyword("iso_week")).sql('(').visit(field).sql(')');
return;
}
default:
return getNativeFunction();
break;
}
acceptDefaultEmulation(ctx);
}
private final static Field<Integer> dowISOToSun1(Field<Integer> dow) {
@ -366,31 +368,37 @@ final class Extract extends AbstractFunction<Integer> {
return dow.add(inline(6)).mod(inline(7)).add(one());
}
private final Field<Integer> getDefaultEmulation() {
private final void acceptDefaultEmulation(Context<?> ctx) {
switch (datePart) {
case DECADE:
return castIfNeeded(DSL.year(field).div(inline(10)), INTEGER);
ctx.visit(castIfNeeded(DSL.year(field).div(inline(10)), INTEGER));
break;
case CENTURY:
return castIfNeeded(
ctx.visit(castIfNeeded(
DSL.sign(DSL.year(field))
.mul(DSL.abs(DSL.year(field)).add(inline(99)))
.div(inline(100)), INTEGER);
.div(inline(100)), INTEGER));
break;
case MILLENNIUM:
return castIfNeeded(
ctx.visit(castIfNeeded(
DSL.sign(DSL.year(field))
.mul(DSL.abs(DSL.year(field)).add(inline(999)))
.div(inline(1000)), INTEGER);
.div(inline(1000)), INTEGER));
break;
case QUARTER:
return DSL.month(field).add(inline(2)).div(inline(3));
ctx.visit(DSL.month(field).add(inline(2)).div(inline(3)));
break;
case TIMEZONE:
return DSL.extract(field, DatePart.TIMEZONE_HOUR).mul(inline(3600))
.add(DSL.extract(field, DatePart.TIMEZONE_MINUTE).mul(inline(60)));
ctx.visit(DSL.extract(field, DatePart.TIMEZONE_HOUR).mul(inline(3600))
.add(DSL.extract(field, DatePart.TIMEZONE_MINUTE).mul(inline(60))));
break;
default:
return getNativeFunction();
acceptNativeFunction(ctx);
break;
}
}
private final Field<Integer> getNativeFunction() {
return DSL.field("{extract}({0} {from} {1})", INTEGER, datePart.toKeyword(), field);
private final void acceptNativeFunction(Context<?> ctx) {
ctx.visit(F_EXTRACT).sql('(').visit(datePart.toKeyword()).sql(' ').visit(K_FROM).sql(' ').visit(field).sql(')');
}
}

View File

@ -37,14 +37,11 @@
*/
package org.jooq.impl;
import static org.jooq.impl.DSL.condition;
import static org.jooq.impl.DSL.inline;
import org.jooq.Clause;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.Field;
import org.jooq.QueryPartInternal;
/**
* @author Lukas Eder
@ -63,16 +60,7 @@ final class FieldCondition extends AbstractCondition {
@Override
public void accept(Context<?> ctx) {
ctx.visit(delegate(ctx.configuration()));
}
@Override // Avoid AbstractCondition implementation
public final Clause[] clauses(Context<?> ctx) {
return null;
}
private final QueryPartInternal delegate(Configuration configuration) {
switch (configuration.family()) {
switch (ctx.family()) {
// [#2485] These don't work nicely, yet
case CUBRID:
@ -90,7 +78,8 @@ final class FieldCondition extends AbstractCondition {
return (QueryPartInternal) condition("{0} = {1}", field, inline(true));
ctx.sql('(').visit(field).sql(" = ").visit(inline(true)).sql(')');
break;
@ -108,7 +97,14 @@ final class FieldCondition extends AbstractCondition {
case POSTGRES:
case SQLITE:
default:
return (QueryPartInternal) field;
ctx.visit(field);
break;
}
}
@Override // Avoid AbstractCondition implementation
public final Clause[] clauses(Context<?> ctx) {
return null;
}
}

View File

@ -105,6 +105,10 @@ final class Keywords {
static final Keyword K_DATE = keyword("date");
static final Keyword K_DATETIME = keyword("datetime");
static final Keyword K_DATETIMEOFFSET = keyword("datetimeoffset");
static final Keyword K_DAY = keyword("day");
static final Keyword K_DAY_MICROSECOND = keyword("day_microsecond");
static final Keyword K_DAY_MILLISECOND = keyword("day_millisecond");
static final Keyword K_DAY_TO_SECOND = keyword("day_to_second");
static final Keyword K_DECIMAL = keyword("decimal");
static final Keyword K_DECLARE = keyword("declare");
static final Keyword K_DEFAULT = keyword("default");
@ -167,6 +171,7 @@ final class Keywords {
static final Keyword K_GRANT_OPTION_FOR = keyword("grant option for");
static final Keyword K_GROUP_BY = keyword("group by");
static final Keyword K_HAVING = keyword("having");
static final Keyword K_HOUR = keyword("hour");
static final Keyword K_HOUR_TO_SECOND = keyword("hour to second");
static final Keyword K_IDENTITY = keyword("identity");
static final Keyword K_IF = keyword("if");
@ -182,6 +187,7 @@ final class Keywords {
static final Keyword K_INNER_JOIN = keyword("inner join");
static final Keyword K_INSERT = keyword("insert");
static final Keyword K_INT = keyword("int");
static final Keyword K_INTERVAL = keyword("interval");
static final Keyword K_INTO = keyword("into");
static final Keyword K_IS = keyword("is");
static final Keyword K_IS_NOT_NULL = keyword("is not null");
@ -202,10 +208,13 @@ final class Keywords {
static final Keyword K_MATCHED = keyword("matched");
static final Keyword K_MAXVALUE = keyword("maxvalue");
static final Keyword K_MERGE_INTO = keyword("merge into");
static final Keyword K_MILLISECOND = keyword("millisecond");
static final Keyword K_MINUS = keyword("minus");
static final Keyword K_MINUTE = keyword("minute");
static final Keyword K_MINVALUE = keyword("minvalue");
static final Keyword K_MOD = keyword("mod");
static final Keyword K_MODIFY = keyword("modify");
static final Keyword K_MONTH = keyword("month");
static final Keyword K_MULTISET = keyword("multiset");
static final Keyword K_NEW_TABLE = keyword("new table");
static final Keyword K_NEXT_VALUE_FOR = keyword("next value for");
@ -286,6 +295,7 @@ final class Keywords {
static final Keyword K_SCHEMA = keyword("schema");
static final Keyword K_SCN = keyword("scn");
static final Keyword K_SEARCH_PATH = keyword("search_path");
static final Keyword K_SECOND = keyword("second");
static final Keyword K_SELECT = keyword("select");
static final Keyword K_SEPARATOR = keyword("separator");
static final Keyword K_SEQUENCE = keyword("sequence");
@ -350,10 +360,14 @@ final class Keywords {
static final Keyword K_WITH_TIES = keyword("with ties");
static final Keyword K_WITHIN_GROUP = keyword("within group");
static final Keyword K_XMLTABLE = keyword("xmltable");
static final Keyword K_YEAR = keyword("year");
static final Keyword K_YEAR_MONTH = keyword("year_month");
static final Keyword K_YEAR_TO_DAY = keyword("year to day");
static final Keyword K_YEAR_TO_MONTH = keyword("year to month");
static final Keyword K_YEAR_TO_FRACTION = keyword("year to fraction");
static final Keyword F_ACOS = keyword("acos");
static final Keyword F_ADD_MONTHS = keyword("add_months");
static final Keyword F_ASC = keyword("asc");
static final Keyword F_ASCII = keyword("ascii");
static final Keyword F_ASCII_VAL = keyword("ascii_val");
@ -376,7 +390,16 @@ final class Keywords {
static final Keyword F_CURRENT_TIMESTAMP = keyword("current_timestamp");
static final Keyword F_CURRENT_USER = keyword("current_user");
static final Keyword F_CURRENTUSER = keyword("currentuser");
static final Keyword F_DATE_ADD = keyword("date_add");
static final Keyword F_DATE_DIFF = keyword("date_diff");
static final Keyword F_DATE_TRUNC = keyword("date_trunc");
static final Keyword F_DATEADD = keyword("dateadd");
static final Keyword F_DATEDIFF = keyword("datediff");
static final Keyword F_DATEPART = keyword("datepart");
static final Keyword F_DAYOFWEEK = keyword("dayofweek");
static final Keyword F_DAYS_BETWEEN = keyword("days_between");
static final Keyword F_DEGREES = keyword("degrees");
static final Keyword F_EXTRACT = keyword("extract");
static final Keyword F_FLOOR = keyword("floor");
static final Keyword F_GEN_ID = keyword("gen_id");
static final Keyword F_HASHBYTES = keyword("hashbytes");
@ -396,12 +419,14 @@ final class Keywords {
static final Keyword F_MID = keyword("mid");
static final Keyword F_NOW = keyword("now");
static final Keyword F_NULLIF = keyword("nullif");
static final Keyword F_NUMTODSINTERVAL = keyword("numtodsinterval");
static final Keyword F_NVL = keyword("nvl");
static final Keyword F_NVL2 = keyword("nvl2");
static final Keyword F_POSITION = keyword("position");
static final Keyword F_POWER = keyword("power");
static final Keyword F_RAND = keyword("rand");
static final Keyword F_RANDOM = keyword("random");
static final Keyword F_RATIO_TO_REPORT = keyword("ratio_to_report");
static final Keyword F_RAWTOHEX = keyword("rawtohex");
static final Keyword F_REPEAT = keyword("repeat");
static final Keyword F_REPLACE = keyword("replace");
@ -409,6 +434,7 @@ final class Keywords {
static final Keyword F_REVERSE = keyword("reverse");
static final Keyword F_RIGHT = keyword("right");
static final Keyword F_RND = keyword("rnd");
static final Keyword F_ROLLUP = keyword("rollup");
static final Keyword F_ROUND = keyword("round");
static final Keyword F_ROUND_DOWN = keyword("round_down");
static final Keyword F_RPAD = keyword("rpad");
@ -427,9 +453,12 @@ final class Keywords {
static final Keyword F_SUBSTRING = keyword("substring");
static final Keyword F_SYSDATE = keyword("sysdate");
static final Keyword F_TANH = keyword("tanh");
static final Keyword F_TIMESTAMPADD = keyword("timestampadd");
static final Keyword F_TIMESTAMPDIFF = keyword("timestampdiff");
static final Keyword F_TO_CHAR = keyword("to_char");
static final Keyword F_TO_CLOB = keyword("to_clob");
static final Keyword F_TO_DATE = keyword("to_date");
static final Keyword F_TO_NUMBER = keyword("to_number");
static final Keyword F_TO_TIMESTAMP = keyword("to_timestamp");
static final Keyword F_TRIM = keyword("trim");
static final Keyword F_TRUNC = keyword("trunc");

View File

@ -38,7 +38,6 @@
package org.jooq.impl;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.keyword;
import static org.jooq.impl.Keywords.K_COALESCE;
import org.jooq.Context;

View File

@ -41,6 +41,7 @@ import static org.jooq.Clause.CONDITION;
import static org.jooq.Clause.CONDITION_NOT;
import static org.jooq.impl.DSL.condition;
import static org.jooq.impl.DSL.not;
import static org.jooq.impl.Keywords.K_NOT;
import org.jooq.Clause;
import org.jooq.Context;
@ -95,7 +96,7 @@ final class NotField extends AbstractField<Boolean> {
case POSTGRES:
case SQLITE:
default:
ctx.visit(DSL.field("{not}({0})", getDataType(), field));
ctx.visit(K_NOT).sql('(').visit(field).sql(')');
break;
}
}

View File

@ -38,6 +38,7 @@
package org.jooq.impl;
import static org.jooq.SQLDialect.SQLITE;
import static org.jooq.impl.Keywords.F_RATIO_TO_REPORT;
import static org.jooq.impl.SQLDataType.DECIMAL;
import static org.jooq.impl.SQLDataType.DOUBLE;
import static org.jooq.impl.Tools.castIfNeeded;

View File

@ -39,6 +39,7 @@ package org.jooq.impl;
import static org.jooq.Clause.CONDITION;
import static org.jooq.Clause.CONDITION_COMPARISON;
import static org.jooq.impl.DSL.keyword;
import static org.jooq.impl.Keywords.K_LIKE_REGEX;
import static org.jooq.impl.Keywords.K_REGEXP;
@ -92,8 +93,7 @@ final class RegexpLike extends AbstractCondition {
// [#620] HSQLDB has its own syntax
case HSQLDB: {
// [#1570] TODO: Replace this by SQL.condition(String, QueryPart...)
ctx.visit(DSL.condition("{regexp_matches}({0}, {1})", search, pattern));
ctx.visit(keyword("regexp_matches")).sql('(').visit(search).sql(", ").visit(pattern).sql(')');
break;
}
@ -104,8 +104,7 @@ final class RegexpLike extends AbstractCondition {
case POSTGRES: {
// [#1570] TODO: Replace this by SQL.condition(String, QueryPart...)
ctx.visit(DSL.condition("{0} ~ {1}", search, pattern));
ctx.visit(search).sql(" ~ ").visit(pattern);
break;
}
@ -119,7 +118,6 @@ final class RegexpLike extends AbstractCondition {
// Render the SQL standard for those databases that do not support
// regular expressions

View File

@ -37,12 +37,11 @@
*/
package org.jooq.impl;
import static org.jooq.impl.Keywords.F_ROLLUP;
import static org.jooq.impl.Keywords.K_WITH_ROLLUP;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.FieldOrRow;
import org.jooq.QueryPart;
/**
* @author Lukas Eder
@ -63,21 +62,19 @@ final class Rollup extends AbstractField<Object> {
@Override
public final void accept(Context<?> ctx) {
ctx.visit(delegate(ctx.configuration()));
}
private final QueryPart delegate(Configuration configuration) {
switch (configuration.family()) {
switch (ctx.family()) {
case CUBRID:
case MARIADB:
case MYSQL:
return new MySQLWithRollup();
ctx.visit(new MySQLWithRollup());
break;
default:
return DSL.field("{rollup}({0})", Object.class, arguments);
ctx.visit(F_ROLLUP).sql('(').visit(arguments).sql(')');
break;
}
}

View File

@ -38,7 +38,6 @@
package org.jooq.impl;
import static org.jooq.impl.DSL.row;
import static org.jooq.impl.DSL.sql;
import org.jooq.Clause;
import org.jooq.Comparator;
@ -72,7 +71,7 @@ final class TableComparison<R extends Record> extends AbstractCondition {
case POSTGRES: {
ctx.visit(DSL.condition("{0} {1} {2}", lhs, sql(comparator.toSQL()), rhs));
ctx.visit(lhs).sql(' ').sql(comparator.toSQL()).sql(' ').visit(rhs);
break;
}

View File

@ -37,6 +37,7 @@
*/
package org.jooq.impl;
import org.jooq.Keyword;
import org.jooq.Name;
// ...
import org.jooq.SQLDialect;
@ -312,11 +313,13 @@ enum Term {
;
private final Name name;
private final String translation;
private final Name name;
private final Keyword keyword;
private final String translation;
private Term() {
this.name = DSL.name(name());
this.keyword = DSL.keyword(name());
this.translation = name().toLowerCase();
}
@ -329,6 +332,10 @@ enum Term {
return name;
}
public Keyword toKeyword() {
return keyword;
}
/**
* Translate the term to its dialect-specific variant.
*

View File

@ -38,17 +38,22 @@
package org.jooq.impl;
import static org.jooq.impl.DSL.function;
import static org.jooq.impl.DSL.keyword;
import static org.jooq.impl.Keywords.F_DATEDIFF;
import static org.jooq.impl.Keywords.F_STRFTIME;
import static org.jooq.impl.Keywords.F_TIMESTAMPDIFF;
import static org.jooq.impl.Keywords.K_MILLISECOND;
import static org.jooq.impl.SQLDataType.INTEGER;
import static org.jooq.impl.Tools.castIfNeeded;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.Field;
import org.jooq.types.DayToSecond;
/**
* @author Lukas Eder
*/
final class TimestampDiff extends AbstractFunction<DayToSecond> {
final class TimestampDiff extends AbstractField<DayToSecond> {
/**
* Generated UID
@ -59,16 +64,19 @@ final class TimestampDiff extends AbstractFunction<DayToSecond> {
private final Field<?> timestamp2;
TimestampDiff(Field<?> timestamp1, Field<?> timestamp2) {
super("timestampdiff", SQLDataType.INTERVALDAYTOSECOND, timestamp1, timestamp2);
super(DSL.name("timestampdiff"), SQLDataType.INTERVALDAYTOSECOND);
this.timestamp1 = timestamp1;
this.timestamp2 = timestamp2;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
final Field<DayToSecond> getFunction0(Configuration configuration) {
switch (configuration.family()) {
public final void accept(Context<?> ctx) {
switch (ctx.family()) {
@ -106,25 +114,30 @@ final class TimestampDiff extends AbstractFunction<DayToSecond> {
// [#4481] Parentheses are important in case this expression is
// placed in the context of other arithmetic
return DSL.field("({0} - {1})", getDataType(), timestamp1, timestamp2);
ctx.sql('(').visit(timestamp1).sql(" - ").visit(timestamp2).sql(')');
break;
// CUBRID's datetime operations operate on a millisecond level
case CUBRID:
return (Field) timestamp1.sub(timestamp2);
ctx.visit(timestamp1.sub(timestamp2));
break;
case DERBY:
return (Field) DSL.field("1000 * {fn {timestampdiff}({sql_tsi_second}, {0}, {1}) }", INTEGER, timestamp2, timestamp1);
ctx.sql("1000 * {fn ").visit(F_TIMESTAMPDIFF).sql('(').visit(keyword("sql_tsi_second")).sql(", ").visit(timestamp2).sql(", ").visit(timestamp1).sql(") }");
break;
case FIREBIRD:
return DSL.field("{datediff}(millisecond, {0}, {1})", getDataType(), timestamp2, timestamp1);
ctx.visit(F_DATEDIFF).sql('(').visit(K_MILLISECOND).sql(", ").visit(timestamp2).sql(", ").visit(timestamp1).sql(')');
break;
case H2:
case HSQLDB:
return DSL.field("{datediff}('ms', {0}, {1})", getDataType(), timestamp2, timestamp1);
ctx.visit(F_DATEDIFF).sql("('ms', ").visit(timestamp2).sql(", ").visit(timestamp1).sql(')');
break;
// MySQL's datetime operations operate on a microsecond level
@ -134,18 +147,21 @@ final class TimestampDiff extends AbstractFunction<DayToSecond> {
case MARIADB:
case MYSQL:
return DSL.field("{timestampdiff}(microsecond, {0}, {1}) / 1000", getDataType(), timestamp2, timestamp1);
ctx.visit(F_TIMESTAMPDIFF).sql('(').visit(keyword("microsecond")).sql(", ").visit(timestamp2).sql(", ").visit(timestamp1).sql(") / 1000");
break;
case SQLITE:
return DSL.field("({strftime}('%s', {0}) - {strftime}('%s', {1})) * 1000", getDataType(), timestamp1, timestamp2);
ctx.sql('(').visit(F_STRFTIME).sql("('%s', ").visit(timestamp1).sql(") - ").visit(F_STRFTIME).sql("('%s', ").visit(timestamp2).sql(")) * 1000");
break;
default:
// Default implementation for equals() and hashCode()
ctx.visit(castIfNeeded(timestamp1.sub(timestamp2), DayToSecond.class));
break;
}
// Default implementation for equals() and hashCode()
return castIfNeeded(timestamp1.sub(timestamp2), DayToSecond.class);
}
}

View File

@ -38,19 +38,21 @@
package org.jooq.impl;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.Keywords.F_DATE_TRUNC;
import static org.jooq.impl.Keywords.F_TRUNC;
import static org.jooq.impl.Keywords.K_CAST;
import static org.jooq.impl.Tools.castIfNeeded;
import java.sql.Date;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.DatePart;
import org.jooq.Field;
import org.jooq.QueryPart;
/**
* @author Lukas Eder
*/
final class TruncDate<T> extends AbstractFunction<T> {
final class TruncDate<T> extends AbstractField<T> {
/**
* Generated UID
@ -61,18 +63,18 @@ final class TruncDate<T> extends AbstractFunction<T> {
private final DatePart part;
TruncDate(Field<T> date, DatePart part) {
super("trunc", date.getDataType());
super(DSL.name("trunc"), date.getDataType());
this.date = date;
this.part = part;
}
@Override
final QueryPart getFunction0(Configuration configuration) {
public final void accept(Context<?> ctx) {
String keyword = null;
String format = null;
switch (configuration.family()) {
switch (ctx.family()) {
// [http://jira.cubrid.org/browse/ENGINE-120] This currently doesn't work for all date parts in CUBRID
case CUBRID:
@ -87,7 +89,8 @@ final class TruncDate<T> extends AbstractFunction<T> {
default: throwUnsupported();
}
return DSL.field("{trunc}({0}, {1})", getDataType(), date, inline(keyword));
ctx.visit(F_TRUNC).sql('(').visit(date).sql(", ").visit(inline(keyword)).sql(')');
break;
}
case H2: {
@ -101,7 +104,9 @@ final class TruncDate<T> extends AbstractFunction<T> {
default: throwUnsupported();
}
return DSL.field("{parsedatetime}({formatdatetime}({0}, {1}), {1})", getDataType(), date, inline(format));
ctx.visit(DSL.keyword("parsedatetime")).sql('(')
.visit(DSL.keyword("formatdatetime")).sql('(').visit(date).sql(", ").visit(inline(format)).sql("), ").visit(inline(format)).sql(')');
break;
}
// These don't work yet and need better integration-testing:
@ -136,7 +141,8 @@ final class TruncDate<T> extends AbstractFunction<T> {
default: throwUnsupported();
}
return DSL.field("{date_trunc}({0}, {1})", getDataType(), inline(keyword), date);
ctx.visit(F_DATE_TRUNC).sql('(').visit(inline(keyword)).sql(", ").visit(date).sql(')');
break;
}
// These don't work yet and need better integration-testing:
@ -220,6 +226,12 @@ final class TruncDate<T> extends AbstractFunction<T> {
@ -228,7 +240,8 @@ final class TruncDate<T> extends AbstractFunction<T> {
default:
return DSL.field("{trunc}({0}, {1})", getDataType(), date, inline(part.name()));
ctx.visit(F_TRUNC).sql('(').visit(date).sql(", ").visit(inline(keyword)).sql(')');
break;
}
}

View File

@ -37,6 +37,7 @@
*/
package org.jooq.impl;
import static org.jooq.impl.DSL.keyword;
import static org.jooq.impl.DSL.one;
import static org.jooq.impl.DSL.zero;
@ -79,7 +80,7 @@ final class WidthBucket<T extends Number> extends AbstractField<T> {
case POSTGRES:
ctx.visit(DSL.field("{width_bucket}({0}, {1}, {2}, {3})", getType(), field, low, high, buckets));
ctx.visit(keyword("width_bucket")).sql('(').visit(field).sql(", ").visit(low).sql(", ").visit(high).sql(", ").visit(buckets).sql(')');
break;
default: