[jOOQ/jOOQ#10880] Add DECFLOAT support
This includes: - Added org.jooq.Decfloat draft implementation - Added DefaultBinding - Added SQLDataType and dialect specific ones - Added binding test - org.jooq.Decfloat implements org.jooq.Data - Normalisation of Decfloat for equals(), hashCode(), toString() - [jOOQ/jOOQ#17060] Add a org.jooq.Data interface, which all the data() bearing types can implement
This commit is contained in:
parent
b60097822c
commit
aa3fbda55d
79
jOOQ/src/main/java/org/jooq/Data.java
Normal file
79
jOOQ/src/main/java/org/jooq/Data.java
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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
|
||||
* Apache-2.0 license and offer limited warranties, support, maintenance, and
|
||||
* commercial database integrations.
|
||||
*
|
||||
* For more information, please visit: https://www.jooq.org/legal/licensing
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.SQLXML;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A type holding {@link #data()}, which is a {@link String} based
|
||||
* representation of a SQL data type with no reasonable representation in the
|
||||
* JDK or in JDBC.
|
||||
* <p>
|
||||
* JDBC maps most types to JDK types, but some vendor specific types lack an
|
||||
* equivalent in the JDK, or the relevant type is unsatisfactory (such as
|
||||
* {@link SQLXML}, which is a resource). To work around such limitations, jOOQ
|
||||
* establishes the set of {@link Data} types, with these properties:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>They contain a {@link NotNull} representation of their {@link #data()}.
|
||||
* In other words, a <code>CAST(NULL AS …)</code> value is represented by a
|
||||
* <code>null</code> reference of type {@link Data}, not as
|
||||
* <code>data() == null</code>. This is consistent with jOOQ's general way of
|
||||
* returning <code>NULL</code> from {@link Result} and {@link Record}
|
||||
* methods.</li>
|
||||
* <li>They're {@link Serializable}</li>
|
||||
* <li>They may or may not use an internal, normalised representation of their
|
||||
* {@link #data()} for {@link #equals(Object)}, {@link #hashCode()}, and
|
||||
* {@link #toString()} (e.g. {@link JSONB})</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
public interface Data extends Serializable {
|
||||
|
||||
/**
|
||||
* Get the vendor specific {@link String} representation of this
|
||||
* {@link Data} object.
|
||||
*/
|
||||
@NotNull
|
||||
String data();
|
||||
|
||||
}
|
||||
201
jOOQ/src/main/java/org/jooq/Decfloat.java
Normal file
201
jOOQ/src/main/java/org/jooq/Decfloat.java
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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
|
||||
* Apache-2.0 license and offer limited warranties, support, maintenance, and
|
||||
* commercial database integrations.
|
||||
*
|
||||
* For more information, please visit: https://www.jooq.org/legal/licensing
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A wrapper type for SQL:2016 standard <code>DECFLOAT</code> data types.
|
||||
* <p>
|
||||
* The wrapper represents DECFLOAT {@link #data()} in serialised string form. A
|
||||
* <code>CAST(NULL AS DECFLOAT)</code> value is represented by a
|
||||
* <code>null</code> reference of type {@link Decfloat}, not as
|
||||
* <code>data() == null</code>. This is consistent with jOOQ's general way of
|
||||
* returning <code>NULL</code> from {@link Result} and {@link Record} methods.
|
||||
*/
|
||||
public class Decfloat implements Data {
|
||||
|
||||
private final String data;
|
||||
private transient BigDecimal coefficient;
|
||||
private transient int exponent;
|
||||
private transient Special special;
|
||||
|
||||
private Decfloat(String data) {
|
||||
this.data = String.valueOf(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public final String data() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Decfloat} instance from string data input.
|
||||
*/
|
||||
@NotNull
|
||||
public static final Decfloat valueOf(String data) {
|
||||
return new Decfloat(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Decfloat} instance from string data input.
|
||||
* <p>
|
||||
* This is the same as {@link #valueOf(String)}, but it can be static
|
||||
* imported.
|
||||
*/
|
||||
@NotNull
|
||||
public static final Decfloat decfloat(String data) {
|
||||
return new Decfloat(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Decfloat} instance from string data input, or
|
||||
* <code>null</code> if the input is <code>null</code>.
|
||||
*/
|
||||
@Nullable
|
||||
public static final Decfloat decfloatOrNull(String data) {
|
||||
return data == null ? null : decfloat(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
parse();
|
||||
|
||||
if (special != null) {
|
||||
return special.hashCode();
|
||||
}
|
||||
else if (coefficient != null) {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
|
||||
result = prime * result + ((coefficient == null) ? 0 : coefficient.hashCode());
|
||||
result = prime * result + exponent;
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return data.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj instanceof Decfloat x) {
|
||||
parse();
|
||||
x.parse();
|
||||
|
||||
if (special != null && x.special != null)
|
||||
return special == x.special;
|
||||
else if (coefficient != null && x.coefficient != null)
|
||||
return Objects.equals(coefficient, x.coefficient) && exponent == x.exponent;
|
||||
else
|
||||
return Objects.equals(data, x.data);
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
parse();
|
||||
|
||||
if (special != null) {
|
||||
switch (special) {
|
||||
case NAN:
|
||||
return "NaN";
|
||||
case POSITIVE_INFINITY:
|
||||
return "Infinity";
|
||||
case NEGATIVE_INFINITY:
|
||||
return "-Infinity";
|
||||
}
|
||||
}
|
||||
else if (coefficient != null)
|
||||
return coefficient + "E" + exponent;
|
||||
|
||||
return String.valueOf(data);
|
||||
}
|
||||
|
||||
private void parse() {
|
||||
if (coefficient != null || special != null)
|
||||
return;
|
||||
|
||||
switch (data) {
|
||||
case "+NaN":
|
||||
case "NaN":
|
||||
special = Special.NAN;
|
||||
break;
|
||||
|
||||
case "+Infinity":
|
||||
case "Infinity":
|
||||
case "+Inf":
|
||||
case "Inf":
|
||||
special = Special.POSITIVE_INFINITY;
|
||||
break;
|
||||
|
||||
case "-Infinity":
|
||||
case "-Inf":
|
||||
special = Special.NEGATIVE_INFINITY;
|
||||
break;
|
||||
|
||||
default: {
|
||||
int i = data.indexOf("E");
|
||||
if (i == -1)
|
||||
i = data.indexOf("e");
|
||||
|
||||
try {
|
||||
coefficient = new BigDecimal(data.substring(0, i)).stripTrailingZeros();
|
||||
exponent = Integer.parseInt(data.substring(i + 1));
|
||||
}
|
||||
|
||||
// [#10880] If we cannot represent the value internally, then we'll just work with data
|
||||
catch (NumberFormatException ignore) {}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum Special {
|
||||
NAN, POSITIVE_INFINITY, NEGATIVE_INFINITY
|
||||
}
|
||||
}
|
||||
@ -37,8 +37,6 @@
|
||||
*/
|
||||
package org.jooq;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -68,7 +66,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
* <li>{@link #toString()}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class JSON implements Serializable {
|
||||
public final class JSON implements Data {
|
||||
|
||||
private final String data;
|
||||
|
||||
@ -76,6 +74,7 @@ public final class JSON implements Serializable {
|
||||
this.data = String.valueOf(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public final String data() {
|
||||
return data;
|
||||
|
||||
@ -37,7 +37,6 @@
|
||||
*/
|
||||
package org.jooq;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jooq.tools.json.JSONParser;
|
||||
@ -75,7 +74,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
* <p>
|
||||
* The {@link #data()} content, however, is not normalised.
|
||||
*/
|
||||
public final class JSONB implements Serializable {
|
||||
public final class JSONB implements Data {
|
||||
|
||||
private final String data;
|
||||
private transient Object parsed;
|
||||
@ -84,6 +83,7 @@ public final class JSONB implements Serializable {
|
||||
this.data = String.valueOf(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public final String data() {
|
||||
return data;
|
||||
|
||||
@ -37,10 +37,6 @@
|
||||
*/
|
||||
package org.jooq;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A wrapper type for spatial data obtained from the database.
|
||||
* <p>
|
||||
@ -54,9 +50,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
* <p>
|
||||
* This data type is supported only by the commercial editions of jOOQ.
|
||||
*/
|
||||
public interface Spatial extends Serializable {
|
||||
public interface Spatial extends Data {
|
||||
|
||||
@NotNull
|
||||
String data();
|
||||
}
|
||||
|
||||
|
||||
@ -37,8 +37,6 @@
|
||||
*/
|
||||
package org.jooq;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -51,7 +49,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
* consistent with jOOQ's general way of returning <code>NULL</code> from
|
||||
* {@link Result} and {@link Record} methods.
|
||||
*/
|
||||
public final class XML implements Serializable {
|
||||
public final class XML implements Data {
|
||||
|
||||
private final String data;
|
||||
|
||||
@ -59,6 +57,7 @@ public final class XML implements Serializable {
|
||||
this.data = String.valueOf(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public final String data() {
|
||||
return data;
|
||||
|
||||
@ -50,6 +50,8 @@ import static java.util.Arrays.asList;
|
||||
import static java.util.function.Function.identity;
|
||||
import static java.util.regex.Matcher.quoteReplacement;
|
||||
import static org.jooq.ContextConverter.scoped;
|
||||
import static org.jooq.Decfloat.decfloat;
|
||||
import static org.jooq.Decfloat.decfloatOrNull;
|
||||
import static org.jooq.Geography.geography;
|
||||
import static org.jooq.Geometry.geometry;
|
||||
// ...
|
||||
@ -257,6 +259,7 @@ import org.jooq.Converter;
|
||||
import org.jooq.ConverterContext;
|
||||
import org.jooq.Converters;
|
||||
import org.jooq.DataType;
|
||||
import org.jooq.Decfloat;
|
||||
import org.jooq.EnumType;
|
||||
import org.jooq.ExecuteScope;
|
||||
import org.jooq.Field;
|
||||
@ -376,6 +379,8 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
return new DefaultDateBinding(dataType, converter);
|
||||
else if (type == DayToSecond.class)
|
||||
return new DefaultDayToSecondBinding(dataType, converter);
|
||||
else if (type == Decfloat.class)
|
||||
return new DefaultDecfloatBinding(dataType, converter);
|
||||
else if (type == Double.class || type == double.class)
|
||||
return new DefaultDoubleBinding(dataType, converter);
|
||||
else if (type == Float.class || type == float.class)
|
||||
@ -875,6 +880,16 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
}
|
||||
}
|
||||
|
||||
if (dataType.getType() == Decfloat.class) {
|
||||
switch (ctx.family()) {
|
||||
|
||||
|
||||
case FIREBIRD:
|
||||
case H2:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1969,6 +1984,43 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
|
||||
}
|
||||
}
|
||||
|
||||
static final class DefaultDecfloatBinding<U> extends InternalBinding<Decfloat, U> {
|
||||
|
||||
DefaultDecfloatBinding(DataType<Decfloat> dataType, Converter<Decfloat, U> converter) {
|
||||
super(dataType, converter);
|
||||
}
|
||||
|
||||
@Override
|
||||
final void set0(BindingSetStatementContext<U> ctx, Decfloat value) throws SQLException {
|
||||
ctx.statement().setString(ctx.index(), value.data());
|
||||
}
|
||||
|
||||
@Override
|
||||
final void set0(BindingSetSQLOutputContext<U> ctx, Decfloat value) throws SQLException {
|
||||
ctx.output().writeString(value.data());
|
||||
}
|
||||
|
||||
@Override
|
||||
final Decfloat get0(BindingGetResultSetContext<U> ctx) throws SQLException {
|
||||
return decfloatOrNull(ctx.resultSet().getString(ctx.index()));
|
||||
}
|
||||
|
||||
@Override
|
||||
final Decfloat get0(BindingGetStatementContext<U> ctx) throws SQLException {
|
||||
return decfloatOrNull(ctx.statement().getString(ctx.index()));
|
||||
}
|
||||
|
||||
@Override
|
||||
final Decfloat get0(BindingGetSQLInputContext<U> ctx) throws SQLException {
|
||||
return decfloatOrNull(ctx.input().readString());
|
||||
}
|
||||
|
||||
@Override
|
||||
final int sqltype(Statement statement, Configuration configuration) {
|
||||
return Types.VARCHAR;
|
||||
}
|
||||
}
|
||||
|
||||
static final class DefaultBlobBinding<U> extends InternalBinding<Blob, U> {
|
||||
|
||||
DefaultBlobBinding(DataType<Blob> dataType, Converter<Blob, U> converter) {
|
||||
|
||||
@ -91,6 +91,7 @@ import java.util.UUID;
|
||||
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.DataType;
|
||||
import org.jooq.Decfloat;
|
||||
import org.jooq.Geography;
|
||||
import org.jooq.Geometry;
|
||||
import org.jooq.JSON;
|
||||
@ -777,6 +778,32 @@ public final class SQLDataType {
|
||||
*/
|
||||
public static final DataType<Geometry> GEOMETRY = new BuiltInDataType<>(Geometry.class, "geometry");
|
||||
|
||||
/**
|
||||
* The {@link Decfloat} type.
|
||||
* <p>
|
||||
* This is not a JDBC standard. This type handles DECFLOAT types where they
|
||||
* are supported.
|
||||
* <p>
|
||||
* If you want to opt out of code generation support for this type, you can
|
||||
* specify <code>/configuration/generator/generate/decfloatTypes</code> to
|
||||
* <code>false</code>.
|
||||
*/
|
||||
public static final DataType<Decfloat> DECFLOAT = new BuiltInDataType<>(Decfloat.class, "decfloat(p)");
|
||||
|
||||
/**
|
||||
* The {@link Decfloat} type.
|
||||
* <p>
|
||||
* This is not a JDBC standard. This type handles DECFLOAT types where they
|
||||
* are supported.
|
||||
* <p>
|
||||
* If you want to opt out of code generation support for this type, you can
|
||||
* specify <code>/configuration/generator/generate/decfloatTypes</code> to
|
||||
* <code>false</code>.
|
||||
*/
|
||||
public static final DataType<Decfloat> DECFLOAT(int precision) {
|
||||
return DECFLOAT.precision(precision, 0);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Static initialisation of dialect-specific data types
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -47,6 +47,7 @@ import java.time.Year;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.jooq.DataType;
|
||||
import org.jooq.Decfloat;
|
||||
import org.jooq.JSON;
|
||||
import org.jooq.JSONB;
|
||||
import org.jooq.SQLDialect;
|
||||
@ -103,6 +104,7 @@ public class FirebirdDataType {
|
||||
public static final DataType<Time> TIME = new BuiltInDataType<>(FAMILY, SQLDataType.TIME, "time");
|
||||
public static final DataType<Timestamp> TIMESTAMP = new BuiltInDataType<>(FAMILY, SQLDataType.TIMESTAMP, "timestamp");
|
||||
public static final DataType<byte[]> BLOB = new BuiltInDataType<>(FAMILY, SQLDataType.BLOB, "blob");
|
||||
public static final DataType<Decfloat> DECFLOAT = new BuiltInDataType<>(FAMILY, SQLDataType.DECFLOAT, "decfloat");
|
||||
|
||||
// TODO Below are HSQLDB data types. Fix this
|
||||
|
||||
|
||||
@ -48,6 +48,7 @@ import java.time.Year;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.jooq.DataType;
|
||||
import org.jooq.Decfloat;
|
||||
import org.jooq.JSON;
|
||||
import org.jooq.JSONB;
|
||||
import org.jooq.Record;
|
||||
@ -149,6 +150,7 @@ public class H2DataType {
|
||||
public static final DataType<JSONB> JSONB = new BuiltInDataType<>(FAMILY, SQLDataType.JSONB, "json");
|
||||
public static final DataType<YearToMonth> INTERVALYEARTOMONTH = new BuiltInDataType<>(FAMILY, SQLDataType.INTERVALYEARTOMONTH, "interval year to month");
|
||||
public static final DataType<DayToSecond> INTERVALDAYTOSECOND = new BuiltInDataType<>(FAMILY, SQLDataType.INTERVALDAYTOSECOND, "interval day to second", "interval day(9) to second");
|
||||
public static final DataType<Decfloat> DECFLOAT = new BuiltInDataType<>(FAMILY, SQLDataType.DECFLOAT, "decfloat");
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Compatibility types for supported SQLDialect.H2, SQLDataTypes
|
||||
|
||||
Loading…
Reference in New Issue
Block a user