[jOOQ/jOOQ#14166] Render correct SQL when CASE have empty contents

This commit is contained in:
Lukas Eder 2022-11-04 12:02:46 +01:00
parent 97c8e00d66
commit 607bde4fa3
8 changed files with 175 additions and 33 deletions

View File

@ -187,6 +187,8 @@ public class Settings
@XmlElement(defaultValue = "true")
protected Boolean transformPatternsCaseUnreachableClauses = true;
@XmlElement(defaultValue = "true")
protected Boolean transformPatternsCaseDistinctToDecode = true;
@XmlElement(defaultValue = "true")
protected Boolean transformPatternsCaseMergeWhenWhen = true;
@XmlElement(defaultValue = "true")
protected Boolean transformPatternsCaseMergeWhenElse = true;
@ -2316,6 +2318,41 @@ public class Settings
this.transformPatternsCaseUnreachableClauses = value;
}
/**
* Transform <code>CASE WHEN a IS NOT DISTINCT FROM b </code> to an equivalent <code>DECODE</code> function.
* <p>
* When all <code>WHEN</code> clauses of a <code>CASE</code> expression use the <code>DISTINCT</code> predicate, then the
* <code>CASE</code> expression can be transformed into a <code>DECODE</code> function call:
* <ul>
* <li><code>CASE WHEN a IS NOT DISTINCT FROM b THEN 1 END</code> is equivalent to <code>DECODE(a, b, 1)</code></li>
* <li><code>CASE WHEN a IS NOT DISTINCT FROM b THEN 1 ELSE 2 END</code> is equivalent to <code>DECODE(a, b, 1, 2)</code></li>
* <li><code>CASE WHEN a IS NOT DISTINCT FROM b THEN 1 WHEN a IS NOT DISTINCT FROM c THEN 2 END</code> is equivalent to <code>DECODE(a, b, 1, c, 2)</code></li>
* <li><code>CASE WHEN a IS NOT DISTINCT FROM b THEN 1 WHEN a IS NOT DISTINCT FROM c THEN 2 ELSE 3 END</code> is equivalent to <code>DECODE(a, b, 1, c, 2, 3)</code></li>
* </ul>
* <p>
* This feature is available in the commercial distribution only.
*
* @return
* possible object is
* {@link Boolean }
*
*/
public Boolean isTransformPatternsCaseDistinctToDecode() {
return transformPatternsCaseDistinctToDecode;
}
/**
* Sets the value of the transformPatternsCaseDistinctToDecode property.
*
* @param value
* allowed object is
* {@link Boolean }
*
*/
public void setTransformPatternsCaseDistinctToDecode(Boolean value) {
this.transformPatternsCaseDistinctToDecode = value;
}
/**
* Transform <code>CASE WHEN a THEN x WHEN b THEN x END</code> to <code>CASE WHEN a OR b THEN x END</code>.
* <p>
@ -5541,6 +5578,11 @@ public class Settings
return this;
}
public Settings withTransformPatternsCaseDistinctToDecode(Boolean value) {
setTransformPatternsCaseDistinctToDecode(value);
return this;
}
public Settings withTransformPatternsCaseMergeWhenWhen(Boolean value) {
setTransformPatternsCaseMergeWhenWhen(value);
return this;
@ -6555,6 +6597,7 @@ public class Settings
builder.append("transformPatternsCaseElseNull", transformPatternsCaseElseNull);
builder.append("transformPatternsCaseElseCase", transformPatternsCaseElseCase);
builder.append("transformPatternsCaseUnreachableClauses", transformPatternsCaseUnreachableClauses);
builder.append("transformPatternsCaseDistinctToDecode", transformPatternsCaseDistinctToDecode);
builder.append("transformPatternsCaseMergeWhenWhen", transformPatternsCaseMergeWhenWhen);
builder.append("transformPatternsCaseMergeWhenElse", transformPatternsCaseMergeWhenElse);
builder.append("transformPatternsCaseToCaseAbbreviation", transformPatternsCaseToCaseAbbreviation);
@ -7310,6 +7353,15 @@ public class Settings
return false;
}
}
if (transformPatternsCaseDistinctToDecode == null) {
if (other.transformPatternsCaseDistinctToDecode!= null) {
return false;
}
} else {
if (!transformPatternsCaseDistinctToDecode.equals(other.transformPatternsCaseDistinctToDecode)) {
return false;
}
}
if (transformPatternsCaseMergeWhenWhen == null) {
if (other.transformPatternsCaseMergeWhenWhen!= null) {
return false;
@ -8483,6 +8535,7 @@ public class Settings
result = ((prime*result)+((transformPatternsCaseElseNull == null)? 0 :transformPatternsCaseElseNull.hashCode()));
result = ((prime*result)+((transformPatternsCaseElseCase == null)? 0 :transformPatternsCaseElseCase.hashCode()));
result = ((prime*result)+((transformPatternsCaseUnreachableClauses == null)? 0 :transformPatternsCaseUnreachableClauses.hashCode()));
result = ((prime*result)+((transformPatternsCaseDistinctToDecode == null)? 0 :transformPatternsCaseDistinctToDecode.hashCode()));
result = ((prime*result)+((transformPatternsCaseMergeWhenWhen == null)? 0 :transformPatternsCaseMergeWhenWhen.hashCode()));
result = ((prime*result)+((transformPatternsCaseMergeWhenElse == null)? 0 :transformPatternsCaseMergeWhenElse.hashCode()));
result = ((prime*result)+((transformPatternsCaseToCaseAbbreviation == null)? 0 :transformPatternsCaseToCaseAbbreviation.hashCode()));

View File

@ -62,7 +62,7 @@ final class CaseImpl implements Case, UTransient {
@Override
public final <V> CaseValueStep<V> value(Field<V> value) {
return new CaseValueStepImpl<>(value);
return new CaseSimpleValueStep<>(value);
}
@Override
@ -72,7 +72,7 @@ final class CaseImpl implements Case, UTransient {
@Override
public final <T> CaseConditionStep<T> when(Condition condition, Field<T> result) {
return new CaseConditionStepImpl<>(condition, result);
return new CaseSearched<>(condition, result);
}
@Override

View File

@ -39,6 +39,7 @@ package org.jooq.impl;
import static java.lang.Boolean.TRUE;
// ...
import static org.jooq.impl.DSL.NULL;
import static org.jooq.impl.DSL.one;
import static org.jooq.impl.DSL.select;
import static org.jooq.impl.Keywords.K_CASE;
@ -64,14 +65,13 @@ import org.jooq.Function2;
import org.jooq.Record1;
// ...
import org.jooq.Select;
import org.jooq.impl.QOM.CaseSearched;
import org.jooq.impl.QOM.Tuple2;
import org.jooq.impl.QOM.UnmodifiableList;
/**
* @author Lukas Eder
*/
final class CaseConditionStepImpl<T>
final class CaseSearched<T>
extends
AbstractField<T>
implements
@ -80,15 +80,15 @@ implements
{
private final List<Tuple2<Condition, Field<T>>> when;
private Field<T> else_;
private Field<T> else_;
CaseConditionStepImpl(DataType<T> type) {
CaseSearched(DataType<T> type) {
super(NQ_CASE, type);
this.when = new QueryPartList<>();
}
CaseConditionStepImpl(Condition condition, Field<T> result) {
CaseSearched(Condition condition, Field<T> result) {
this(result.getDataType());
when(condition, result);
@ -163,7 +163,14 @@ implements
@Override
public final void accept(Context<?> ctx) {
switch (ctx.family()) {
if (when.isEmpty()) {
if (else_ != null)
ctx.visit(else_);
else
ctx.visit(NULL(getDataType()));
}
else {
switch (ctx.family()) {
@ -174,9 +181,10 @@ implements
default:
acceptNative(ctx);
break;
default:
acceptNative(ctx);
break;
}
}
}
@ -265,7 +273,7 @@ implements
@Override
public final Function2<? super UnmodifiableList<? extends Tuple2<Condition, Field<T>>>, ? super Field<T>, ? extends CaseSearched<T>> $constructor() {
return (w, e) -> {
CaseConditionStepImpl<T> r = new CaseConditionStepImpl<>(getDataType());
CaseSearched<T> r = new CaseSearched<>(getDataType());
w.forEach(t -> r.when(t.$1(), t.$2()));
r.else_(e);
return r;

View File

@ -38,6 +38,7 @@
package org.jooq.impl;
import static java.lang.Boolean.TRUE;
import static org.jooq.impl.DSL.NULL;
import static org.jooq.impl.Keywords.K_CASE;
import static org.jooq.impl.Keywords.K_ELSE;
import static org.jooq.impl.Keywords.K_END;
@ -60,14 +61,13 @@ import org.jooq.DataType;
import org.jooq.Field;
import org.jooq.Function3;
// ...
import org.jooq.impl.QOM.CaseSimple;
import org.jooq.impl.QOM.Tuple2;
import org.jooq.impl.QOM.UnmodifiableList;
/**
* @author Lukas Eder
*/
final class CaseWhenStepImpl<V, T>
final class CaseSimple<V, T>
extends
AbstractField<T>
implements
@ -75,23 +75,23 @@ implements
QOM.CaseSimple<V, T>
{
private final Field<V> value;
private final Field<V> value;
private final List<Tuple2<Field<V>, Field<T>>> when;
private Field<T> else_;
private Field<T> else_;
CaseWhenStepImpl(Field<V> value, Field<V> compareValue, Field<T> result) {
CaseSimple(Field<V> value, Field<V> compareValue, Field<T> result) {
this(value, result.getDataType());
when(compareValue, result);
}
CaseWhenStepImpl(Field<V> value, Map<? extends Field<V>, ? extends Field<T>> map) {
CaseSimple(Field<V> value, Map<? extends Field<V>, ? extends Field<T>> map) {
this(value, dataType(map));
mapFields(map);
}
CaseWhenStepImpl(Field<V> value, DataType<T> type) {
CaseSimple(Field<V> value, DataType<T> type) {
super(NQ_CASE, type);
this.value = value;
@ -168,7 +168,14 @@ implements
@Override
public final void accept(Context<?> ctx) {
switch (ctx.family()) {
if (when.isEmpty()) {
if (else_ != null)
ctx.visit(else_);
else
ctx.visit(NULL(getDataType()));
}
else {
switch (ctx.family()) {
@ -179,14 +186,15 @@ implements
// The DERBY dialect doesn't support the simple CASE clause
case DERBY:
acceptSearched(ctx);
break;
// The DERBY dialect doesn't support the simple CASE clause
case DERBY:
acceptSearched(ctx);
break;
default:
acceptNative(ctx);
break;
default:
acceptNative(ctx);
break;
}
}
}
@ -284,7 +292,7 @@ implements
@Override
public final Function3<? super Field<V>, ? super UnmodifiableList<? extends Tuple2<Field<V>, Field<T>>>, ? super Field<T>, ? extends CaseSimple<V, T>> $constructor() {
return (v, w, e) -> {
CaseWhenStepImpl<V, T> r = new CaseWhenStepImpl<>(v, getDataType());
CaseSimple<V, T> r = new CaseSimple<>(v, getDataType());
w.forEach(t -> r.when(t.$1(), t.$2()));
r.else_(e);
return r;

View File

@ -52,11 +52,11 @@ import org.jooq.impl.QOM.UTransient;
*
* @author Lukas Eder
*/
final class CaseValueStepImpl<V> implements CaseValueStep<V>, UTransient {
final class CaseSimpleValueStep<V> implements CaseValueStep<V>, UTransient {
private final Field<V> value;
CaseValueStepImpl(Field<V> value) {
CaseSimpleValueStep(Field<V> value) {
this.value = value;
}
@ -82,7 +82,7 @@ final class CaseValueStepImpl<V> implements CaseValueStep<V>, UTransient {
@Override
public final <T> CaseWhenStep<V, T> when(Field<V> compareValue, Field<T> result) {
return new CaseWhenStepImpl<>(value, compareValue, result);
return new CaseSimple<>(value, compareValue, result);
}
@Override
@ -99,6 +99,6 @@ final class CaseValueStepImpl<V> implements CaseValueStep<V>, UTransient {
@Override
public final <T> CaseWhenStep<V, T> mapFields(Map<? extends Field<V>, ? extends Field<T>> fields) {
return new CaseWhenStepImpl<>(value, fields);
return new CaseSimple<>(value, fields);
}
}

View File

@ -1629,7 +1629,7 @@ implements
for (Entry<FieldOrRow, FieldOrRowOrSelect> e : m.updateMap.entrySet()) {
FieldOrRowOrSelect exp = update.updateMap.get(e.getKey());
if (exp instanceof CaseConditionStepImpl c)
if (exp instanceof CaseSearched c)
c.when(negate.and(condition), e.getValue());
// [#10523] [#13325] TODO: ClassCastException when we're using Row here, once supported

View File

@ -2117,6 +2117,64 @@ package org.jooq.impl;

View File

@ -615,6 +615,21 @@ Case clauses can be proven to be unreachable, and thus removed:
This feature is available in the commercial distribution only.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="transformPatternsCaseDistinctToDecode" type="boolean" minOccurs="0" maxOccurs="1" default="true">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Transform <code>CASE WHEN a IS NOT DISTINCT FROM b ..</code> to an equivalent <code>DECODE</code> function.
<p>
When all <code>WHEN</code> clauses of a <code>CASE</code> expression use the <code>DISTINCT</code> predicate, then the
<code>CASE</code> expression can be transformed into a <code>DECODE</code> function call:
<ul>
<li><code>CASE WHEN a IS NOT DISTINCT FROM b THEN 1 END</code> is equivalent to <code>DECODE(a, b, 1)</code></li>
<li><code>CASE WHEN a IS NOT DISTINCT FROM b THEN 1 ELSE 2 END</code> is equivalent to <code>DECODE(a, b, 1, 2)</code></li>
<li><code>CASE WHEN a IS NOT DISTINCT FROM b THEN 1 WHEN a IS NOT DISTINCT FROM c THEN 2 END</code> is equivalent to <code>DECODE(a, b, 1, c, 2)</code></li>
<li><code>CASE WHEN a IS NOT DISTINCT FROM b THEN 1 WHEN a IS NOT DISTINCT FROM c THEN 2 ELSE 3 END</code> is equivalent to <code>DECODE(a, b, 1, c, 2, 3)</code></li>
</ul>
<p>
This feature is available in the commercial distribution only.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="transformPatternsCaseMergeWhenWhen" type="boolean" minOccurs="0" maxOccurs="1" default="true">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Transform <code>CASE WHEN a THEN x WHEN b THEN x END</code> to <code>CASE WHEN a OR b THEN x END</code>.
<p>