[jOOQ/jOOQ#14988] Emulate LATERAL (TableImpl | JoinTable) where not

supported

This includes:

- [jOOQ/jOOQ#14985] Ensuring path joins work with APPLY
- [jOOQ/jOOQ#14985] Ensuring path joins work with LATERAL

In the latter case, we currently just omit the problem of making path
joins work with LATERAL by avoiding wrapping a path in LATERAL. There
might be a better solution for this in the future.
This commit is contained in:
Lukas Eder 2023-04-28 11:48:41 +02:00
parent 185792157d
commit fa6fb4bb0d
6 changed files with 38 additions and 38 deletions

View File

@ -11810,7 +11810,14 @@ public class DSL {
@NotNull
@Support({ FIREBIRD, MYSQL, POSTGRES, TRINO, YUGABYTEDB })
public static <R extends Record> Table<R> lateral(TableLike<R> table) {
return new Lateral<>(table.asTable());
// [#14988] LATERAL (table reference) isn't supported in any dialect
// and it's also superfluous, so we'll just omit it to make
// sure things like APPLY table reference are emulated correctly
if (table instanceof TableImpl || table instanceof JoinTable)
return (Table<R>) table;
else
return new Lateral<>(table.asTable());
}
/**

View File

@ -226,19 +226,19 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
if (part instanceof TableImpl) {
Table<?> root = (Table<?>) part;
Table<?> child = root;
List<Table<?>> tables = new ArrayList<>();
List<TableImpl<?>> tables = new ArrayList<>();
// [#14985] Traverse paths only when referencing paths (!forceNew),
// not when declaring them in FROM (forceNew)
if (!forceNew) {
while (root instanceof TableImpl && (child = ((TableImpl<?>) root).child) != null) {
while ((child = TableImpl.child(root)) != null) {
// [#14985] If a subpath has been declared in FROM, join
// to that, instead of the root.
if (scopeStack.get(root) != null)
break;
tables.add(root);
tables.add((TableImpl<?>) root);
root = child;
}
}
@ -252,8 +252,8 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
JoinNode childNode = e.joinNode;
for (int i = tables.size() - 1; i >= 0; i--) {
Table<?> t = tables.get(i);
ForeignKey<?, ?> k = ((TableImpl<?>) t).childPath;
TableImpl<?> t = tables.get(i);
ForeignKey<?, ?> k = t.childPath;
JoinNode next = childNode.children.get(k);
if (next == null) {

View File

@ -59,7 +59,6 @@ import static org.jooq.Clause.TABLE_JOIN_PARTITION_BY;
import static org.jooq.Clause.TABLE_JOIN_SEMI_LEFT;
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;
@ -70,7 +69,6 @@ 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;
import static org.jooq.JoinType.OUTER_APPLY;
import static org.jooq.JoinType.RIGHT_OUTER_JOIN;
// ...
// ...
@ -98,13 +96,11 @@ import static org.jooq.SQLDialect.YUGABYTEDB;
import static org.jooq.impl.ConditionProviderImpl.extractCondition;
import static org.jooq.impl.DSL.condition;
import static org.jooq.impl.DSL.exists;
import static org.jooq.impl.DSL.lateral;
import static org.jooq.impl.DSL.noCondition;
import static org.jooq.impl.DSL.notExists;
import static org.jooq.impl.DSL.selectFrom;
import static org.jooq.impl.DSL.selectOne;
import static org.jooq.impl.DSL.trueCondition;
import static org.jooq.impl.Keywords.K_CROSS_JOIN_LATERAL;
import static org.jooq.impl.Keywords.K_LEFT_JOIN_LATERAL;
import static org.jooq.impl.Keywords.K_LEFT_OUTER_JOIN_LATERAL;
import static org.jooq.impl.Keywords.K_ON;
import static org.jooq.impl.Keywords.K_PARTITION_BY;
import static org.jooq.impl.Keywords.K_USING;
@ -238,11 +234,23 @@ implements
// [#14985] APPLY or LATERAL with path joins
if ((this instanceof CrossApply || this instanceof OuterApply) && TableImpl.child(rhs) != null)
ctx.visit($table2(selectFrom(rhs).asTable(rhs)));
else if (rhs instanceof Lateral && TableImpl.child(((Lateral<?>) rhs).$arg1()) != null)
ctx.visit($table2(lateral(selectFrom(((Lateral<?>) rhs).$arg1()).asTable(((Lateral<?>) rhs).$arg1()))));
// [#14988] Make sure APPLY table reference continues working by wrapping lateral(rhs)
else if (this instanceof CrossApply && EMULATE_APPLY.contains(ctx.dialect()))
ctx.visit(lhs.crossJoin(lateral(rhs)));
else if (this instanceof OuterApply && EMULATE_APPLY.contains(ctx.dialect()))
ctx.visit(lhs.leftJoin(lateral(rhs)).on(noCondition()));
accept0(ctx);
else
accept0(ctx);
}
private final void accept0(Context<?> ctx) {
@ -319,17 +327,6 @@ implements
toSQLJoinCondition(ctx);
ctx.formatIndentEnd();
}
else if (OUTER_APPLY == translatedType && EMULATE_APPLY.contains(ctx.dialect())) {
ctx.formatIndentStart()
.formatSeparator()
.start(TABLE_JOIN_ON)
.visit(K_ON)
.sql(' ')
.visit(trueCondition())
.end(TABLE_JOIN_ON)
.formatIndentEnd();
}
ctx.end(translatedClause)
.formatIndentEnd();
}
@ -448,15 +445,6 @@ implements
break;
}
if (translatedType == CROSS_APPLY && EMULATE_APPLY.contains(ctx.dialect()))
keyword = K_CROSS_JOIN_LATERAL;
else if (translatedType == OUTER_APPLY && EMULATE_APPLY.contains(ctx.dialect()))
if (ctx.settings().getRenderOptionalOuterKeyword() == RenderOptionalKeyword.OFF)
keyword = K_LEFT_JOIN_LATERAL;
else
keyword = K_LEFT_OUTER_JOIN_LATERAL;
return keyword;
}
@ -572,8 +560,7 @@ implements
}
// [#14985] Path joins additional conditions
else if (rhs instanceof TableImpl
&& ((TableImpl<?>) rhs).child != null
else if (TableImpl.child(rhs) != null
// Do this only if we're *not* rendering implicit joins, in case of which join paths
// are expected, and their predicates are already present.

View File

@ -106,7 +106,6 @@ final class Keywords {
static final Keyword K_CONTINUE = keyword("continue");
static final Keyword K_COUNT = keyword("count");
static final Keyword K_CREATE = keyword("create");
static final Keyword K_CROSS_JOIN_LATERAL = keyword("cross join lateral");
static final Keyword K_CUBE = keyword("cube");
static final Keyword K_CURRENT = keyword("current");
static final Keyword K_CURRENT_ROW = keyword("current row");
@ -247,8 +246,6 @@ final class Keywords {
static final Keyword K_LATERAL = keyword("lateral");
static final Keyword K_LEADING = keyword("leading");
static final Keyword K_LEAVE = keyword("leave");
static final Keyword K_LEFT_JOIN_LATERAL = keyword("left join lateral");
static final Keyword K_LEFT_OUTER_JOIN_LATERAL = keyword("left outer join lateral");
static final Keyword K_LET = keyword("let");
static final Keyword K_LIKE = keyword("like");
static final Keyword K_LIKE_REGEX = keyword("like_regex");

View File

@ -38,6 +38,7 @@
package org.jooq.impl;
import static org.jooq.impl.DSL.lateral;
import static org.jooq.impl.DSL.selectFrom;
import static org.jooq.impl.Keywords.K_LATERAL;
import org.jooq.Context;

View File

@ -288,6 +288,14 @@ implements
this.parameters = parameters;
}
static final Table<?> child(Table<?> t) {
if (t instanceof TableImpl<?> ti)
if (ti.child != null)
return ti.child;
return null;
}
final Condition childPathCondition() {
if (child == null)
return noCondition();