[jOOQ/jOOQ#9061] Wrong data types in meta lookups for expressions

When unqualified column names are part of expressions (e.g. ID + 1) then the data type information is lost on both the column reference and the whole expression.
This commit is contained in:
Lukas Eder 2020-06-26 13:38:14 +02:00
parent 77a3943a65
commit 78f7c67f67
6 changed files with 869 additions and 2324 deletions

View File

@ -0,0 +1,711 @@
/*
* 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
*
* http://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: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
import static org.jooq.Nullability.NOT_NULL;
import static org.jooq.impl.DSL.unquotedName;
import static org.jooq.impl.SQLDataType.BLOB;
import static org.jooq.impl.SQLDataType.CLOB;
import static org.jooq.impl.SQLDataType.NCLOB;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.util.Collection;
import java.util.List;
// ...
import org.jooq.Binding;
import org.jooq.CharacterSet;
import org.jooq.Collation;
import org.jooq.Comment;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.Converter;
import org.jooq.DataType;
import org.jooq.Domain;
import org.jooq.EnumType;
import org.jooq.Field;
import org.jooq.JSON;
import org.jooq.JSONB;
import org.jooq.Name;
import org.jooq.Nullability;
// ...
import org.jooq.Result;
import org.jooq.UDTRecord;
import org.jooq.XML;
import org.jooq.tools.Convert;
import org.jooq.types.Interval;
import org.jooq.types.UNumber;
/**
* @author Lukas Eder
*/
@SuppressWarnings({ "rawtypes", "unchecked", "deprecation" })
abstract class AbstractDataType<T> extends AbstractNamed implements DataType<T> {
/**
* Generated UID
*/
private static final long serialVersionUID = 4155588654449505119L;
AbstractDataType(Name name, Comment comment) {
super(name, comment);
}
/**
* [#7811] Allow for subtypes to override the constructor
*/
abstract AbstractDataType<T> construct(
Integer newPrecision,
Integer newScale,
Integer newLength,
Nullability newNullability,
Collation newCollation,
CharacterSet newCharacterSet,
boolean newIdentity,
Field<T> newDefaultValue
);
@Override
public final DataType<T> nullability(Nullability n) {
return construct(precision0(), scale0(), length0(), n, collation(), characterSet(), n.nullable() ? false : identity(), defaultValue());
}
@Override
public final DataType<T> nullable(boolean n) {
return nullability(Nullability.of(n));
}
@Override
public final boolean nullable() {
return nullability().nullable();
}
@Override
public final DataType<T> collation(Collation c) {
return construct(precision0(), scale0(), length0(), nullability(), c, characterSet(), identity(), defaultValue());
}
@Override
public final DataType<T> characterSet(CharacterSet c) {
return construct(precision0(), scale0(), length0(), nullability(), collation(), c, identity(), defaultValue());
}
@Override
public final DataType<T> identity(boolean i) {
return construct(precision0(), scale0(), length0(), i ? NOT_NULL : nullability(), collation(), characterSet(), i, i ? null : defaultValue());
}
@Override
public final DataType<T> defaultValue(T d) {
return default_(d);
}
@Override
public final DataType<T> defaultValue(Field<T> d) {
return default_(d);
}
@Override
public final Field<T> defaultValue() {
return default_();
}
@Override
public final DataType<T> default_(T d) {
return default_(Tools.field(d, this));
}
@Override
public final DataType<T> default_(Field<T> d) {
return construct(precision0(), scale0(), length0(), nullability(), collation(), characterSet(), d != null ? false : identity(), d);
}
@Override
@Deprecated
public final DataType<T> defaulted(boolean d) {
return defaultValue(d ? Tools.field(null, this) : null);
}
@Override
public final boolean defaulted() {
return defaultValue() != null;
}
@Override
public final int precision() {
Integer precision = precision0();
return precision == null ? 0 : precision;
}
@Override
public final DataType<T> precision(int p) {
return precision0(p, scale());
}
@Override
public final DataType<T> precision(int p, int s) {
return precision0(p, s);
}
final AbstractDataType<T> precision0(Integer p, Integer s) {
if (eq(precision(), p) && eq(scale(), s))
return this;
// [#4120] LOB types are not allowed to have precision
else if (isLob())
return this;
else
return construct(p, s, length0(), nullability(), collation(), characterSet(), identity(), defaultValue());
}
@Override
public final boolean hasPrecision() {
Class<?> tType = tType0();
return tType == BigInteger.class
|| tType == BigDecimal.class
|| tType == Timestamp.class
|| tType == Time.class
|| tType == LocalDateTime.class
|| tType == LocalTime.class
|| tType == OffsetDateTime.class
|| tType == OffsetTime.class
|| tType == Instant.class
;
}
@Override
public final boolean precisionDefined() {
return precision0() != null && hasPrecision();
}
@Override
public final int scale() {
Integer scale = scale0();
return scale == null ? 0 : scale;
}
@Override
public final DataType<T> scale(int s) {
return scale0(s);
}
final AbstractDataType<T> scale0(Integer s) {
if (eq(scale(), s))
return this;
// [#4120] LOB types are not allowed to have scale
if (isLob())
return this;
else
return construct(precision0(), s, length0(), nullability(), collation(), characterSet(), identity(), defaultValue());
}
@Override
public final boolean hasScale() {
return tType0() == BigDecimal.class;
}
@Override
public final boolean scaleDefined() {
return scale0() != null && hasScale();
}
@Override
public final DataType<T> length(int l) {
return length0(l);
}
final AbstractDataType<T> length0(Integer l) {
if (eq(length0(), l))
return this;
// [#4120] LOB types are not allowed to have length
if (isLob())
return this;
else
return construct(precision0(), scale0(), l, nullability(), collation(), characterSet(), identity(), defaultValue());
}
@Override
public final int length() {
Integer length = length0();
return length == null ? 0 : length;
}
@Override
public final boolean hasLength() {
Class<?> tType = tType0();
return (tType == byte[].class || tType == String.class) && !isLob();
}
@Override
public final boolean lengthDefined() {
return length0() != null && hasLength();
}
@Override
public /* final */ int getSQLType() {
return getSQLType(DSL.using(getDialect()).configuration());
}
@Override
public final int getSQLType(Configuration configuration) {
// TODO [#1227] There is some confusion with these types, especially
// when it comes to byte[] which can be mapped to BLOB, BINARY, VARBINARY
Class<?> tType = tType0();
if (tType == Blob.class)
return Types.BLOB;
else if (tType == Boolean.class)
return Types.BOOLEAN;
else if (tType == BigInteger.class)
return Types.BIGINT;
else if (tType == BigDecimal.class)
return Types.DECIMAL;
else if (tType == Byte.class)
return Types.TINYINT;
else if (tType == byte[].class)
return Types.BLOB;
else if (tType == Clob.class)
return Types.CLOB;
else if (Tools.isDate(tType)) {
switch (configuration.family()) {
default:
return Types.DATE;
}
}
else if (tType == Double.class)
return Types.DOUBLE;
else if (tType == Float.class)
return Types.FLOAT;
else if (tType == Integer.class)
return Types.INTEGER;
else if (tType == Long.class)
return Types.BIGINT;
else if (tType == Short.class)
return Types.SMALLINT;
else if (tType == String.class)
return Types.VARCHAR;
else if (Tools.isTime(tType))
return Types.TIME;
else if (Tools.isTimestamp(tType))
return Types.TIMESTAMP;
// [#5779] Few JDBC drivers support the JDBC 4.2 TIME[STAMP]_WITH_TIMEZONE types.
else if (tType == OffsetTime.class)
return Types.VARCHAR;
else if (tType == OffsetDateTime.class)
return Types.VARCHAR;
else if (tType == Instant.class)
return Types.VARCHAR;
// The type byte[] is handled earlier.
else if (tType.isArray())
return Types.ARRAY;
else if (EnumType.class.isAssignableFrom(tType))
return Types.VARCHAR;
else if (UDTRecord.class.isAssignableFrom(tType))
return Types.STRUCT;
else if (Result.class.isAssignableFrom(tType)) {
switch (configuration.family()) {
case H2:
return -10; // OracleTypes.CURSOR;
case POSTGRES:
default:
return Types.OTHER;
}
}
else
return Types.OTHER;
}
@Override
public /* non-final */ Domain<T> getDomain() {
return null;
}
@Override
public final Converter<?, T> getConverter() {
return getBinding().converter();
}
@Override
public final String getTypeName() {
return typeName0();
}
@Override
public final String getCastTypeName() {
// [#9958] We should be able to avoid checking for x > 0, but there may
// be a lot of data types constructed with a 0 value instead of
// a null value, historically, so removing this check would
// introduce a lot of regressions!
if (lengthDefined() && length() > 0)
return castTypeBase0() + "(" + length() + ")";
else if (precisionDefined() && precision() > 0)
if (scaleDefined() && scale() > 0)
return castTypeBase0() + "(" + precision() + ", " + scale() + ")";
else
return castTypeBase0() + "(" + precision() + ")";
else
return castTypeName0();
}
@Override
public /* non-final */ String getTypeName(Configuration configuration) {
return getDataType(configuration).getTypeName();
}
@Override
public /* non-final */ String getCastTypeName(Configuration configuration) {
return getDataType(configuration).getCastTypeName();
}
@SuppressWarnings("unchecked")
@Override
public final Class<T[]> getArrayType() {
return (Class<T[]>) Array.newInstance(getType(), 0).getClass();
}
@Override
public final DataType<T[]> getArrayDataType() {
return new ArrayDataType<>(this);
}
@Override
public /* non-final */ Class<?> getArrayComponentType() {
return null;
}
@Override
public /* non-final */ DataType<?> getArrayComponentDataType() {
return null;
}
@Override
public final <E extends EnumType> DataType<E> asEnumDataType(Class<E> enumDataType) {
// TODO: Make EnumTypes implement Named
String enumTypeName = Tools.enums(enumDataType)[0].getName();
return new DefaultDataType<>(getDialect(), (DataType<E>) null, enumDataType, unquotedName(enumTypeName), enumTypeName, enumTypeName, precision0(), scale0(), length0(), nullability(), (Field) defaultValue());
}
@Override
public /* non-final */ <U> DataType<U> asConvertedDataType(Converter<? super T, U> converter) {
return asConvertedDataType(DefaultBinding.newBinding(converter, this, null));
}
@Override
public /* non-final */ <U> DataType<U> asConvertedDataType(Binding<? super T, U> newBinding) {
if (getBinding() == newBinding)
return (DataType<U>) this;
if (newBinding == null)
newBinding = (Binding<? super T, U>) DefaultBinding.binding(getType(), isLob());
return new ConvertedDataType<>(this, newBinding);
}
@Override
public /* final */ T convert(Object object) {
// [#1441] Avoid unneeded type conversions to improve performance
if (object == null)
return null;
else if (object.getClass() == getType())
return (T) object;
else
return Convert.convert(object, getType());
}
@Override
public final T[] convert(Object... objects) {
return (T[]) Convert.convertArray(objects, getType());
}
@Override
public final List<T> convert(Collection<?> objects) {
return Convert.convert(objects, getType());
}
@Override
public final boolean isNumeric() {
return Number.class.isAssignableFrom(tType0()) && !isInterval();
}
@Override
public final boolean isInteger() {
Class<?> tType = tType0();
return UNumber.class.isAssignableFrom(tType)
|| Byte.class == tType
|| Short.class == tType
|| Integer.class == tType
|| Long.class == tType
;
}
@Override
public final boolean isString() {
return tType0() == String.class;
}
@Override
public final boolean isDateTime() {
Class<?> tType = tType0();
return java.util.Date.class.isAssignableFrom(tType)
|| java.time.temporal.Temporal.class.isAssignableFrom(tType)
;
}
@Override
public final boolean isDate() {
Class<?> tType = tType0();
return java.sql.Date.class.isAssignableFrom(tType)
|| java.time.LocalDate.class.isAssignableFrom(tType)
;
}
@Override
public final boolean isTimestamp() {
Class<?> tType = tType0();
return java.sql.Timestamp.class.isAssignableFrom(tType)
|| java.time.LocalDateTime.class.isAssignableFrom(tType)
;
}
@Override
public final boolean isTime() {
Class<?> tType = tType0();
return java.sql.Time.class.isAssignableFrom(tType)
|| java.time.LocalTime.class.isAssignableFrom(tType)
;
}
@Override
public final boolean isTemporal() {
return isDateTime() || isInterval();
}
@Override
public final boolean isInterval() {
return Interval.class.isAssignableFrom(tType0());
}
@Override
public final boolean isLob() {
DataType<T> t = getSQLDataType();
if (t == this)
return getTypeName().endsWith("lob");
else
return (t == BLOB || t == CLOB || t == NCLOB);
}
@Override
public final boolean isBinary() {
return tType0() == byte[].class;
}
@Override
public final boolean isArray() {
Class<?> tType = tType0();
return
(!isBinary() && tType.isArray());
}
@Override
public final boolean isUDT() {
return UDTRecord.class.isAssignableFrom(tType0());
}
@Override
public final boolean isEnum() {
return EnumType.class.isAssignableFrom(tType0());
}
@Override
public final boolean isJSON() {
Class<?> tType = tType0();
return tType == JSON.class || tType == JSONB.class;
}
@Override
public final boolean isXML() {
return tType0() == XML.class;
}
@Override
public final void accept(Context<?> ctx) {
ctx.visit(getQualifiedName());
}
private static final boolean eq(Integer i1, Integer i2) {
return (i1 == i2) || (i1 != null && i2 != null && i1.intValue() == i2.intValue());
}
abstract String typeName0();
abstract String castTypeBase0();
abstract String castTypeName0();
abstract Class<?> tType0();
abstract Integer precision0();
abstract Integer scale0();
abstract Integer length0();
// ------------------------------------------------------------------------
// The Object API
// ------------------------------------------------------------------------
@Override
public String toString() {
return getCastTypeName() + " (" + getType().getName() + ")";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getDialect() == null) ? 0 : getDialect().hashCode());
result = prime * result + length();
result = prime * result + precision();
result = prime * result + scale();
result = prime * result + ((getType() == null) ? 0 : getType().hashCode());
result = prime * result + ((tType0() == null) ? 0 : tType0().hashCode());
result = prime * result + ((typeName0() == null) ? 0 : typeName0().hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof AbstractDataType))
return false;
AbstractDataType<?> other = (AbstractDataType<?>) obj;
if (getDialect() != other.getDialect())
return false;
if (!eq(length0(), other.length0()))
return false;
if (!eq(precision0(), other.precision0()))
return false;
if (!eq(scale0(), other.scale0()))
return false;
if (getType() == null) {
if (other.getType() != null)
return false;
}
else if (!getType().equals(other.getType()))
return false;
if (tType0() == null) {
if (other.tType0() != null)
return false;
}
else if (!tType0().equals(other.tType0()))
return false;
if (typeName0() == null) {
if (other.typeName0() != null)
return false;
}
else if (!typeName0().equals(other.typeName0()))
return false;
return true;
}
}

View File

@ -66,17 +66,17 @@ abstract class AbstractTypedNamed<T> extends AbstractNamed implements Typed<T> {
@Override
public final Converter<?, T> getConverter() {
return type.getConverter();
return getDataType().getConverter();
}
@Override
public final Binding<?, T> getBinding() {
return type.getBinding();
return getDataType().getBinding();
}
@Override
public final Class<T> getType() {
return type.getType();
return getDataType().getType();
}
@Override
@ -86,6 +86,6 @@ abstract class AbstractTypedNamed<T> extends AbstractNamed implements Typed<T> {
@Override
public final DataType<T> getDataType(Configuration configuration) {
return type.getDataType(configuration);
return getDataType().getDataType(configuration);
}
}

View File

@ -71,7 +71,7 @@ final class ConvertedDataType<T, U> extends DefaultDataType<U> {
private final DataType<T> delegate;
@SuppressWarnings("unchecked")
ConvertedDataType(DefaultDataType<T> delegate, Binding<? super T, U> binding) {
ConvertedDataType(AbstractDataType<T> delegate, Binding<? super T, U> binding) {
super(
null,
binding.converter().toType(),

View File

@ -38,34 +38,18 @@
package org.jooq.impl;
import static java.util.Collections.unmodifiableCollection;
import static org.jooq.Nullability.NOT_NULL;
// ...
import static org.jooq.SQLDialect.HSQLDB;
import static org.jooq.SQLDialect.POSTGRES;
import static org.jooq.impl.CommentImpl.NO_COMMENT;
import static org.jooq.impl.DSL.unquotedName;
import static org.jooq.impl.DefaultBinding.binding;
import static org.jooq.impl.SQLDataType.BLOB;
import static org.jooq.impl.SQLDataType.CLOB;
import static org.jooq.impl.SQLDataType.NCLOB;
import static org.jooq.tools.reflect.Reflect.wrapper;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
@ -74,30 +58,19 @@ import org.jooq.Binding;
import org.jooq.CharacterSet;
import org.jooq.Collation;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.Converter;
import org.jooq.DataType;
import org.jooq.Domain;
import org.jooq.EnumType;
import org.jooq.Field;
import org.jooq.JSON;
import org.jooq.JSONB;
import org.jooq.Name;
import org.jooq.Nullability;
// ...
import org.jooq.Result;
import org.jooq.SQLDialect;
import org.jooq.TableRecord;
import org.jooq.UDTRecord;
import org.jooq.XML;
import org.jooq.exception.MappingException;
import org.jooq.exception.SQLDialectNotSupportedException;
import org.jooq.tools.Convert;
import org.jooq.types.Interval;
import org.jooq.types.UByte;
import org.jooq.types.UInteger;
import org.jooq.types.ULong;
import org.jooq.types.UNumber;
import org.jooq.types.UShort;
/**
@ -111,7 +84,7 @@ import org.jooq.types.UShort;
*/
@SuppressWarnings({"unchecked"})
@org.jooq.Internal
public class DefaultDataType<T> extends AbstractNamed implements DataType<T> {
public class DefaultDataType<T> extends AbstractDataType<T> {
/**
* Generated UID
@ -212,11 +185,6 @@ public class DefaultDataType<T> extends AbstractNamed implements DataType<T> {
*/
private final Binding<?, T> binding;
/**
* The Java class corresponding to arrays of this data type.
*/
private final Class<T[]> arrayType;
/**
* The type name used for casting to this type.
*/
@ -322,7 +290,6 @@ public class DefaultDataType<T> extends AbstractNamed implements DataType<T> {
this.typeName = typeName;
this.castTypeName = castTypeName;
this.castTypeBase = TYPE_NAME_PATTERN.matcher(castTypeName).replaceAll("").trim();
this.arrayType = (Class<T[]>) Array.newInstance(type, 0).getClass();
this.nullability = nullability;
this.collation = collation;
@ -365,6 +332,7 @@ public class DefaultDataType<T> extends AbstractNamed implements DataType<T> {
/**
* [#7811] Allow for subtypes to override the constructor
*/
@Override
DefaultDataType<T> construct(
Integer newPrecision,
Integer newScale,
@ -401,7 +369,6 @@ public class DefaultDataType<T> extends AbstractNamed implements DataType<T> {
this.typeName = t.typeName;
this.castTypeName = t.castTypeName;
this.castTypeBase = t.castTypeBase;
this.arrayType = t.arrayType;
this.nullability = nullability;
this.collation = collation;
@ -429,204 +396,44 @@ public class DefaultDataType<T> extends AbstractNamed implements DataType<T> {
return precision;
}
@Override
public final DataType<T> nullability(Nullability n) {
return construct(precision, scale, length, n, collation, characterSet, n.nullable() ? false : identity, defaultValue);
}
@Override
public final Nullability nullability() {
return nullability;
}
@Override
public final DataType<T> nullable(boolean n) {
return nullability(Nullability.of(n));
}
@Override
public final boolean nullable() {
return nullability.nullable();
}
@Override
public final DataType<T> collation(Collation c) {
return construct(precision, scale, length, nullability, c, characterSet, identity, defaultValue);
}
@Override
public final Collation collation() {
return collation;
}
@Override
public final DataType<T> characterSet(CharacterSet c) {
return construct(precision, scale, length, nullability, collation, c, identity, defaultValue);
}
@Override
public final CharacterSet characterSet() {
return characterSet;
}
@Override
public final DataType<T> identity(boolean i) {
return construct(precision, scale, length, i ? NOT_NULL : nullability, collation, characterSet, i, i ? null : defaultValue);
}
@Override
public final boolean identity() {
return identity;
}
@Override
public final DataType<T> defaultValue(T d) {
return default_(d);
}
@Override
public final DataType<T> defaultValue(Field<T> d) {
return default_(d);
}
@Override
public final Field<T> defaultValue() {
return default_();
}
@Override
public final DataType<T> default_(T d) {
return default_(Tools.field(d, this));
}
@Override
public final DataType<T> default_(Field<T> d) {
return construct(precision, scale, length, nullability, collation, characterSet, d != null ? false : identity, d);
}
@Override
public final Field<T> default_() {
return defaultValue;
}
@Override
@Deprecated
public final DataType<T> defaulted(boolean d) {
return defaultValue(d ? Tools.field(null, this) : null);
final Integer precision0() {
return precision;
}
@Override
public final boolean defaulted() {
return defaultValue != null;
final Integer scale0() {
return scale;
}
@Override
public final DataType<T> precision(int p) {
return precision0(p, scale);
}
@Override
public final DataType<T> precision(int p, int s) {
return precision0(p, s);
}
private final DefaultDataType<T> precision0(Integer p, Integer s) {
if (eq(precision, p) && eq(scale, s))
return this;
// [#4120] LOB types are not allowed to have precision
else if (isLob())
return this;
else
return construct(p, s, length, nullability, collation, characterSet, identity, defaultValue);
}
@Override
public final int precision() {
return precision == null ? 0 : precision;
}
@Override
public final boolean hasPrecision() {
return tType == BigInteger.class
|| tType == BigDecimal.class
|| tType == Timestamp.class
|| tType == Time.class
|| tType == LocalDateTime.class
|| tType == LocalTime.class
|| tType == OffsetDateTime.class
|| tType == OffsetTime.class
|| tType == Instant.class
;
}
@Override
public final boolean precisionDefined() {
return precision != null && hasPrecision();
}
@Override
public final DataType<T> scale(int s) {
return scale0(s);
}
private final DefaultDataType<T> scale0(Integer s) {
if (eq(scale, s))
return this;
// [#4120] LOB types are not allowed to have scale
if (isLob())
return this;
else
return construct(precision, s, length, nullability, collation, characterSet, identity, defaultValue);
}
@Override
public final int scale() {
return scale == null ? 0 : scale;
}
@Override
public final boolean hasScale() {
return tType == BigDecimal.class;
}
@Override
public final boolean scaleDefined() {
return scale != null && hasScale();
}
@Override
public final DataType<T> length(int l) {
return length0(l);
}
private final DefaultDataType<T> length0(Integer l) {
if (eq(length, l))
return this;
// [#4120] LOB types are not allowed to have length
if (isLob())
return this;
else
return construct(precision, scale, l, nullability, collation, characterSet, identity, defaultValue);
}
@Override
public final int length() {
return length == null ? 0 : length;
}
@Override
public final boolean hasLength() {
return (tType == byte[].class || tType == String.class) && !isLob();
}
@Override
public final boolean lengthDefined() {
return length != null && hasLength();
final Integer length0() {
return length;
}
@Override
@ -671,216 +478,34 @@ public class DefaultDataType<T> extends AbstractNamed implements DataType<T> {
return this;
}
@Override
public /* final */ int getSQLType() {
return getSQLType(DSL.using(dialect).configuration());
}
@Override
public final int getSQLType(Configuration configuration) {
// TODO [#1227] There is some confusion with these types, especially
// when it comes to byte[] which can be mapped to BLOB, BINARY, VARBINARY
if (tType == Blob.class)
return Types.BLOB;
else if (tType == Boolean.class)
return Types.BOOLEAN;
else if (tType == BigInteger.class)
return Types.BIGINT;
else if (tType == BigDecimal.class)
return Types.DECIMAL;
else if (tType == Byte.class)
return Types.TINYINT;
else if (tType == byte[].class)
return Types.BLOB;
else if (tType == Clob.class)
return Types.CLOB;
else if (Tools.isDate(tType)) {
switch (configuration.family()) {
default:
return Types.DATE;
}
}
else if (tType == Double.class)
return Types.DOUBLE;
else if (tType == Float.class)
return Types.FLOAT;
else if (tType == Integer.class)
return Types.INTEGER;
else if (tType == Long.class)
return Types.BIGINT;
else if (tType == Short.class)
return Types.SMALLINT;
else if (tType == String.class)
return Types.VARCHAR;
else if (Tools.isTime(tType))
return Types.TIME;
else if (Tools.isTimestamp(tType))
return Types.TIMESTAMP;
// [#5779] Few JDBC drivers support the JDBC 4.2 TIME[STAMP]_WITH_TIMEZONE types.
else if (tType == OffsetTime.class)
return Types.VARCHAR;
else if (tType == OffsetDateTime.class)
return Types.VARCHAR;
else if (tType == Instant.class)
return Types.VARCHAR;
// The type byte[] is handled earlier.
else if (tType.isArray())
return Types.ARRAY;
else if (EnumType.class.isAssignableFrom(tType))
return Types.VARCHAR;
else if (UDTRecord.class.isAssignableFrom(tType))
return Types.STRUCT;
else if (Result.class.isAssignableFrom(tType)) {
switch (configuration.family()) {
case H2:
return -10; // OracleTypes.CURSOR;
case POSTGRES:
default:
return Types.OTHER;
}
}
else
return Types.OTHER;
}
@Override
public final Class<T> getType() {
return uType;
}
@Override
public /* non-final */ Domain<T> getDomain() {
return null;
}
@Override
public final Binding<?, T> getBinding() {
return binding;
}
@Override
public final Converter<?, T> getConverter() {
return binding.converter();
}
@Override
public final String getTypeName() {
final String typeName0() {
return typeName;
}
@Override
public /* non-final */ String getTypeName(Configuration configuration) {
return getDataType(configuration).getTypeName();
final String castTypeBase0() {
return castTypeBase;
}
@Override
public final String getCastTypeName() {
// [#9958] We should be able to avoid checking for x > 0, but there may
// be a lot of data types constructed with a 0 value instead of
// a null value, historically, so removing this check would
// introduce a lot of regressions!
if (lengthDefined() && length() > 0)
return castTypeBase + "(" + length + ")";
else if (precisionDefined() && precision() > 0)
if (scaleDefined() && scale() > 0)
return castTypeBase + "(" + precision + ", " + scale + ")";
else
return castTypeBase + "(" + precision + ")";
else
return castTypeName;
final String castTypeName0() {
return castTypeName;
}
@Override
public /* non-final */ String getCastTypeName(Configuration configuration) {
return getDataType(configuration).getCastTypeName();
}
@Override
public final Class<T[]> getArrayType() {
return arrayType;
}
@Override
public final DataType<T[]> getArrayDataType() {
return new ArrayDataType<>(this);
}
@Override
public /* non-final */ Class<?> getArrayComponentType() {
return null;
}
@Override
public /* non-final */ DataType<?> getArrayComponentDataType() {
return null;
}
@SuppressWarnings("rawtypes")
@Override
public final <E extends EnumType> DataType<E> asEnumDataType(Class<E> enumDataType) {
// TODO: Make EnumTypes implement Named
String enumTypeName = Tools.enums(enumDataType)[0].getName();
return new DefaultDataType<>(dialect, (DataType<E>) null, enumDataType, unquotedName(enumTypeName), enumTypeName, enumTypeName, precision, scale, length, nullability, (Field) defaultValue);
}
@Override
public /* non-final */ <U> DataType<U> asConvertedDataType(Converter<? super T, U> converter) {
return asConvertedDataType(DefaultBinding.newBinding(converter, this, null));
}
@SuppressWarnings("deprecation")
@Override
public /* non-final */ <U> DataType<U> asConvertedDataType(Binding<? super T, U> newBinding) {
if (binding == newBinding)
return (DataType<U>) this;
if (newBinding == null)
newBinding = (Binding<? super T, U>) DefaultBinding.binding(getType(), isLob());
return new ConvertedDataType<>(this, newBinding);
final Class<?> tType0() {
return tType;
}
@Override
@ -888,28 +513,6 @@ public class DefaultDataType<T> extends AbstractNamed implements DataType<T> {
return dialect;
}
@Override
public /* final */ T convert(Object object) {
// [#1441] Avoid unneeded type conversions to improve performance
if (object == null)
return null;
else if (object.getClass() == uType)
return (T) object;
else
return Convert.convert(object, uType);
}
@Override
public final T[] convert(Object... objects) {
return (T[]) Convert.convertArray(objects, uType);
}
@Override
public final List<T> convert(Collection<?> objects) {
return Convert.convert(objects, uType);
}
public static final DataType<Object> getDefaultDataType(String typeName) {
return new DefaultDataType<>(SQLDialect.DEFAULT, Object.class, typeName, typeName);
}
@ -1029,174 +632,6 @@ public class DefaultDataType<T> extends AbstractNamed implements DataType<T> {
}
}
@Override
public final boolean isNumeric() {
return Number.class.isAssignableFrom(tType) && !isInterval();
}
@Override
public final boolean isInteger() {
return UNumber.class.isAssignableFrom(tType)
|| Byte.class == tType
|| Short.class == tType
|| Integer.class == tType
|| Long.class == tType
;
}
@Override
public final boolean isString() {
return tType == String.class;
}
@Override
public final boolean isDateTime() {
return java.util.Date.class.isAssignableFrom(tType)
|| java.time.temporal.Temporal.class.isAssignableFrom(tType)
;
}
@Override
public final boolean isDate() {
return java.sql.Date.class.isAssignableFrom(tType)
|| java.time.LocalDate.class.isAssignableFrom(tType)
;
}
@Override
public final boolean isTimestamp() {
return java.sql.Timestamp.class.isAssignableFrom(tType)
|| java.time.LocalDateTime.class.isAssignableFrom(tType)
;
}
@Override
public final boolean isTime() {
return java.sql.Time.class.isAssignableFrom(tType)
|| java.time.LocalTime.class.isAssignableFrom(tType)
;
}
@Override
public final boolean isTemporal() {
return isDateTime() || isInterval();
}
@Override
public final boolean isInterval() {
return Interval.class.isAssignableFrom(tType);
}
@Override
public final boolean isLob() {
DataType<T> t = getSQLDataType();
if (t == this)
return getTypeName().endsWith("lob");
else
return (t == BLOB || t == CLOB || t == NCLOB);
}
@Override
public final boolean isBinary() {
return tType == byte[].class;
}
@Override
public final boolean isArray() {
return
(!isBinary() && tType.isArray());
}
@Override
public final boolean isUDT() {
return UDTRecord.class.isAssignableFrom(tType);
}
@Override
public final boolean isEnum() {
return EnumType.class.isAssignableFrom(tType);
}
@Override
public final boolean isJSON() {
return tType == JSON.class || tType == JSONB.class;
}
@Override
public final boolean isXML() {
return tType == XML.class;
}
// ------------------------------------------------------------------------
// The Object API
// ------------------------------------------------------------------------
@Override
public String toString() {
return getCastTypeName() + " (" + uType.getName() + ")";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((dialect == null) ? 0 : dialect.hashCode());
result = prime * result + length();
result = prime * result + precision();
result = prime * result + scale();
result = prime * result + ((uType == null) ? 0 : uType.hashCode());
result = prime * result + ((tType == null) ? 0 : tType.hashCode());
result = prime * result + ((typeName == null) ? 0 : typeName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DefaultDataType<?> other = (DefaultDataType<?>) obj;
if (dialect != other.dialect)
return false;
if (!eq(length, other.length))
return false;
if (!eq(precision, other.precision))
return false;
if (!eq(scale, other.scale))
return false;
if (uType == null) {
if (other.uType != null)
return false;
}
else if (!uType.equals(other.uType))
return false;
if (tType == null) {
if (other.tType != null)
return false;
}
else if (!tType.equals(other.tType))
return false;
if (typeName == null) {
if (other.typeName != null)
return false;
}
else if (!typeName.equals(other.typeName))
return false;
return true;
}
/**
* @return The type name without all special characters and white spaces
*/
@ -1268,13 +703,4 @@ public class DefaultDataType<T> extends AbstractNamed implements DataType<T> {
return d;
}
private static final boolean eq(Integer i1, Integer i2) {
return (i1 == i2) || (i1 != null && i2 != null && i1.intValue() == i2.intValue());
}
@Override
public final void accept(Context<?> ctx) {
ctx.visit(getQualifiedName());
}
}

File diff suppressed because it is too large Load Diff

View File

@ -12251,7 +12251,7 @@ final class ParserContext {
Field<?> f2;
if ((f2 = t.field(f.getName())) != null) {
if (f1 != null) {
position(f.position);
position(f.position());
throw exception("Ambiguous field identifier");
}
@ -12259,10 +12259,8 @@ final class ParserContext {
}
}
if (f1 != null) {
f.delegate = (AbstractField) f1;
f.sql = null;
}
if (f1 != null)
f.delegate((AbstractField) f1);
else
retain.add(f);
}
@ -12289,7 +12287,7 @@ final class ParserContext {
void unknownField(FieldProxy<?> field) {
if (!scopeClear && !metaLookupsForceIgnore && metaLookups == THROW_ON_FAILURE) {
position(field.position);
position(field.position());
throw exception("Unknown field identifier");
}
}
@ -12335,7 +12333,7 @@ final class ParserContext {
FieldProxy<?> field = lookupFields.get(name.last());
if (field == null)
lookupFields.set(name.last(), field = new FieldProxy<>((AbstractField<Object>) field(name), sql, position));
lookupFields.set(name.last(), field = new FieldProxy<>((AbstractField<Object>) field(name), position));
return field;
}