diff --git a/jOOQ/src/main/java/org/jooq/Clause.java b/jOOQ/src/main/java/org/jooq/Clause.java index 50ff39b3be..a3f63b69b3 100644 --- a/jOOQ/src/main/java/org/jooq/Clause.java +++ b/jOOQ/src/main/java/org/jooq/Clause.java @@ -134,6 +134,7 @@ public enum Clause { TABLE_JOIN_OUTER_FULL, TABLE_JOIN_NATURAL_OUTER_LEFT, TABLE_JOIN_NATURAL_OUTER_RIGHT, + TABLE_JOIN_NATURAL_OUTER_FULL, TABLE_JOIN_CROSS_APPLY, TABLE_JOIN_OUTER_APPLY, TABLE_JOIN_SEMI_LEFT, diff --git a/jOOQ/src/main/java/org/jooq/JoinType.java b/jOOQ/src/main/java/org/jooq/JoinType.java index 922f314440..a5b8f46c08 100644 --- a/jOOQ/src/main/java/org/jooq/JoinType.java +++ b/jOOQ/src/main/java/org/jooq/JoinType.java @@ -38,9 +38,28 @@ package org.jooq; +// ... +// ... +// ... +// ... +import static org.jooq.SQLDialect.CUBRID; +// ... +import static org.jooq.SQLDialect.DERBY; +import static org.jooq.SQLDialect.FIREBIRD; +import static org.jooq.SQLDialect.H2; +// ... +import static org.jooq.SQLDialect.HSQLDB; +// ... +// ... +import static org.jooq.SQLDialect.MARIADB; import static org.jooq.SQLDialect.MYSQL; // ... // ... +import static org.jooq.SQLDialect.POSTGRES; +// ... +// ... +// ... +// ... // ... import org.jooq.impl.DSL; @@ -73,13 +92,13 @@ public enum JoinType { /** * RIGHT OUTER JOIN two tables. */ - @Support + @Support({ CUBRID, DERBY, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) RIGHT_OUTER_JOIN("right outer join", true), /** * FULL OUTER JOIN two tables. */ - @Support + @Support({ FIREBIRD, HSQLDB, POSTGRES }) FULL_OUTER_JOIN("full outer join", true), /** @@ -97,9 +116,15 @@ public enum JoinType { /** * NATURAL RIGHT OUTER JOIN two tables. */ - @Support + @Support({ CUBRID, DERBY, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) NATURAL_RIGHT_OUTER_JOIN("natural right outer join", false), + /** + * NATURAL FULL OUTER JOIN two tables. + */ + @Support({ FIREBIRD, HSQLDB, POSTGRES }) + NATURAL_FULL_OUTER_JOIN("natural full outer join", false), + /** * CROSS APPLY two tables. */ diff --git a/jOOQ/src/main/java/org/jooq/SelectJoinStep.java b/jOOQ/src/main/java/org/jooq/SelectJoinStep.java index bbbd9e3264..180398eff1 100644 --- a/jOOQ/src/main/java/org/jooq/SelectJoinStep.java +++ b/jOOQ/src/main/java/org/jooq/SelectJoinStep.java @@ -1449,6 +1449,119 @@ public interface SelectJoinStep extends SelectWhereStep { @Support({ CUBRID, DERBY, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) SelectJoinStep naturalRightOuterJoin(Name name); + /** + * Convenience method to NATURAL FULL OUTER JOIN a table to + * the last table added to the FROM clause using + * {@link Table#naturalFullOuterJoin(TableLike)} + *

+ * Natural joins are supported by most RDBMS. If they aren't supported, they + * are emulated if jOOQ has enough information. + * + * @see Table#naturalFullOuterJoin(TableLike) + */ + @Support({ FIREBIRD, HSQLDB, POSTGRES }) + SelectJoinStep naturalFullOuterJoin(TableLike table); + + /** + * Convenience method to NATURAL FULL OUTER JOIN a table to + * the last table added to the FROM clause using + * {@link Table#naturalFullOuterJoin(String)} + *

+ * Natural joins are supported by most RDBMS. If they aren't supported, they + * are emulated if jOOQ has enough information. + *

+ * NOTE: When inserting plain SQL into jOOQ objects, you must + * guarantee syntax integrity. You may also create the possibility of + * malicious SQL injection. Be sure to properly use bind variables and/or + * escape literals when concatenated into SQL clauses! + * + * @see DSL#table(SQL) + * @see Table#naturalFullOuterJoin(SQL) + * @see SQL + */ + @Support({ FIREBIRD, HSQLDB, POSTGRES }) + @PlainSQL + SelectJoinStep naturalFullOuterJoin(SQL sql); + + /** + * Convenience method to NATURAL FULL OUTER JOIN a table to + * the last table added to the FROM clause using + * {@link Table#naturalFullOuterJoin(String)} + *

+ * Natural joins are supported by most RDBMS. If they aren't supported, they + * are emulated if jOOQ has enough information. + *

+ * NOTE: When inserting plain SQL into jOOQ objects, you must + * guarantee syntax integrity. You may also create the possibility of + * malicious SQL injection. Be sure to properly use bind variables and/or + * escape literals when concatenated into SQL clauses! + * + * @see DSL#table(String) + * @see Table#naturalFullOuterJoin(String) + * @see SQL + */ + @Support({ FIREBIRD, HSQLDB, POSTGRES }) + @PlainSQL + SelectJoinStep naturalFullOuterJoin(String sql); + + /** + * Convenience method to NATURAL FULL OUTER JOIN a table to + * the last table added to the FROM clause using + * {@link Table#naturalFullOuterJoin(String, Object...)} + *

+ * Natural joins are supported by most RDBMS. If they aren't supported, they + * are emulated if jOOQ has enough information. + *

+ * NOTE: When inserting plain SQL into jOOQ objects, you must + * guarantee syntax integrity. You may also create the possibility of + * malicious SQL injection. Be sure to properly use bind variables and/or + * escape literals when concatenated into SQL clauses! + * + * @see DSL#table(String, Object...) + * @see DSL#sql(String, Object...) + * @see Table#naturalFullOuterJoin(String, Object...) + * @see SQL + */ + @Support({ FIREBIRD, HSQLDB, POSTGRES }) + @PlainSQL + SelectJoinStep naturalFullOuterJoin(String sql, Object... bindings); + + /** + * Convenience method to NATURAL FULL OUTER JOIN a table to + * the last table added to the FROM clause using + * {@link Table#naturalFullOuterJoin(String, QueryPart...)} + *

+ * Natural joins are supported by most RDBMS. If they aren't supported, they + * are emulated if jOOQ has enough information. + *

+ * NOTE: When inserting plain SQL into jOOQ objects, you must + * guarantee syntax integrity. You may also create the possibility of + * malicious SQL injection. Be sure to properly use bind variables and/or + * escape literals when concatenated into SQL clauses! + * + * @see DSL#table(String, QueryPart...) + * @see DSL#sql(String, QueryPart...) + * @see Table#naturalFullOuterJoin(String, QueryPart...) + * @see SQL + */ + @Support({ FIREBIRD, HSQLDB, POSTGRES }) + @PlainSQL + SelectJoinStep naturalFullOuterJoin(String sql, QueryPart... parts); + + /** + * Convenience method to NATURAL FULL OUTER JOIN a table to + * the last table added to the FROM clause using + * {@link Table#naturalFullOuterJoin(Name)} + *

+ * Natural joins are supported by most RDBMS. If they aren't supported, they + * are emulated if jOOQ has enough information. + * + * @see DSL#table(Name) + * @see Table#naturalFullOuterJoin(Name) + */ + @Support({ FIREBIRD, HSQLDB, POSTGRES }) + SelectJoinStep naturalFullOuterJoin(Name name); + // ------------------------------------------------------------------------- // XXX: SEMI and ANTI JOIN // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/Table.java b/jOOQ/src/main/java/org/jooq/Table.java index abea8e118a..651a8c6538 100644 --- a/jOOQ/src/main/java/org/jooq/Table.java +++ b/jOOQ/src/main/java/org/jooq/Table.java @@ -1885,6 +1885,100 @@ public interface Table extends TableLike, Named { @Support({ CUBRID, DERBY, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) Table naturalRightOuterJoin(Name name); + /** + * NATURAL FULL OUTER JOIN a table to this table. + *

+ * If this is not supported by your RDBMS, then jOOQ will try to emulate + * this behaviour using the information provided in this query. + */ + @Support({ FIREBIRD, HSQLDB, POSTGRES }) + Table naturalFullOuterJoin(TableLike table); + + /** + * NATURAL FULL OUTER JOIN a table to this table. + *

+ * If this is not supported by your RDBMS, then jOOQ will try to emulate + * this behaviour using the information provided in this query. + *

+ * NOTE: When inserting plain SQL into jOOQ objects, you must + * guarantee syntax integrity. You may also create the possibility of + * malicious SQL injection. Be sure to properly use bind variables and/or + * escape literals when concatenated into SQL clauses! + * + * @see DSL#table(SQL) + * @see SQL + */ + @Support({ FIREBIRD, HSQLDB, POSTGRES }) + @PlainSQL + Table naturalFullOuterJoin(SQL sql); + + /** + * NATURAL FULL OUTER JOIN a table to this table. + *

+ * If this is not supported by your RDBMS, then jOOQ will try to emulate + * this behaviour using the information provided in this query. + *

+ * NOTE: When inserting plain SQL into jOOQ objects, you must + * guarantee syntax integrity. You may also create the possibility of + * malicious SQL injection. Be sure to properly use bind variables and/or + * escape literals when concatenated into SQL clauses! + * + * @see DSL#table(String) + * @see SQL + */ + @Support({ FIREBIRD, HSQLDB, POSTGRES }) + @PlainSQL + Table naturalFullOuterJoin(String sql); + + /** + * NATURAL FULL OUTER JOIN a table to this table. + *

+ * If this is not supported by your RDBMS, then jOOQ will try to emulate + * this behaviour using the information provided in this query. + *

+ * NOTE: When inserting plain SQL into jOOQ objects, you must + * guarantee syntax integrity. You may also create the possibility of + * malicious SQL injection. Be sure to properly use bind variables and/or + * escape literals when concatenated into SQL clauses! + * + * @see DSL#table(String, Object...) + * @see DSL#sql(String, Object...) + * @see SQL + */ + @Support({ FIREBIRD, HSQLDB, POSTGRES }) + @PlainSQL + Table naturalFullOuterJoin(String sql, Object... bindings); + + /** + * NATURAL FULL OUTER JOIN a table to this table. + *

+ * If this is not supported by your RDBMS, then jOOQ will try to emulate + * this behaviour using the information provided in this query. + *

+ * NOTE: When inserting plain SQL into jOOQ objects, you must + * guarantee syntax integrity. You may also create the possibility of + * malicious SQL injection. Be sure to properly use bind variables and/or + * escape literals when concatenated into SQL clauses! + * + * @see DSL#table(String, QueryPart...) + * @see DSL#sql(String, QueryPart...) + * @see SQL + */ + @Support({ FIREBIRD, HSQLDB, POSTGRES }) + @PlainSQL + Table naturalFullOuterJoin(String sql, QueryPart... parts); + + /** + * NATURAL FULL OUTER JOIN a table to this table. + *

+ * If this is not supported by your RDBMS, then jOOQ will try to emulate + * this behaviour using the information provided in this query. + * + * @see DSL#table(Name) + */ + @Support({ FIREBIRD, HSQLDB, POSTGRES }) + Table naturalFullOuterJoin(Name name); + // ------------------------------------------------------------------------- // XXX: APPLY clauses on tables // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java index 57bf44c450..e4d84c1921 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java @@ -45,6 +45,7 @@ import static org.jooq.JoinType.JOIN; import static org.jooq.JoinType.LEFT_ANTI_JOIN; import static org.jooq.JoinType.LEFT_OUTER_JOIN; import static org.jooq.JoinType.LEFT_SEMI_JOIN; +import static org.jooq.JoinType.NATURAL_FULL_OUTER_JOIN; import static org.jooq.JoinType.NATURAL_JOIN; import static org.jooq.JoinType.NATURAL_LEFT_OUTER_JOIN; import static org.jooq.JoinType.NATURAL_RIGHT_OUTER_JOIN; @@ -1455,6 +1456,36 @@ abstract class AbstractTable extends AbstractNamed implements return naturalRightOuterJoin(table(name)); } + @Override + public final Table naturalFullOuterJoin(TableLike table) { + return join(table, NATURAL_FULL_OUTER_JOIN); + } + + @Override + public final Table naturalFullOuterJoin(SQL sql) { + return naturalFullOuterJoin(table(sql)); + } + + @Override + public final Table naturalFullOuterJoin(String sql) { + return naturalFullOuterJoin(table(sql)); + } + + @Override + public final Table naturalFullOuterJoin(String sql, Object... bindings) { + return naturalFullOuterJoin(table(sql, bindings)); + } + + @Override + public final Table naturalFullOuterJoin(String sql, QueryPart... parts) { + return naturalFullOuterJoin(table(sql, parts)); + } + + @Override + public final Table naturalFullOuterJoin(Name name) { + return naturalFullOuterJoin(table(name)); + } + @Override public final Table crossApply(TableLike table) { return join(table, CROSS_APPLY); diff --git a/jOOQ/src/main/java/org/jooq/impl/JoinTable.java b/jOOQ/src/main/java/org/jooq/impl/JoinTable.java index 499d3749bf..479fe22dce 100755 --- a/jOOQ/src/main/java/org/jooq/impl/JoinTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/JoinTable.java @@ -45,6 +45,7 @@ import static org.jooq.Clause.TABLE_JOIN_CROSS; import static org.jooq.Clause.TABLE_JOIN_CROSS_APPLY; import static org.jooq.Clause.TABLE_JOIN_INNER; import static org.jooq.Clause.TABLE_JOIN_NATURAL; +import static org.jooq.Clause.TABLE_JOIN_NATURAL_OUTER_FULL; import static org.jooq.Clause.TABLE_JOIN_NATURAL_OUTER_LEFT; import static org.jooq.Clause.TABLE_JOIN_NATURAL_OUTER_RIGHT; import static org.jooq.Clause.TABLE_JOIN_ON; @@ -58,10 +59,12 @@ import static org.jooq.Clause.TABLE_JOIN_STRAIGHT; import static org.jooq.Clause.TABLE_JOIN_USING; import static org.jooq.JoinType.CROSS_APPLY; import static org.jooq.JoinType.CROSS_JOIN; +import static org.jooq.JoinType.FULL_OUTER_JOIN; import static org.jooq.JoinType.JOIN; import static org.jooq.JoinType.LEFT_ANTI_JOIN; import static org.jooq.JoinType.LEFT_OUTER_JOIN; import static org.jooq.JoinType.LEFT_SEMI_JOIN; +import static org.jooq.JoinType.NATURAL_FULL_OUTER_JOIN; import static org.jooq.JoinType.NATURAL_JOIN; import static org.jooq.JoinType.NATURAL_LEFT_OUTER_JOIN; import static org.jooq.JoinType.NATURAL_RIGHT_OUTER_JOIN; @@ -280,6 +283,7 @@ implements NATURAL_JOIN, NATURAL_LEFT_OUTER_JOIN, NATURAL_RIGHT_OUTER_JOIN, + NATURAL_FULL_OUTER_JOIN, CROSS_APPLY, OUTER_APPLY).contains(translatedType)) { ctx.formatIndentStart(); @@ -337,6 +341,7 @@ implements case FULL_OUTER_JOIN: return TABLE_JOIN_OUTER_FULL; case NATURAL_LEFT_OUTER_JOIN: return TABLE_JOIN_NATURAL_OUTER_LEFT; case NATURAL_RIGHT_OUTER_JOIN: return TABLE_JOIN_NATURAL_OUTER_RIGHT; + case NATURAL_FULL_OUTER_JOIN: return TABLE_JOIN_NATURAL_OUTER_FULL; case CROSS_APPLY: return TABLE_JOIN_CROSS_APPLY; case OUTER_APPLY: return TABLE_JOIN_OUTER_APPLY; case LEFT_SEMI_JOIN: return TABLE_JOIN_SEMI_LEFT; @@ -358,6 +363,8 @@ implements return LEFT_OUTER_JOIN; else if (emulateNaturalRightOuterJoin(context)) return RIGHT_OUTER_JOIN; + else if (emulateNaturalFullOuterJoin(context)) + return FULL_OUTER_JOIN; else return type; } @@ -382,6 +389,10 @@ implements return type == NATURAL_RIGHT_OUTER_JOIN && EMULATE_NATURAL_OUTER_JOIN.contains(context.family()); } + private final boolean emulateNaturalFullOuterJoin(Context context) { + return type == NATURAL_FULL_OUTER_JOIN && EMULATE_NATURAL_OUTER_JOIN.contains(context.family()); + } + private final void toSQLJoinCondition(Context context) { if (!using.isEmpty()) { @@ -427,7 +438,8 @@ implements // common fields in lhs and rhs of the JOIN clause else if (emulateNaturalJoin(context) || emulateNaturalLeftOuterJoin(context) || - emulateNaturalRightOuterJoin(context)) { + emulateNaturalRightOuterJoin(context) || + emulateNaturalFullOuterJoin(context)) { boolean first = true; for (Field field : lhs.fields()) { diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index f59532987f..d807dd93a6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -8468,57 +8468,38 @@ final class ParserImpl implements Parser { } private static final JoinType parseJoinTypeIf(ParserContext ctx) { - if (parseKeywordIf(ctx, "CROSS JOIN")) - return JoinType.CROSS_JOIN; - else if (parseKeywordIf(ctx, "CROSS APPLY")) - return JoinType.CROSS_APPLY; - else if (parseKeywordIf(ctx, "CROSS JOIN")) - return JoinType.CROSS_JOIN; - else if (parseKeywordIf(ctx, "INNER")) { - parseKeyword(ctx, "JOIN"); - return JoinType.JOIN; + if (parseKeywordIf(ctx, "CROSS")) { + if (parseKeywordIf(ctx, "JOIN")) + return JoinType.CROSS_JOIN; + else if (parseKeywordIf(ctx, "APPLY")) + return JoinType.CROSS_APPLY; } + else if (parseKeywordIf(ctx, "INNER") && parseKeyword(ctx, "JOIN")) + return JoinType.JOIN; else if (parseKeywordIf(ctx, "JOIN")) return JoinType.JOIN; else if (parseKeywordIf(ctx, "LEFT")) { - if (parseKeywordIf(ctx, "SEMI")) { - parseKeyword(ctx, "JOIN"); + if (parseKeywordIf(ctx, "SEMI") && parseKeyword(ctx, "JOIN")) return JoinType.LEFT_SEMI_JOIN; - } - else if (parseKeywordIf(ctx, "ANTI")) { - parseKeyword(ctx, "JOIN"); + else if (parseKeywordIf(ctx, "ANTI") && parseKeyword(ctx, "JOIN")) return JoinType.LEFT_ANTI_JOIN; - } - else { - parseKeywordIf(ctx, "OUTER"); - parseKeyword(ctx, "JOIN"); + else if ((parseKeywordIf(ctx, "OUTER") || true) && parseKeyword(ctx, "JOIN")) return JoinType.LEFT_OUTER_JOIN; - } } - else if (parseKeywordIf(ctx, "RIGHT")) { - parseKeywordIf(ctx, "OUTER"); - parseKeyword(ctx, "JOIN"); + else if (parseKeywordIf(ctx, "RIGHT") && (parseKeywordIf(ctx, "OUTER") || true) && parseKeyword(ctx, "JOIN")) return JoinType.RIGHT_OUTER_JOIN; - } - else if (parseKeywordIf(ctx, "FULL")) { - parseKeywordIf(ctx, "OUTER"); - parseKeyword(ctx, "JOIN"); + else if (parseKeywordIf(ctx, "FULL") && (parseKeywordIf(ctx, "OUTER") || true) && parseKeyword(ctx, "JOIN")) return JoinType.FULL_OUTER_JOIN; - } else if (parseKeywordIf(ctx, "OUTER APPLY")) return JoinType.OUTER_APPLY; else if (parseKeywordIf(ctx, "NATURAL")) { - if (parseKeywordIf(ctx, "LEFT")) { - parseKeywordIf(ctx, "OUTER"); - parseKeyword(ctx, "JOIN"); + if (parseKeywordIf(ctx, "LEFT") && (parseKeywordIf(ctx, "OUTER") || true) && parseKeyword(ctx, "JOIN")) return JoinType.NATURAL_LEFT_OUTER_JOIN; - } - else if (parseKeywordIf(ctx, "RIGHT")) { - parseKeywordIf(ctx, "OUTER"); - parseKeyword(ctx, "JOIN"); + else if (parseKeywordIf(ctx, "RIGHT") && (parseKeywordIf(ctx, "OUTER") || true) && parseKeyword(ctx, "JOIN")) return JoinType.NATURAL_RIGHT_OUTER_JOIN; - } - else if (parseKeywordIf(ctx, "JOIN")) + else if (parseKeywordIf(ctx, "FULL") && (parseKeywordIf(ctx, "OUTER") || true) && parseKeyword(ctx, "JOIN")) + return JoinType.NATURAL_FULL_OUTER_JOIN; + else if ((parseKeywordIf(ctx, "INNER") || true) && parseKeyword(ctx, "JOIN")) return JoinType.NATURAL_JOIN; } else if (parseKeywordIf(ctx, "STRAIGHT_JOIN")) @@ -8740,9 +8721,11 @@ final class ParserImpl implements Parser { return peekKeyword(ctx, string, true, false, true); } - private static final void parseKeyword(ParserContext ctx, String keyword) { + private static final boolean parseKeyword(ParserContext ctx, String keyword) { if (!parseKeywordIf(ctx, keyword)) throw ctx.expected("Keyword '" + keyword + "'"); + + return true; } private static final boolean parseKeywordIf(ParserContext ctx, String keyword) { diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java index 2d24d2ea3f..c1e9c91d79 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectImpl.java @@ -2059,6 +2059,7 @@ final class SelectImpl table) { + return join(table, JoinType.NATURAL_FULL_OUTER_JOIN); + } + @Override public final SelectImpl leftSemiJoin(TableLike table) { return join(table, JoinType.LEFT_SEMI_JOIN); @@ -2426,6 +2432,31 @@ final class SelectImpl extends AbstractResultQuery imp case NATURAL_JOIN: case NATURAL_LEFT_OUTER_JOIN: case NATURAL_RIGHT_OUTER_JOIN: + case NATURAL_FULL_OUTER_JOIN: