[jOOQ/jOOQ#14799] Add support for TRY_CAST()
This commit is contained in:
parent
f5ca5ae269
commit
cb9ff43fcd
@ -40,12 +40,13 @@ package org.jooq.impl;
|
||||
// ...
|
||||
import static org.jooq.impl.DSL.inline;
|
||||
import static org.jooq.impl.Keywords.K_AS;
|
||||
import static org.jooq.impl.Keywords.K_CAST;
|
||||
import static org.jooq.impl.Keywords.K_TRIM;
|
||||
import static org.jooq.impl.Keywords.*;
|
||||
import static org.jooq.impl.Names.N_CAST;
|
||||
import static org.jooq.impl.Names.N_SAFE_CAST;
|
||||
import static org.jooq.impl.Names.N_TO_CLOB;
|
||||
import static org.jooq.impl.Names.N_TO_DATE;
|
||||
import static org.jooq.impl.Names.N_TO_TIMESTAMP;
|
||||
import static org.jooq.impl.Names.N_TRY_CAST;
|
||||
import static org.jooq.impl.Names.N_XMLTYPE;
|
||||
import static org.jooq.impl.SQLDataType.BOOLEAN;
|
||||
import static org.jooq.impl.SQLDataType.CHAR;
|
||||
@ -305,25 +306,35 @@ final class Cast<T> extends AbstractField<T> implements QOM.Cast<T> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static class CastNative<T> extends AbstractQueryPart implements UTransient {
|
||||
private final QueryPart expression;
|
||||
private final DataType<T> type;
|
||||
private final Keyword typeAsKeyword;
|
||||
final QueryPart expression;
|
||||
final DataType<T> type;
|
||||
final Keyword typeAsKeyword;
|
||||
final boolean tryCast;
|
||||
|
||||
CastNative(QueryPart expression, DataType<T> type) {
|
||||
this(expression, type, false);
|
||||
}
|
||||
|
||||
CastNative(QueryPart expression, DataType<T> type, boolean tryCast) {
|
||||
this.expression = expression;
|
||||
this.type = type;
|
||||
this.typeAsKeyword = null;
|
||||
this.tryCast = tryCast;
|
||||
}
|
||||
|
||||
CastNative(QueryPart expression, Keyword typeAsKeyword) {
|
||||
this.expression = expression;
|
||||
this.type = null;
|
||||
this.typeAsKeyword = typeAsKeyword;
|
||||
this.tryCast = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -340,7 +351,8 @@ final class Cast<T> extends AbstractField<T> implements QOM.Cast<T> {
|
||||
|
||||
else
|
||||
c.sql(type.getCastTypeName(c.configuration()));
|
||||
}
|
||||
},
|
||||
tryCast
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -350,20 +362,50 @@ final class Cast<T> extends AbstractField<T> implements QOM.Cast<T> {
|
||||
ThrowingConsumer<? super Context<?>, E> expression,
|
||||
ThrowingConsumer<? super Context<?>, E> type
|
||||
) throws E {
|
||||
renderCast(ctx, expression, type, false);
|
||||
}
|
||||
|
||||
static <E extends Throwable> void renderCast(
|
||||
Context<?> ctx,
|
||||
ThrowingConsumer<? super Context<?>, E> expression,
|
||||
ThrowingConsumer<? super Context<?>, E> type,
|
||||
boolean tryCast
|
||||
) throws E {
|
||||
|
||||
// Avoid casting bind values inside an explicit cast...
|
||||
CastMode castMode = ctx.castMode();
|
||||
|
||||
// Default rendering, if no special case has applied yet
|
||||
ctx.visit(K_CAST).sql('(')
|
||||
.castMode(CastMode.NEVER);
|
||||
if (tryCast) {
|
||||
switch (ctx.family()) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
default:
|
||||
ctx.visit(N_TRY_CAST);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
ctx.visit(K_CAST);
|
||||
|
||||
ctx.sql('(').castMode(CastMode.NEVER);
|
||||
expression.accept(ctx);
|
||||
|
||||
ctx.castMode(castMode)
|
||||
.sql(' ').visit(K_AS).sql(' ');
|
||||
ctx.castMode(castMode).sql(' ').visit(K_AS).sql(' ');
|
||||
|
||||
type.accept(ctx);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ctx.sql(')');
|
||||
}
|
||||
|
||||
|
||||
@ -20934,6 +20934,30 @@ public class DSL {
|
||||
return new Nullif<>(value, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>TRY_CAST</code> function.
|
||||
*
|
||||
* @param value The value to be cast to a data type
|
||||
* @param dataType The data type to try to cast the value to
|
||||
*/
|
||||
@NotNull
|
||||
@Support({ DUCKDB, TRINO })
|
||||
public static <T> Field<T> tryCast(Object value, DataType<T> dataType) {
|
||||
return new TryCast<>(Tools.field(value), dataType);
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>TRY_CAST</code> function.
|
||||
*
|
||||
* @param value The value to be cast to a data type
|
||||
* @param dataType The data type to try to cast the value to
|
||||
*/
|
||||
@NotNull
|
||||
@Support({ DUCKDB, TRINO })
|
||||
public static <T> Field<T> tryCast(Field<?> value, DataType<T> dataType) {
|
||||
return new TryCast<>(value, dataType);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// System functions
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -105,6 +105,7 @@ final class Keywords {
|
||||
static final Keyword K_CONTAINS = keyword("contains");
|
||||
static final Keyword K_CONTENT = keyword("content");
|
||||
static final Keyword K_CONTINUE = keyword("continue");
|
||||
static final Keyword K_CONVERSION = keyword("conversion");
|
||||
static final Keyword K_COUNT = keyword("count");
|
||||
static final Keyword K_CREATE = keyword("create");
|
||||
static final Keyword K_CUBE = keyword("cube");
|
||||
|
||||
@ -233,6 +233,7 @@ final class Names {
|
||||
static final Name N_ROWID = systemName("rowid");
|
||||
static final Name N_ROWSFROM = systemName("rowsfrom");
|
||||
static final Name N_ROW_NUMBER = systemName("row_number");
|
||||
static final Name N_SAFE_CAST = systemName("safe_cast");
|
||||
static final Name N_SCHEMA_NAME = systemName("schema_name");
|
||||
static final Name N_SECONDS_BETWEEN = systemName("seconds_between");
|
||||
static final Name N_SEQ4 = systemName("seq4");
|
||||
@ -573,6 +574,7 @@ final class Names {
|
||||
static final Name N_TRANSLATE = systemName("translate");
|
||||
static final Name N_TRIM = systemName("trim");
|
||||
static final Name N_TRUNC = systemName("trunc");
|
||||
static final Name N_TRY_CAST = systemName("try_cast");
|
||||
static final Name N_UCASE = systemName("ucase");
|
||||
static final Name N_UNIQUE = systemName("unique");
|
||||
static final Name N_UPDATING = systemName("updating");
|
||||
|
||||
@ -382,6 +382,7 @@ import static org.jooq.impl.DSL.translate;
|
||||
import static org.jooq.impl.DSL.trim;
|
||||
import static org.jooq.impl.DSL.trueCondition;
|
||||
import static org.jooq.impl.DSL.trunc;
|
||||
import static org.jooq.impl.DSL.tryCast;
|
||||
import static org.jooq.impl.DSL.unique;
|
||||
import static org.jooq.impl.DSL.unnest;
|
||||
import static org.jooq.impl.DSL.user;
|
||||
@ -9086,6 +9087,8 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
|
||||
return parseFunctionArgs2(() -> toField(parseNumericOp()), (f1, f2) -> shr(f1, f2));
|
||||
else if ((field = parseFieldSysConnectByPathIf()) != null)
|
||||
return field;
|
||||
else if ((field = parseFieldCastIf()) != null)
|
||||
return field;
|
||||
else if (!ignoreProEdition() && parseFunctionNameIf("ST_AREA") && requireProEdition()) {
|
||||
|
||||
|
||||
@ -9291,6 +9294,8 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
|
||||
return parseFunctionArgs2((f1, f2) -> DSL.timestampDiff(f1, f2));
|
||||
else if ((field = parseFieldTruncIf()) != null)
|
||||
return field;
|
||||
else if ((field = parseFieldCastIf()) != null)
|
||||
return field;
|
||||
|
||||
break;
|
||||
|
||||
@ -11557,15 +11562,24 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
|
||||
private final Field<?> parseFieldCastIf() {
|
||||
boolean cast = parseFunctionNameIf("CAST");
|
||||
boolean coerce = !cast && parseFunctionNameIf("COERCE");
|
||||
boolean tryCast = !cast && !coerce && parseFunctionNameIf("TRY_CAST", "SAFE_CAST");
|
||||
|
||||
if (cast || coerce) {
|
||||
if (cast || coerce || tryCast) {
|
||||
parse('(');
|
||||
Field<?> field = parseField();
|
||||
parseKeyword("AS");
|
||||
DataType<?> type = parseCastDataType();
|
||||
|
||||
if (!tryCast)
|
||||
tryCast = parseKeywordIf("DEFAULT NULL ON CONVERSION ERROR");
|
||||
|
||||
parse(')');
|
||||
|
||||
return cast ? cast(field, type) : coerce(field, type);
|
||||
return tryCast
|
||||
? tryCast(field, type)
|
||||
: cast
|
||||
? cast(field, type)
|
||||
: coerce(field, type);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@ -6010,6 +6010,40 @@ public final class QOM {
|
||||
@NotNull default Nullif<T> $other(Field<T> newOther) { return $arg2(newOther); }
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>TRY CAST</code> function.
|
||||
*/
|
||||
public /*sealed*/ interface TryCast<T>
|
||||
extends
|
||||
UOperator2<Field<?>, DataType<T>, TryCast<T>>,
|
||||
org.jooq.Field<T>
|
||||
//permits
|
||||
// TryCast
|
||||
{
|
||||
|
||||
/**
|
||||
* The value to be cast to a data type
|
||||
*/
|
||||
@NotNull default Field<?> $value() { return $arg1(); }
|
||||
|
||||
/**
|
||||
* The value to be cast to a data type
|
||||
*/
|
||||
@CheckReturnValue
|
||||
@NotNull default TryCast<T> $value(Field<?> newValue) { return $arg1(newValue); }
|
||||
|
||||
/**
|
||||
* The data type to try to cast the value to
|
||||
*/
|
||||
@NotNull default DataType<T> $dataType() { return $arg2(); }
|
||||
|
||||
/**
|
||||
* The data type to try to cast the value to
|
||||
*/
|
||||
@CheckReturnValue
|
||||
@NotNull default TryCast<T> $dataType(DataType<T> newDataType) { return $arg2(newDataType); }
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>CURRENT CATALOG</code> function.
|
||||
*/
|
||||
|
||||
167
jOOQ/src/main/java/org/jooq/impl/TryCast.java
Normal file
167
jOOQ/src/main/java/org/jooq/impl/TryCast.java
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Other licenses:
|
||||
* -----------------------------------------------------------------------------
|
||||
* Commercial licenses for this work are available. These replace the above
|
||||
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
|
||||
* database integrations.
|
||||
*
|
||||
* For more information, please visit: https://www.jooq.org/legal/licensing
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import static org.jooq.impl.DSL.*;
|
||||
import static org.jooq.impl.Internal.*;
|
||||
import static org.jooq.impl.Keywords.*;
|
||||
import static org.jooq.impl.Names.*;
|
||||
import static org.jooq.impl.SQLDataType.*;
|
||||
import static org.jooq.impl.Tools.*;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.*;
|
||||
import static org.jooq.impl.Tools.ExtendedDataKey.*;
|
||||
import static org.jooq.impl.Tools.SimpleDataKey.*;
|
||||
import static org.jooq.SQLDialect.*;
|
||||
|
||||
import org.jooq.*;
|
||||
import org.jooq.Function1;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.conf.*;
|
||||
import org.jooq.impl.*;
|
||||
import org.jooq.impl.QOM.*;
|
||||
import org.jooq.tools.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.*;
|
||||
|
||||
|
||||
/**
|
||||
* The <code>TRY CAST</code> statement.
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked", "unused" })
|
||||
final class TryCast<T>
|
||||
extends
|
||||
AbstractField<T>
|
||||
implements
|
||||
QOM.TryCast<T>
|
||||
{
|
||||
|
||||
final Field<?> value;
|
||||
final DataType<T> dataType;
|
||||
|
||||
TryCast(
|
||||
Field<?> value,
|
||||
DataType<T> dataType
|
||||
) {
|
||||
super(
|
||||
N_TRY_CAST,
|
||||
dataType
|
||||
);
|
||||
|
||||
this.value = nullSafeNotNull(value, OTHER);
|
||||
this.dataType = dataType;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// XXX: QueryPart API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
switch (ctx.family()) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
default:
|
||||
ctx.visit(new Cast.CastNative<>(value, dataType, true));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// XXX: Query Object Model
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public final Field<?> $arg1() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DataType<T> $arg2() {
|
||||
return dataType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final QOM.TryCast<T> $arg1(Field<?> newValue) {
|
||||
return $constructor().apply(newValue, $arg2());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final QOM.TryCast<T> $arg2(DataType<T> newValue) {
|
||||
return $constructor().apply($arg1(), newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Function2<? super Field<?>, ? super DataType<T>, ? extends QOM.TryCast<T>> $constructor() {
|
||||
return (a1, a2) -> new TryCast<>(a1, a2);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// XXX: The Object API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean equals(Object that) {
|
||||
if (that instanceof QOM.TryCast<?> o) {
|
||||
return
|
||||
StringUtils.equals($value(), o.$value()) &&
|
||||
StringUtils.equals($dataType(), o.$dataType())
|
||||
;
|
||||
}
|
||||
else
|
||||
return super.equals(that);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user