[jOOQ/jOOQ#15755] Add Settings.renderImplicitJoinToManyType: RenderImplicitJoinType to govern the implicit to-many join style

This commit is contained in:
Lukas Eder 2023-10-25 15:55:15 +02:00
parent b96378c5ac
commit 49fa0bd34c
4 changed files with 82 additions and 2 deletions

View File

@ -93,6 +93,9 @@ public class Settings
@XmlElement(defaultValue = "DEFAULT")
@XmlSchemaType(name = "string")
protected RenderImplicitJoinType renderImplicitJoinType = RenderImplicitJoinType.DEFAULT;
@XmlElement(defaultValue = "DEFAULT")
@XmlSchemaType(name = "string")
protected RenderImplicitJoinType renderImplicitJoinToManyType = RenderImplicitJoinType.DEFAULT;
@XmlElement(defaultValue = "IMPLICIT_NULL")
@XmlSchemaType(name = "string")
protected RenderDefaultNullability renderDefaultNullability = RenderDefaultNullability.IMPLICIT_NULL;
@ -1027,6 +1030,32 @@ public class Settings
this.renderImplicitJoinType = value;
}
/**
* The join type to be generated by implicit joins for to-many paths in {@link org.jooq.Select} queries.
* <p>
* The <code>DEFAULT</code> is <code>SCALAR_SUBQUERY</code> if the join path is implicit only, i.e. absent from
* the <code>FROM</code> clause, to prevent accidental cartesian products, or <code>LEFT_JOIN</code> if declared
* explicitly in the <code>FROM</code> clause. In DML statements, it is always <code>SCALAR_SUBQUERY</code>,
* unless DML joins are supported.
*
*/
public RenderImplicitJoinType getRenderImplicitJoinToManyType() {
return renderImplicitJoinToManyType;
}
/**
* The join type to be generated by implicit joins for to-many paths in {@link org.jooq.Select} queries.
* <p>
* The <code>DEFAULT</code> is <code>SCALAR_SUBQUERY</code> if the join path is implicit only, i.e. absent from
* the <code>FROM</code> clause, to prevent accidental cartesian products, or <code>LEFT_JOIN</code> if declared
* explicitly in the <code>FROM</code> clause. In DML statements, it is always <code>SCALAR_SUBQUERY</code>,
* unless DML joins are supported.
*
*/
public void setRenderImplicitJoinToManyType(RenderImplicitJoinType value) {
this.renderImplicitJoinToManyType = value;
}
/**
* Whether the {@link org.jooq.Nullability#DEFAULT} nullablity should be rendered in generated DDL, and how it should be rendered.
*
@ -6232,6 +6261,20 @@ public class Settings
return this;
}
/**
* The join type to be generated by implicit joins for to-many paths in {@link org.jooq.Select} queries.
* <p>
* The <code>DEFAULT</code> is <code>SCALAR_SUBQUERY</code> if the join path is implicit only, i.e. absent from
* the <code>FROM</code> clause, to prevent accidental cartesian products, or <code>LEFT_JOIN</code> if declared
* explicitly in the <code>FROM</code> clause. In DML statements, it is always <code>SCALAR_SUBQUERY</code>,
* unless DML joins are supported.
*
*/
public Settings withRenderImplicitJoinToManyType(RenderImplicitJoinType value) {
setRenderImplicitJoinToManyType(value);
return this;
}
/**
* Whether the {@link org.jooq.Nullability#DEFAULT} nullablity should be rendered in generated DDL, and how it should be rendered.
*
@ -7646,6 +7689,7 @@ public class Settings
builder.append("renderImplicitWindowRange", renderImplicitWindowRange);
builder.append("renderScalarSubqueriesForStoredFunctions", renderScalarSubqueriesForStoredFunctions);
builder.append("renderImplicitJoinType", renderImplicitJoinType);
builder.append("renderImplicitJoinToManyType", renderImplicitJoinToManyType);
builder.append("renderDefaultNullability", renderDefaultNullability);
builder.append("renderCoalesceToEmptyStringInConcat", renderCoalesceToEmptyStringInConcat);
builder.append("renderOrderByRownumberForEmulatedPagination", renderOrderByRownumberForEmulatedPagination);
@ -8065,6 +8109,15 @@ public class Settings
return false;
}
}
if (renderImplicitJoinToManyType == null) {
if (other.renderImplicitJoinToManyType!= null) {
return false;
}
} else {
if (!renderImplicitJoinToManyType.equals(other.renderImplicitJoinToManyType)) {
return false;
}
}
if (renderDefaultNullability == null) {
if (other.renderDefaultNullability!= null) {
return false;
@ -9894,6 +9947,7 @@ public class Settings
result = ((prime*result)+((renderImplicitWindowRange == null)? 0 :renderImplicitWindowRange.hashCode()));
result = ((prime*result)+((renderScalarSubqueriesForStoredFunctions == null)? 0 :renderScalarSubqueriesForStoredFunctions.hashCode()));
result = ((prime*result)+((renderImplicitJoinType == null)? 0 :renderImplicitJoinType.hashCode()));
result = ((prime*result)+((renderImplicitJoinToManyType == null)? 0 :renderImplicitJoinToManyType.hashCode()));
result = ((prime*result)+((renderDefaultNullability == null)? 0 :renderDefaultNullability.hashCode()));
result = ((prime*result)+((renderCoalesceToEmptyStringInConcat == null)? 0 :renderCoalesceToEmptyStringInConcat.hashCode()));
result = ((prime*result)+((renderOrderByRownumberForEmulatedPagination == null)? 0 :renderOrderByRownumberForEmulatedPagination.hashCode()));

View File

@ -1261,7 +1261,7 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
Table<?> t = e.getValue().joinTree();
result = result
.join(t, node.joinType(LEFT_OUTER_JOIN))
.join(t, node.joinToManyType())
.on(onKey0(e.getKey().getForeignKey(), t, result));
}
@ -1298,6 +1298,15 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
}
}
private final JoinType joinToManyType() {
switch (defaultIfNull(Tools.settings(ctx.configuration()).getRenderImplicitJoinToManyType(), RenderImplicitJoinType.DEFAULT)) {
case INNER_JOIN:
return JOIN;
default:
return LEFT_OUTER_JOIN;
}
}
final boolean hasJoinPaths() {
return !pathsToOne.isEmpty() || !pathsToMany.isEmpty();
}

View File

@ -43,6 +43,7 @@ import static java.util.stream.Collectors.joining;
import static org.jooq.Clause.FIELD;
import static org.jooq.Clause.FIELD_REFERENCE;
// ...
import static org.jooq.conf.RenderImplicitJoinType.DEFAULT;
import static org.jooq.conf.RenderImplicitJoinType.SCALAR_SUBQUERY;
import static org.jooq.impl.DSL.select;
import static org.jooq.impl.DefaultMetaProvider.meta;
@ -69,6 +70,7 @@ import org.jooq.RowId;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.Update;
import org.jooq.conf.RenderImplicitJoinType;
import org.jooq.impl.QOM.UEmpty;
import org.jooq.impl.Tools.SimpleDataKey;
import org.jooq.tools.StringUtils;
@ -202,7 +204,13 @@ implements
}
private final boolean implicitJoinAsScalarSubquery(Context<?> ctx, TableImpl<?> t, Table<?> root) {
return (t.childPath != null && ctx.settings().getRenderImplicitJoinType() == SCALAR_SUBQUERY)
return
// [#15754] Explicit scalar subqueries can be configured for implicit to-one paths
(t.childPath != null && ctx.settings().getRenderImplicitJoinType() == SCALAR_SUBQUERY && !ctx.inScope(t))
// [#15755] The default is to render scalar subqueries for implicit to-many paths
|| (t.parentPath != null && (ctx.settings().getRenderImplicitJoinToManyType() == DEFAULT || ctx.settings().getRenderImplicitJoinToManyType() == SCALAR_SUBQUERY) && !ctx.inScope(t))
// [#7508] Implicit join path references inside of DML queries have to
// be emulated differently

View File

@ -168,6 +168,15 @@ The <code>DEFAULT</code> is dependent on the nullability of the foreign key (<co
for nullable foreign keys and <code>INNER_JOIN</code> for non-nullable foreign keys). In DML statements,
it is always <code>SCALAR_SUBQUERY</code>, unless DML joins are supported.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="renderImplicitJoinToManyType" type="jooq-runtime:RenderImplicitJoinType" minOccurs="0" maxOccurs="1" default="DEFAULT">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The join type to be generated by implicit joins for to-many paths in {@link org.jooq.Select} queries.
<p>
The <code>DEFAULT</code> is <code>SCALAR_SUBQUERY</code> if the join path is implicit only, i.e. absent from
the <code>FROM</code> clause, to prevent accidental cartesian products, or <code>LEFT_JOIN</code> if declared
explicitly in the <code>FROM</code> clause. In DML statements, it is always <code>SCALAR_SUBQUERY</code>,
unless DML joins are supported.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="renderDefaultNullability" type="jooq-runtime:RenderDefaultNullability" minOccurs="0" maxOccurs="1" default="IMPLICIT_NULL">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether the {@link org.jooq.Nullability#DEFAULT} nullablity should be rendered in generated DDL, and how it should be rendered.]]></jxb:javadoc></jxb:property></appinfo></annotation>