From 5076b5eeff26641e1fd7aa3d3460aa897870e0e7 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Thu, 20 Jun 2019 17:03:34 +0200 Subject: [PATCH] [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. --- jOOQ/src/main/java/org/jooq/impl/DSL.java | 6 +- jOOQ/src/main/java/org/jooq/impl/DateAdd.java | 135 ++++++---- .../src/main/java/org/jooq/impl/DateDiff.java | 44 +++- .../main/java/org/jooq/impl/Expression.java | 200 +++++++++------ jOOQ/src/main/java/org/jooq/impl/Extract.java | 242 +++++++++--------- .../java/org/jooq/impl/FieldCondition.java | 26 +- .../src/main/java/org/jooq/impl/Keywords.java | 29 +++ jOOQ/src/main/java/org/jooq/impl/LeadLag.java | 1 - .../src/main/java/org/jooq/impl/NotField.java | 3 +- .../java/org/jooq/impl/RatioToReport.java | 1 + .../main/java/org/jooq/impl/RegexpLike.java | 8 +- jOOQ/src/main/java/org/jooq/impl/Rollup.java | 15 +- .../java/org/jooq/impl/TableComparison.java | 3 +- jOOQ/src/main/java/org/jooq/impl/Term.java | 11 +- .../java/org/jooq/impl/TimestampDiff.java | 48 ++-- .../main/java/org/jooq/impl/TruncDate.java | 33 ++- .../main/java/org/jooq/impl/WidthBucket.java | 3 +- 17 files changed, 484 insertions(+), 324 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/DSL.java b/jOOQ/src/main/java/org/jooq/impl/DSL.java index 351146325a..d76aaa05b8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DSL.java +++ b/jOOQ/src/main/java/org/jooq/impl/DSL.java @@ -15929,7 +15929,7 @@ public class DSL { */ @Support({ H2, HSQLDB, POSTGRES }) public static Field toDate(Field value, Field 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 toTimestamp(Field value, Field 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 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)); } /** diff --git a/jOOQ/src/main/java/org/jooq/impl/DateAdd.java b/jOOQ/src/main/java/org/jooq/impl/DateAdd.java index e664627b29..5de8898ffe 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DateAdd.java +++ b/jOOQ/src/main/java/org/jooq/impl/DateAdd.java @@ -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 extends AbstractFunction { +final class DateAdd extends AbstractField { /** * Generated UID @@ -61,7 +74,7 @@ final class DateAdd extends AbstractFunction { private final DatePart datePart; DateAdd(Field date, Field 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 extends AbstractFunction { } @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 extends AbstractFunction { 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 extends AbstractFunction { 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 extends AbstractFunction { + + + + + + + + + + + @@ -298,8 +329,6 @@ final class DateAdd extends AbstractFunction { } - - return null; } private final void throwUnsupported() { diff --git a/jOOQ/src/main/java/org/jooq/impl/DateDiff.java b/jOOQ/src/main/java/org/jooq/impl/DateDiff.java index e3defc4ec0..1502369575 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DateDiff.java +++ b/jOOQ/src/main/java/org/jooq/impl/DateDiff.java @@ -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 extends AbstractFunction { +final class DateDiff extends AbstractField { /** * Generated UID @@ -57,38 +63,43 @@ final class DateDiff extends AbstractFunction { private final Field date2; DateDiff(Field date1, Field date2) { - super("datediff", SQLDataType.INTEGER, date1, date2); + super(DSL.name("datediff"), SQLDataType.INTEGER); this.date1 = date1; this.date2 = date2; } @Override - final Field 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 extends AbstractFunction { // [#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 extends AbstractFunction { + + + + 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); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Expression.java b/jOOQ/src/main/java/org/jooq/impl/Expression.java index 5f98da42d4..eef1952e52 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Expression.java +++ b/jOOQ/src/main/java/org/jooq/impl/Expression.java @@ -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 extends AbstractFunction { +final class Expression extends AbstractField { /** * Generated UID @@ -106,14 +122,16 @@ final class Expression extends AbstractFunction { private final Field lhs; private final QueryPartList> rhs; + private final Field[] arguments; private final ExpressionOperator operator; Expression(ExpressionOperator operator, Field 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>(rhs); + this.arguments = Tools.combine(lhs, rhs); } @Override @@ -138,8 +156,8 @@ final class Expression extends AbstractFunction { @SuppressWarnings("unchecked") @Override - final Field 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 extends AbstractFunction { // 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 extends AbstractFunction { // ~(a & b) & (a | b) else if (BIT_XOR == operator && EMULATE_BIT_XOR.contains(family)) - return (Field) 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 extends AbstractFunction { // Many dialects don't support shifts. Use multiplication/division instead else if (SHL == operator && EMULATE_SHR_SHL.contains(family)) - return lhs.mul((Field) castIfNeeded(DSL.power(two(), rhsAsNumber()), lhs)); + ctx.visit(lhs.mul((Field) 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) castIfNeeded(DSL.power(two(), rhsAsNumber()), lhs)); + ctx.visit(lhs.div((Field) 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) DSL.bitNot(DSL.bitAnd(lhsAsNumber(), rhsAsNumber())); + ctx.visit(DSL.bitNot(DSL.bitAnd(lhsAsNumber(), rhsAsNumber()))); else if (BIT_NOR == operator) - return (Field) DSL.bitNot(DSL.bitOr(lhsAsNumber(), rhsAsNumber())); + ctx.visit(DSL.bitNot(DSL.bitOr(lhsAsNumber(), rhsAsNumber()))); else if (BIT_XNOR == operator) - return (Field) 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 extends AbstractFunction { lhs.getDataType().isDateTime() && (rhs.get(0).getDataType().isNumeric() || rhs.get(0).getDataType().isInterval())) - return new DateExpression(lhs, operator, rhs.get(0)); + ctx.visit(new DateExpression(lhs, operator, rhs.get(0))); // --------------------------------------------------------------------- // XXX: Other operators @@ -218,7 +236,7 @@ final class Expression extends AbstractFunction { // Use the default operator expression for all other cases else - return new DefaultExpression(lhs, operator, rhs); + ctx.visit(new DefaultExpression(lhs, operator, rhs)); } /** @@ -243,7 +261,7 @@ final class Expression extends AbstractFunction { /** * Return the expression to be rendered when the RHS is an interval type */ - private static class DateExpression extends AbstractFunction { + private static class DateExpression extends AbstractField { /** * Generated UID @@ -255,7 +273,7 @@ final class Expression extends AbstractFunction { private final Field rhs; DateExpression(Field 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 extends AbstractFunction { } @Override - final Field 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 getYTSExpression() { @@ -290,9 +308,8 @@ final class Expression extends AbstractFunction { } @SuppressWarnings({ "unchecked", "rawtypes" }) - private final Field 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 extends AbstractFunction { 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 extends AbstractFunction { 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 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 extends AbstractFunction { 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 extends AbstractFunction { + case POSTGRES: default: - return new DefaultExpression(lhs, operator, new QueryPartList>(Arrays.asList(rhs))); + ctx.visit(new DefaultExpression(lhs, operator, new QueryPartList>(Arrays.asList(rhs)))); + break; } } @@ -554,8 +588,10 @@ final class Expression extends AbstractFunction { * Return the expression to be rendered when the RHS is a number type */ @SuppressWarnings({ "unchecked", "rawtypes" }) - private final Field getNumberExpression(Configuration configuration) { - switch (configuration.family()) { + private final void acceptNumberExpression(Context ctx) { + switch (ctx.family()) { + + @@ -578,9 +614,10 @@ final class Expression extends AbstractFunction { 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 extends AbstractFunction { 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 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 extends AbstractFunction { 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 extends AbstractFunction { + + case POSTGRES: { @@ -644,16 +690,19 @@ final class Expression extends AbstractFunction { // 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 extends AbstractFunction { case H2: default: - return new DefaultExpression(lhs, operator, new QueryPartList>(Arrays.asList(rhs))); + ctx.visit(new DefaultExpression(lhs, operator, new QueryPartList>(Arrays.asList(rhs)))); + break; } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Extract.java b/jOOQ/src/main/java/org/jooq/impl/Extract.java index 3e17d0f0e3..a45c2e509a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Extract.java +++ b/jOOQ/src/main/java/org/jooq/impl/Extract.java @@ -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 { +final class Extract extends AbstractField { private static final long serialVersionUID = 3748640920856031034L; @@ -63,48 +81,67 @@ final class Extract extends AbstractFunction { 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 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 { 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 { 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 dowISOToSun1(Field dow) { @@ -366,31 +368,37 @@ final class Extract extends AbstractFunction { return dow.add(inline(6)).mod(inline(7)).add(one()); } - private final Field 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 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(')'); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldCondition.java b/jOOQ/src/main/java/org/jooq/impl/FieldCondition.java index 91a8591174..b94fe07ea1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldCondition.java @@ -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; + } + } diff --git a/jOOQ/src/main/java/org/jooq/impl/Keywords.java b/jOOQ/src/main/java/org/jooq/impl/Keywords.java index c55b77fa03..9b3883d9e0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Keywords.java +++ b/jOOQ/src/main/java/org/jooq/impl/Keywords.java @@ -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"); diff --git a/jOOQ/src/main/java/org/jooq/impl/LeadLag.java b/jOOQ/src/main/java/org/jooq/impl/LeadLag.java index 68c590bcc8..f9014694b6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/LeadLag.java +++ b/jOOQ/src/main/java/org/jooq/impl/LeadLag.java @@ -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; diff --git a/jOOQ/src/main/java/org/jooq/impl/NotField.java b/jOOQ/src/main/java/org/jooq/impl/NotField.java index 5082ce3783..eda3a14999 100644 --- a/jOOQ/src/main/java/org/jooq/impl/NotField.java +++ b/jOOQ/src/main/java/org/jooq/impl/NotField.java @@ -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 { case POSTGRES: case SQLITE: default: - ctx.visit(DSL.field("{not}({0})", getDataType(), field)); + ctx.visit(K_NOT).sql('(').visit(field).sql(')'); break; } } diff --git a/jOOQ/src/main/java/org/jooq/impl/RatioToReport.java b/jOOQ/src/main/java/org/jooq/impl/RatioToReport.java index fdc6510390..42a4e3e4c1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RatioToReport.java +++ b/jOOQ/src/main/java/org/jooq/impl/RatioToReport.java @@ -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; diff --git a/jOOQ/src/main/java/org/jooq/impl/RegexpLike.java b/jOOQ/src/main/java/org/jooq/impl/RegexpLike.java index b04b90a1fa..53194e7295 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RegexpLike.java +++ b/jOOQ/src/main/java/org/jooq/impl/RegexpLike.java @@ -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 diff --git a/jOOQ/src/main/java/org/jooq/impl/Rollup.java b/jOOQ/src/main/java/org/jooq/impl/Rollup.java index 8871742d45..031420e6a3 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Rollup.java +++ b/jOOQ/src/main/java/org/jooq/impl/Rollup.java @@ -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 { @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; } } diff --git a/jOOQ/src/main/java/org/jooq/impl/TableComparison.java b/jOOQ/src/main/java/org/jooq/impl/TableComparison.java index 07417ced7f..5807fe6197 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableComparison.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableComparison.java @@ -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 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; } diff --git a/jOOQ/src/main/java/org/jooq/impl/Term.java b/jOOQ/src/main/java/org/jooq/impl/Term.java index 748fba6e75..0f21e2c753 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Term.java +++ b/jOOQ/src/main/java/org/jooq/impl/Term.java @@ -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. * diff --git a/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java b/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java index d32729bd3e..5f8cffc38b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java +++ b/jOOQ/src/main/java/org/jooq/impl/TimestampDiff.java @@ -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 { +final class TimestampDiff extends AbstractField { /** * Generated UID @@ -59,16 +64,19 @@ final class TimestampDiff extends AbstractFunction { 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 getFunction0(Configuration configuration) { - switch (configuration.family()) { + public final void accept(Context ctx) { + switch (ctx.family()) { + + + + @@ -106,25 +114,30 @@ final class TimestampDiff extends AbstractFunction { // [#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 { 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); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/TruncDate.java b/jOOQ/src/main/java/org/jooq/impl/TruncDate.java index dc6febc858..e710371ee2 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TruncDate.java +++ b/jOOQ/src/main/java/org/jooq/impl/TruncDate.java @@ -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 extends AbstractFunction { +final class TruncDate extends AbstractField { /** * Generated UID @@ -61,18 +63,18 @@ final class TruncDate extends AbstractFunction { private final DatePart part; TruncDate(Field 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 extends AbstractFunction { 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 extends AbstractFunction { 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 extends AbstractFunction { 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 extends AbstractFunction { + + + + + + @@ -228,7 +240,8 @@ final class TruncDate extends AbstractFunction { 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; } } diff --git a/jOOQ/src/main/java/org/jooq/impl/WidthBucket.java b/jOOQ/src/main/java/org/jooq/impl/WidthBucket.java index 296d14b9b0..ea716346c6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/WidthBucket.java +++ b/jOOQ/src/main/java/org/jooq/impl/WidthBucket.java @@ -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 extends AbstractField { 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: