Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jooq.impl.DefaultBinding Maven / Gradle / Ivy
/*
* 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 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 java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.HOUR_OF_DAY;
import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.NANO_OF_SECOND;
import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
import static java.time.temporal.ChronoField.YEAR;
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.Geography.geography;
import static org.jooq.Geometry.geometry;
// ...
// ...
// ...
// ...
// ...
// ...
import static org.jooq.SQLDialect.CUBRID;
// ...
import static org.jooq.SQLDialect.DEFAULT;
import static org.jooq.SQLDialect.DERBY;
// ...
import static org.jooq.SQLDialect.FIREBIRD;
// ...
import static org.jooq.SQLDialect.H2;
// ...
// ...
// ...
import static org.jooq.SQLDialect.HSQLDB;
// ...
// ...
// ...
import static org.jooq.SQLDialect.MYSQL;
// ...
import static org.jooq.SQLDialect.POSTGRES;
// ...
// ...
// ...
import static org.jooq.SQLDialect.SQLITE;
// ...
// ...
// ...
// ...
import static org.jooq.SQLDialect.YUGABYTEDB;
import static org.jooq.conf.ParamType.INLINED;
import static org.jooq.impl.Convert.convert;
import static org.jooq.impl.Convert.patchIso8601Timestamp;
import static org.jooq.impl.DSL.cast;
import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.log;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DSL.using;
import static org.jooq.impl.DefaultBinding.DefaultDoubleBinding.REQUIRES_LITERAL_CAST;
import static org.jooq.impl.DefaultBinding.DefaultDoubleBinding.infinity;
import static org.jooq.impl.DefaultBinding.DefaultDoubleBinding.nan;
import static org.jooq.impl.DefaultBinding.DefaultEnumTypeBinding.pgEnumValue;
import static org.jooq.impl.DefaultBinding.DefaultEnumTypeBinding.pgRenderEnumCast;
import static org.jooq.impl.DefaultBinding.DefaultJSONBBinding.EMULATE_AS_BLOB;
import static org.jooq.impl.DefaultBinding.DefaultJSONBinding.patchSnowflakeJSON;
import static org.jooq.impl.DefaultBinding.DefaultResultBinding.readMultisetJSON;
import static org.jooq.impl.DefaultBinding.DefaultResultBinding.readMultisetXML;
import static org.jooq.impl.DefaultDataType.getDataType;
import static org.jooq.impl.DefaultExecuteContext.localExecuteContext;
import static org.jooq.impl.DefaultExecuteContext.localTargetConnection;
import static org.jooq.impl.Internal.arrayType;
import static org.jooq.impl.Keywords.K_ARRAY;
import static org.jooq.impl.Keywords.K_AS;
import static org.jooq.impl.Keywords.K_BLOB;
import static org.jooq.impl.Keywords.K_CAST;
import static org.jooq.impl.Keywords.K_DATE;
import static org.jooq.impl.Keywords.K_DATETIME;
import static org.jooq.impl.Keywords.K_DATETIME2;
import static org.jooq.impl.Keywords.K_DATETIMEOFFSET;
import static org.jooq.impl.Keywords.K_FALSE;
import static org.jooq.impl.Keywords.K_FORMAT;
import static org.jooq.impl.Keywords.K_GEOGRAPHY;
import static org.jooq.impl.Keywords.K_GEOMETRY;
import static org.jooq.impl.Keywords.K_HOUR_TO_SECOND;
import static org.jooq.impl.Keywords.K_JSON;
import static org.jooq.impl.Keywords.K_NULL;
import static org.jooq.impl.Keywords.K_TIME;
import static org.jooq.impl.Keywords.K_TIMESTAMP;
import static org.jooq.impl.Keywords.K_TIMESTAMP_WITH_TIME_ZONE;
import static org.jooq.impl.Keywords.K_TIME_WITH_TIME_ZONE;
import static org.jooq.impl.Keywords.K_TRUE;
import static org.jooq.impl.Keywords.K_YEAR_TO_DAY;
import static org.jooq.impl.Keywords.K_YEAR_TO_FRACTION;
import static org.jooq.impl.Names.N_BYTEA;
import static org.jooq.impl.Names.N_PARSE_JSON;
import static org.jooq.impl.Names.N_ST_GEOMFROMTEXT;
import static org.jooq.impl.Names.N_ST_GEOMFROMWKB;
import static org.jooq.impl.R2DBC.isR2dbc;
import static org.jooq.impl.SQLDataType.BIGINT;
import static org.jooq.impl.SQLDataType.BLOB;
import static org.jooq.impl.SQLDataType.BOOLEAN;
import static org.jooq.impl.SQLDataType.CHAR;
import static org.jooq.impl.SQLDataType.DATE;
import static org.jooq.impl.SQLDataType.DECIMAL_INTEGER;
import static org.jooq.impl.SQLDataType.DOUBLE;
import static org.jooq.impl.SQLDataType.INTEGER;
import static org.jooq.impl.SQLDataType.LONGVARCHAR;
import static org.jooq.impl.SQLDataType.NUMERIC;
import static org.jooq.impl.SQLDataType.OTHER;
import static org.jooq.impl.SQLDataType.REAL;
import static org.jooq.impl.SQLDataType.ROWID;
import static org.jooq.impl.SQLDataType.SMALLINT;
import static org.jooq.impl.SQLDataType.TIME;
import static org.jooq.impl.SQLDataType.TIMESTAMP;
import static org.jooq.impl.SQLDataType.VARBINARY;
import static org.jooq.impl.SQLDataType.VARCHAR;
import static org.jooq.impl.Tools.CTX;
import static org.jooq.impl.Tools.apply;
import static org.jooq.impl.Tools.asInt;
import static org.jooq.impl.Tools.attachRecords;
import static org.jooq.impl.Tools.convertBytesToHex;
import static org.jooq.impl.Tools.convertHexToBytes;
import static org.jooq.impl.Tools.emulateMultiset;
import static org.jooq.impl.Tools.enums;
// ...
import static org.jooq.impl.Tools.getMappedUDTName;
import static org.jooq.impl.Tools.getRecordQualifier;
import static org.jooq.impl.Tools.isEmpty;
import static org.jooq.impl.Tools.map;
import static org.jooq.impl.Tools.needsBackslashEscaping;
import static org.jooq.impl.Tools.newRecord;
import static org.jooq.impl.Tools.uncoerce;
import static org.jooq.tools.StringUtils.leftPad;
import static org.jooq.tools.jdbc.JDBCUtils.safeFree;
import static org.jooq.tools.jdbc.JDBCUtils.wasNull;
import static org.jooq.tools.reflect.Reflect.onClass;
import static org.jooq.tools.reflect.Reflect.wrapper;
import static org.jooq.util.postgres.PostgresUtils.toDayToSecond;
import static org.jooq.util.postgres.PostgresUtils.toPGArray;
import static org.jooq.util.postgres.PostgresUtils.toPGArrayString;
import static org.jooq.util.postgres.PostgresUtils.toPGInterval;
import static org.jooq.util.postgres.PostgresUtils.toYearToMonth;
import java.io.Serializable;
import java.io.StringReader;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.JDBCType;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Struct;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Year;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.SignStyle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// ...
// ...
// ...
import org.jooq.Attachable;
import org.jooq.Binding;
import org.jooq.BindingGetResultSetContext;
import org.jooq.BindingGetSQLInputContext;
import org.jooq.BindingGetStatementContext;
import org.jooq.BindingRegisterContext;
import org.jooq.BindingSQLContext;
import org.jooq.BindingScope;
import org.jooq.BindingSetSQLOutputContext;
import org.jooq.BindingSetStatementContext;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.ContextConverter;
import org.jooq.Converter;
import org.jooq.ConverterContext;
import org.jooq.Converters;
import org.jooq.DSLContext;
import org.jooq.DataType;
import org.jooq.EnumType;
import org.jooq.ExecuteScope;
import org.jooq.Field;
import org.jooq.Geography;
import org.jooq.Geometry;
import org.jooq.JSON;
import org.jooq.JSONB;
import org.jooq.Package;
import org.jooq.Param;
// ...
import org.jooq.QualifiedRecord;
import org.jooq.Record;
import org.jooq.RenderContext;
import org.jooq.Result;
import org.jooq.Row;
import org.jooq.RowId;
import org.jooq.SQLDialect;
import org.jooq.Schema;
import org.jooq.Scope;
import org.jooq.Source;
import org.jooq.Spatial;
import org.jooq.UDT;
import org.jooq.UDTField;
import org.jooq.UDTRecord;
import org.jooq.XML;
import org.jooq.conf.NestedCollectionEmulation;
import org.jooq.exception.ControlFlowSignal;
import org.jooq.exception.DataTypeException;
import org.jooq.exception.MappingException;
import org.jooq.exception.SQLDialectNotSupportedException;
import org.jooq.impl.Cast.CastNative;
import org.jooq.impl.R2DBC.R2DBCPreparedStatement;
import org.jooq.impl.Tools.ExtendedDataKey;
import org.jooq.tools.JooqLogger;
import org.jooq.tools.Longs;
import org.jooq.tools.StringUtils;
import org.jooq.tools.jdbc.JDBCUtils;
import org.jooq.tools.jdbc.MockArray;
import org.jooq.tools.jdbc.MockResultSet;
import org.jooq.types.DayToSecond;
import org.jooq.types.UByte;
import org.jooq.types.UInteger;
import org.jooq.types.ULong;
import org.jooq.types.UShort;
import org.jooq.types.YearToMonth;
import org.jooq.types.YearToSecond;
import org.jooq.util.postgres.PostgresUtils;
// ...
// ...
// ...
// ...
// ...
// ...
// ...
// ...
/**
* @author Lukas Eder
*/
public class DefaultBinding implements Binding {
static final JooqLogger log = JooqLogger.getLogger(DefaultBinding.class);
private static final Set REQUIRE_JDBC_DATE_LITERAL = SQLDialect.supportedBy(MYSQL);
// Taken from org.postgresql.PGStatement 9223372036825200000
private static final long PG_DATE_POSITIVE_INFINITY = 9223372036825200000L;
private static final long PG_DATE_NEGATIVE_INFINITY = -9223372036832400000L;
final Binding delegate;
/**
* Get the internal default binding for a {@link Converter}.
*/
public static final Binding binding(Converter converter) {
return binding(DefaultDataType.getDataType(DEFAULT, converter.fromType()), converter);
}
/**
* Get the internal default binding for a {@link DataType}.
*/
public static final Binding binding(DataType dataType) {
return binding(dataType, Converters.identity(dataType.getType()));
}
@SuppressWarnings({ "rawtypes", "unchecked" })
static final Binding binding(DataType dataType, Converter converter) {
Class> type = converter.fromType();
// Concrete types
if (type == BigDecimal.class)
return new DefaultBigDecimalBinding(dataType, converter);
else if (type == BigInteger.class)
return new DefaultBigIntegerBinding(dataType, converter);
else if (type == Blob.class)
return new DefaultBlobBinding(dataType, converter);
else if (type == Boolean.class)
return new DefaultBooleanBinding(dataType, converter);
else if (type == Byte.class || type == byte.class)
return new DefaultByteBinding(dataType, converter);
else if (type == byte[].class)
return new DefaultBytesBinding(dataType, converter);
else if (type == Clob.class)
return new DefaultClobBinding(dataType, converter);
else if (type == Date.class)
return new DefaultDateBinding(dataType, converter);
else if (type == DayToSecond.class)
return new DefaultDayToSecondBinding(dataType, converter);
else if (type == Double.class || type == double.class)
return new DefaultDoubleBinding(dataType, converter);
else if (type == Float.class || type == float.class)
return new DefaultFloatBinding(dataType, converter);
else if (type == Geometry.class)
return new CommercialOnlyBinding(dataType, converter);
else if (type == Geography.class)
return new CommercialOnlyBinding(dataType, converter);
else if (type == Integer.class || type == int.class)
return new DefaultIntegerBinding(dataType, converter);
else if (type == JSON.class)
return new DefaultJSONBinding(dataType, converter);
else if (type == JSONB.class)
return new DefaultJSONBBinding(dataType, converter);
else if (type == XML.class)
return new DefaultXMLBinding(dataType, converter);
else if (type == LocalDate.class)
return (Binding) new DelegatingBinding<>(
(DataType) dataType,
ContextConverter.ofNullable(Date.class, LocalDate.class,
(BiFunction & Serializable) (t, x) -> t.toLocalDate(),
(BiFunction & Serializable) (t, x) -> Date.valueOf(t)
),
(ContextConverter) converter,
c -> new DefaultDateBinding<>(DATE, c)
);
else if (type == LocalDateTime.class)
return (Binding) new DelegatingBinding<>(
(DataType) dataType,
ContextConverter.ofNullable(Timestamp.class, LocalDateTime.class,
(BiFunction & Serializable) (t, x) -> t.toLocalDateTime(),
(BiFunction & Serializable) (t, x) -> Timestamp.valueOf(t)
),
(ContextConverter) converter,
c -> new DefaultTimestampBinding<>(TIMESTAMP, c)
);
else if (type == LocalTime.class)
return (Binding) new DelegatingBinding<>(
(DataType) dataType,
ContextConverter.ofNullable(Time.class, LocalTime.class,
(BiFunction & Serializable) (t, x) -> t.toLocalTime(),
(BiFunction & Serializable) (t, x) -> Time.valueOf(t)
),
(ContextConverter) converter,
c -> new DefaultTimeBinding<>(TIME, c)
);
else if (type == Long.class || type == long.class)
return new DefaultLongBinding(dataType, converter);
else if (type == OffsetDateTime.class)
return new DefaultOffsetDateTimeBinding(dataType, converter);
else if (type == OffsetTime.class)
return new DefaultOffsetTimeBinding(dataType, converter);
else if (type == Instant.class)
return new DefaultInstantBinding(dataType, converter);
else if (type == RowId.class)
return new DefaultRowIdBinding(dataType, converter);
else if (type == Short.class || type == short.class)
return new DefaultShortBinding(dataType, converter);
else if (type == String.class)
if (dataType.isNString())
return new DefaultNStringBinding(dataType, converter);
else
return new DefaultStringBinding(dataType, converter);
else if (type == Time.class)
return new DefaultTimeBinding(dataType, converter);
else if (type == Timestamp.class)
return new DefaultTimestampBinding(dataType, converter);
// [#8022] Support for the "synthetic" timestamp type
else if (type == java.util.Date.class)
return new DefaultTimestampBinding(dataType, Converters.of(TimestampToJavaUtilDateConverter.INSTANCE, (Converter) converter));
else if (type == UByte.class)
return (Binding) new DelegatingBinding<>(
(DataType) dataType,
ContextConverter.ofNullable(Short.class, UByte.class,
(BiFunction & Serializable) (t, x) -> UByte.valueOf(t),
(BiFunction & Serializable) (t, x) -> t.shortValue()
),
(ContextConverter) converter,
c -> new DefaultShortBinding<>(SMALLINT, c)
);
else if (type == UInteger.class)
return (Binding) new DelegatingBinding<>(
(DataType) dataType,
ContextConverter.ofNullable(Long.class, UInteger.class,
(BiFunction & Serializable) (t, x) -> UInteger.valueOf(t),
(BiFunction & Serializable) (t, x) -> t.longValue()
),
(ContextConverter) converter,
c -> new DefaultLongBinding<>(BIGINT, c)
);
else if (type == ULong.class)
return (Binding) new DelegatingBinding<>(
(DataType) dataType,
ContextConverter.ofNullable(BigInteger.class, ULong.class,
(BiFunction & Serializable) (t, x) -> ULong.valueOf(t),
(BiFunction & Serializable) (t, x) -> t.toBigInteger()
),
(ContextConverter) converter,
c -> new DefaultBigIntegerBinding<>(DECIMAL_INTEGER, c)
);
else if (type == UShort.class)
return (Binding) new DelegatingBinding<>(
(DataType) dataType,
ContextConverter.ofNullable(Integer.class, UShort.class,
(BiFunction & Serializable) (t, x) -> UShort.valueOf(t),
(BiFunction & Serializable) (t, x) -> t.intValue()
),
(ContextConverter) converter,
c -> new DefaultIntegerBinding<>(INTEGER, c)
);
else if (type == UUID.class)
return new DefaultUUIDBinding(dataType, converter);
else if (type == YearToSecond.class)
return new DefaultYearToSecondBinding(dataType, converter);
else if (type == YearToMonth.class)
return new DefaultYearToMonthBinding(dataType, converter);
else if (type == Year.class)
return new DefaultYearBinding(dataType, converter);
// Subtypes of array types etc.
// The type byte[] is handled earlier. byte[][] can be handled here
else if (type.isArray())
return new DefaultArrayBinding(dataType, converter);
else if (EnumType.class.isAssignableFrom(type))
return new DefaultEnumTypeBinding(dataType, converter);
else if (Record.class.isAssignableFrom(type))
return new DefaultRecordBinding(dataType, converter);
else if (Result.class.isAssignableFrom(type))
return new DefaultResultBinding(dataType, converter);
// Undefined types
else
return new DefaultOtherBinding(dataType, converter);
}
/**
* @deprecated - 3.11 - [#6631] - Use {@link #binding(Converter)} instead.
*/
@Deprecated(forRemoval = true)
public DefaultBinding(Converter converter) {
this.delegate = binding(converter);
}
public DefaultBinding(Binding delegate) {
this.delegate = delegate;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
static final Binding newBinding(final Converter converter, final DataType dataType, final Binding binding) {
final Binding theBinding;
if (converter == null && binding == null) {
theBinding = (Binding) dataType.getBinding();
}
else if (converter == null) {
theBinding = (Binding) binding;
}
else if (binding == null) {
theBinding = binding(dataType, (ContextConverter) scoped(converter));
}
else {
theBinding = new Binding() {
final ContextConverter theConverter = Converters.of(binding.converter(), converter);
@Override
public Converter converter() {
return theConverter;
}
@Override
public void sql(BindingSQLContext ctx) throws SQLException {
binding.sql(ctx.convert(converter));
}
@Override
public void register(BindingRegisterContext ctx) throws SQLException {
binding.register(ctx.convert(converter));
}
@Override
public void set(BindingSetStatementContext ctx) throws SQLException {
binding.set(ctx.convert(converter));
}
@Override
public void set(BindingSetSQLOutputContext ctx) throws SQLException {
binding.set(ctx.convert(converter));
}
@Override
public void get(BindingGetResultSetContext ctx) throws SQLException {
binding.get(ctx.convert(converter));
}
@Override
public void get(BindingGetStatementContext ctx) throws SQLException {
binding.get(ctx.convert(converter));
}
@Override
public void get(BindingGetSQLInputContext ctx) throws SQLException {
binding.get(ctx.convert(converter));
}
};
}
return theBinding;
}
static final Map> typeMap(Class> type, Scope scope) {
return typeMap(type, scope, new HashMap<>());
}
@SuppressWarnings("unchecked")
static final Map> typeMap(Class> type, Scope scope, Map> result) {
try {
if (QualifiedRecord.class.isAssignableFrom(type)) {
Class> t = (Class>) type;
result.put(getMappedUDTName(scope, t), t);
for (Field> field : getRecordQualifier(t).fields())
typeMap(field.getType(), scope, result);
}
}
catch (Exception e) {
throw new MappingException("Error while collecting type map", e);
}
return result;
}
private static final long parse(Class extends java.util.Date> type, String date) throws SQLException {
// Try reading a plain number first
Long number = Longs.tryParse(date);
if (number != null)
return number;
// If that fails, try reading a formatted date
// [#7325] In SQLite dates could be stored in both ISO standard formats:
// With T (default standard), or without T (optional standard, JDBC standard)
date = StringUtils.replace(date, "T", " ");
if (type == Timestamp.class)
return Timestamp.valueOf(date).getTime();
// Dates may come with " 00:00:00". This is safely trimming time information
else if (type == Date.class)
return Date.valueOf(date.split(" ")[0]).getTime();
else if (type == Time.class)
return Time.valueOf(date).getTime();
throw new SQLException("Could not parse date " + date);
}
// -----------------------------------------------------------------------------------------------------------------
// Implementation of Binding API for backwards compatibility
// -----------------------------------------------------------------------------------------------------------------
@Override
public Converter converter() {
return delegate.converter();
}
@Override
public void sql(BindingSQLContext ctx) throws SQLException {
delegate.sql(ctx);
}
@Override
public void register(BindingRegisterContext ctx) throws SQLException {
delegate.register(ctx);
}
@Override
public void set(BindingSetStatementContext ctx) throws SQLException {
delegate.set(ctx);
}
@Override
public void set(BindingSetSQLOutputContext ctx) throws SQLException {
delegate.set(ctx);
}
@Override
public void get(BindingGetResultSetContext ctx) throws SQLException {
delegate.get(ctx);
}
@Override
public void get(BindingGetStatementContext ctx) throws SQLException {
delegate.get(ctx);
}
@Override
public void get(BindingGetSQLInputContext ctx) throws SQLException {
delegate.get(ctx);
}
// -----------------------------------------------------------------------------------------------------------------
// Object API
// -----------------------------------------------------------------------------------------------------------------
@Override
public String toString() {
return delegate.toString();
}
// -----------------------------------------------------------------------------------------------------------------
// Type-specific subclasses API
// -----------------------------------------------------------------------------------------------------------------
/**
* An internal binding class for default data type binding implementations.
*
* This base class can be safely assumed to not leak into custom bindings.
*/
abstract static class InternalBinding implements org.jooq.Binding {
static final Set NEEDS_PRECISION_SCALE_ON_BIGDECIMAL = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, H2, HSQLDB);
static final Set REQUIRES_JSON_CAST = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB);
static final Set NO_SUPPORT_ENUM_CAST = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB);
static final Set NO_SUPPORT_NVARCHAR = SQLDialect.supportedBy(DERBY, FIREBIRD, POSTGRES, SQLITE, YUGABYTEDB);
final DataType dataType;
final ContextConverter converter;
final boolean attachable;
InternalBinding(DataType dataType, Converter converter) {
this.dataType = dataType;
this.converter = ContextConverter.scoped(converter);
// [#11099] Caching this per binding seems to have a considerable performance effect.
// We must be careful to short circuit instanceof Attachable checks only if we *know*
// the type cannot be attachable, i.e. for final declared types only (such as String)
this.attachable = Attachable.class.isAssignableFrom(converter.toType())
|| !Modifier.isFinal(converter.toType().getModifiers());
}
@Override
public final ContextConverter converter() {
return converter;
}
static final Configuration originalConfiguration(Scope ctx) {
if (ctx instanceof ExecuteScope es) {
if (es.executeContext() instanceof DefaultExecuteContext dec) {
return dec.originalConfiguration();
}
}
return ctx.configuration();
}
static final DSLContext originalScope(Scope ctx) {
if (ctx instanceof ExecuteScope es) {
if (es.executeContext() instanceof DefaultExecuteContext dec) {
return dec.originalConfiguration().dsl();
}
}
return ctx.dsl();
}
private final boolean shouldCast(BindingSQLContext ctx, T converted) {
// [#10662] A few dialects require casts for NULL literals
if (ctx.render().paramType() == INLINED) {
if (converted == null) {
switch (ctx.family()) {
case DERBY:
return true;
}
}
}
// Many dialects require casts for bind values
else {
// Generated enums should not be cast...
if (!(converted instanceof EnumType)) {
switch (ctx.family()) {
// These dialects can hardly detect the type of a bound constant.
case DERBY:
case FIREBIRD:
// These dialects have some trouble, when they mostly get it right.
case H2:
case HSQLDB:
case IGNITE:
// [#1261] There are only a few corner-cases, where this is
// really needed. Check back on related CUBRID bugs
case CUBRID:
// [#1029] Postgres and [#632] Sybase need explicit casting
// in very rare cases. BigQuery doesn't support NULL BOOLEAN or INT64 bind values
case POSTGRES:
case YUGABYTEDB: {
return true;
}
}
}
}
// [#566] JDBC doesn't explicitly support interval data types. To be on
// the safe side, always cast these types in those dialects that support
// them
if (dataType.isInterval()) {
switch (ctx.family()) {
case H2:
case HSQLDB:
case POSTGRES:
case YUGABYTEDB:
return true;
}
}
// [#7242] [#13252] Other vendor specific types also need a lot of casting
if (dataType.isJSON() || dataType.isXML()) {
switch (ctx.family()) {
case POSTGRES:
case YUGABYTEDB:
return true;
}
}
if (dataType.getType() == UUID.class) {
switch (ctx.family()) {
case HSQLDB:
case POSTGRES:
case YUGABYTEDB:
return true;
}
}
// [#2902] The xerial driver binds BigDecimal as String, which may produce
// wrong results
if (dataType.isDecimal()) {
switch (ctx.family()) {
case SQLITE:
return true;
}
}
return false;
}
/**
* Render the bind variable including a cast, if necessary
*/
private final void sqlCast(BindingSQLContext ctx, T converted) throws SQLException {
DataType sqlDataType = dataType.getSQLDataType();
SQLDialect family = ctx.family();
// [#822] Some RDBMS need precision / scale information on BigDecimals
if (converted != null && dataType.getType() == BigDecimal.class && NEEDS_PRECISION_SCALE_ON_BIGDECIMAL.contains(ctx.dialect())) {
// Add precision / scale on BigDecimals
int scale = ((BigDecimal) converted).scale();
int precision = ((BigDecimal) converted).precision();
// [#5323] BigDecimal precision is always 1 for BigDecimals smaller than 1.0
if (scale >= precision)
precision = scale + 1;
sqlCast(ctx, converted, dataType, null, precision, scale);
}
// [#7905] The ROWID type cannot be cast to
else if (ROWID == sqlDataType)
sql(ctx, converted);
// [#1028] Most databases don't know an OTHER type (except H2, HSQLDB).
else if (OTHER == sqlDataType)
// If the bind value is set, it can be used to derive the cast type
if (converted != null)
sqlCast(ctx, converted, DefaultDataType.getDataType(family, converted.getClass()), null, null, null);
// [#11511] Specifically when in a parser context, we must not
// blindly cast bind variables
else
ctx.render().sql(ctx.variable());
// [#1029] Postgres generally doesn't need the casting. Only in the
// above case where the type is OTHER
// [#1125] Also with temporal data types, casting is needed some times
// [#4338] ... specifically when using JSR-310 types
// [#1130] TODO type can be null for ARRAY types, etc.
// [#7351] UUID data types need to be cast too
// [#7242] JSON(B) data types need to be cast too
// [#13252] XML data types need to be cast too
else if (REQUIRES_JSON_CAST.contains(ctx.dialect()) &&
(sqlDataType == null ||
(!sqlDataType.isTemporal()
&& sqlDataType != SQLDataType.UUID
&& !sqlDataType.isXML()
&& !sqlDataType.isJSON())))
sql(ctx, converted);
// [#7379] Most databases cannot cast a bind variable to an enum type
else if (!NO_SUPPORT_ENUM_CAST.contains(ctx.dialect()) && dataType.isEnum())
sqlCast(
ctx,
converted,
Tools.emulateEnumType((DataType) dataType),
dataType.lengthDefined() ? dataType.length() : null,
dataType.precisionDefined() ? dataType.precision() : null,
dataType.scaleDefined() ? dataType.scale() : null
);
// In all other cases, the bind variable can be cast normally
else
sqlCast(
ctx,
converted,
dataType,
dataType.lengthDefined() ? dataType.length() : null,
dataType.precisionDefined() ? dataType.precision() : null,
dataType.scaleDefined() ? dataType.scale() : null
);
}
private static final int getValueLength(String string) {
if (string == null)
return 1;
else {
int length = string.length();
// If non 7-bit ASCII characters are present, multiply the length by
// 4 to be sure that even UTF-32 collations will fit. But don't use
// larger numbers than Derby's upper limit 32672
for (int i = 0; i < length; i++)
if (string.charAt(i) > 127)
return Math.min(32672, 4 * length);
return Math.min(32672, length);
}
}
private final void sqlCast(BindingSQLContext ctx, T converted, DataType> t, Integer length, Integer precision, Integer scale) throws SQLException {
switch (ctx.family()) {
default:
sqlCast0(ctx, converted, t, length, precision, scale);
break;
}
}
private final void sqlCast0(BindingSQLContext ctx, T converted, DataType> t, Integer length, Integer precision, Integer scale) throws SQLException {
ctx.render().visit(K_CAST).sql('(');
sql(ctx, converted);
ctx.render().sql(' ').visit(K_AS).sql(' ')
.sql(DefaultDataType.set(t, length, precision, scale).getCastTypeName(ctx.configuration()))
.sql(')');
}
@Override
public final void sql(BindingSQLContext ctx) throws SQLException {
T converted = converter().to(ctx.value(), ctx.converterContext());
// Casting can be enforced or prevented
switch (ctx.render().castMode()) {
case NEVER:
sql(ctx, converted);
return;
case ALWAYS:
sqlCast(ctx, converted);
return;
}
// See if we "should" cast, to stay on the safe side
if (shouldCast(ctx, converted))
sqlCast(ctx, converted);
// Most RDBMS can infer types for bind values
else
sql(ctx, converted);
}
private final void sql(BindingSQLContext ctx, T value) throws SQLException {
if (ctx.render().paramType() == INLINED)
if (value == null)
ctx.render().visit(K_NULL);
else
sqlInline0(ctx, value);
else
sqlBind0(ctx, value);
}
/**
* Escape a string literal by replacing '
by ''
, and possibly also backslashes.
*/
static final String escape(Object val, Context> ctx) {
String result = val.toString();
if (needsBackslashEscaping(ctx.configuration()))
result = StringUtils.replace(result, "\\", "\\\\");
return StringUtils.replace(result, "'", "''");
}
@Override
public final void register(BindingRegisterContext ctx) throws SQLException {
if (!FALSE.equals(ctx.settings().isExecuteLogging()))
if (log.isTraceEnabled())
log.trace("Registering variable " + ctx.index(), "" + dataType);
register0(ctx);
}
@Override
public final void set(BindingSetStatementContext ctx) throws SQLException {
T value = converter().to(ctx.value(), ctx.converterContext());
if (!FALSE.equals(ctx.settings().isExecuteLogging()))
if (log.isTraceEnabled())
if (value != null && value.getClass().isArray() && value.getClass() != byte[].class)
log.trace("Binding variable " + ctx.index(), Arrays.asList((Object[]) value) + " (" + dataType + ")");
else
log.trace("Binding variable " + ctx.index(), value + " (" + dataType + ")");
if (value == null)
setNull0(ctx);
else
set0(ctx, value);
}
@Override
public final void set(BindingSetSQLOutputContext ctx) throws SQLException {
T value = converter().to(ctx.value(), ctx.converterContext());
if (value == null)
ctx.output().writeObject(null);
else
set0(ctx, value);
}
@Override
public final void get(BindingGetResultSetContext ctx) throws SQLException {
U value = converter().from(get0(ctx), ctx.converterContext());
if (attachable)
value = attach(value, originalConfiguration(ctx));
ctx.value(value);
}
@Override
public final void get(BindingGetStatementContext ctx) throws SQLException {
U value = converter().from(get0(ctx), ctx.converterContext());
if (attachable)
value = attach(value, originalConfiguration(ctx));
ctx.value(value);
}
@Override
public final void get(BindingGetSQLInputContext ctx) throws SQLException {
U value = converter().from(get0(ctx), ctx.converterContext());
if (attachable)
value = attach(value, originalConfiguration(ctx));
ctx.value(value);
}
private static final U attach(U value, Configuration configuration) {
// [#4372] Attach records if possible / required
if (value instanceof Attachable && attachRecords(configuration))
((Attachable) value).attach(configuration);
return value;
}
/* non-final */ void setNull0(BindingSetStatementContext ctx) throws SQLException {
if (ctx.statement() instanceof R2DBCPreparedStatement s)
s.setNull(ctx.index(), dataType);
else
ctx.statement().setNull(ctx.index(), sqltype(ctx.statement(), ctx.configuration()));
}
/* non-final */ void register0(BindingRegisterContext ctx) throws SQLException {
ctx.statement().registerOutParameter(ctx.index(), sqltype(ctx.statement(), ctx.configuration()));
}
@SuppressWarnings("unused")
/* non-final */ void sqlInline0(BindingSQLContext ctx, T value) throws SQLException {
sqlInline1(ctx, value);
}
@SuppressWarnings("unused")
final void sqlInline1(BindingSQLContext ctx, Object value) throws SQLException {
// Known fall-through types:
// - Blob, Clob
// - String
// - UUID
ctx.render().sql('\'')
.sql(escape(value, ctx.render()), true)
.sql('\'');
}
@SuppressWarnings("unused")
/* non-final */ void sqlBind0(BindingSQLContext ctx, T value) throws SQLException {
ctx.render().sql(ctx.variable());
}
// abstract void register0(BindingRegisterContext ctx) throws SQLException;
abstract void set0(BindingSetStatementContext ctx, T value) throws SQLException;
abstract void set0(BindingSetSQLOutputContext ctx, T value) throws SQLException;
abstract T get0(BindingGetResultSetContext ctx) throws SQLException;
abstract T get0(BindingGetStatementContext ctx) throws SQLException;
abstract T get0(BindingGetSQLInputContext ctx) throws SQLException;
abstract int sqltype(Statement statement, Configuration configuration) throws SQLException;
// -----------------------------------------------------------------------------------------------------------------
// Object API
// -----------------------------------------------------------------------------------------------------------------
@Override
public String toString() {
return "AbstractBinding [type=" + dataType + ", converter=" + converter + "]";
}
}
static final class DelegatingBinding extends InternalBinding {
private final ContextConverter delegatingConverter;
private final InternalBinding delegatingBinding;
DelegatingBinding(
DataType originalDataType,
ContextConverter delegatingConverter,
ContextConverter originalConverter,
Function super ContextConverter, ? extends InternalBinding> f
) {
super(originalDataType, originalConverter);
this.delegatingConverter = delegatingConverter;
this.delegatingBinding = f.apply(Converters.of(delegatingConverter, originalConverter));
}
@Override
final void sqlInline0(BindingSQLContext ctx, X value) throws SQLException {
delegatingBinding.sqlInline0(ctx, delegatingConverter.to(value, ctx.converterContext()));
}
@Override
final void sqlBind0(BindingSQLContext ctx, X value) throws SQLException {
delegatingBinding.sqlBind0(ctx, delegatingConverter.to(value, ctx.converterContext()));
}
@Override
final void set0(BindingSetStatementContext ctx, X value) throws SQLException {
delegatingBinding.set0(ctx, delegatingConverter.to(value, ctx.converterContext()));
}
@Override
final void setNull0(BindingSetStatementContext ctx) throws SQLException {
delegatingBinding.setNull0(ctx);
}
@Override
final void set0(BindingSetSQLOutputContext ctx, X value) throws SQLException {
delegatingBinding.set0(ctx, delegatingConverter.to(value, ctx.converterContext()));
}
@Override
final X get0(BindingGetResultSetContext ctx) throws SQLException {
return delegatingConverter.from(delegatingBinding.get0(ctx), ctx.converterContext());
}
@Override
final X get0(BindingGetStatementContext ctx) throws SQLException {
return delegatingConverter.from(delegatingBinding.get0(ctx), ctx.converterContext());
}
@Override
final X get0(BindingGetSQLInputContext ctx) throws SQLException {
return delegatingConverter.from(delegatingBinding.get0(ctx), ctx.converterContext());
}
@Override
final int sqltype(Statement statement, Configuration configuration) throws SQLException {
return delegatingBinding.sqltype(statement, configuration);
}
}
static final class DefaultArrayBinding extends InternalBinding {
private static final Set REQUIRES_ARRAY_CAST = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB);
DefaultArrayBinding(DataType dataType, Converter converter) {
super(dataType, converter);
}
@SuppressWarnings("unchecked")
@Override
final void sqlInline0(BindingSQLContext ctx, Object[] value) throws SQLException {
String separator = "";
if (REQUIRES_ARRAY_CAST.contains(ctx.dialect())) {
// [#8933] In some cases, we cannot derive the cast type from
// array type directly
DataType> arrayType =
dataType.getType() == Object[].class
? getDataType(ctx.dialect(), deriveArrayTypeFromComponentType(value))
: dataType;
ctx.render().visit(cast(inline(PostgresUtils.toPGArrayString(value)), arrayType));
}
// By default, render HSQLDB syntax
else {
boolean squareBrackets = true;
ctx.render().visit(K_ARRAY);
ctx.render().sql(squareBrackets ? '[' : '(');
for (Object o : value) {
ctx.render().sql(separator);
binding((DataType) dataType.getArrayComponentDataType()).sql(new DefaultBindingSQLContext<>(ctx.configuration(), ctx.data(), ctx.render(), o));
separator = ", ";
}
ctx.render().sql(squareBrackets ? ']' : ')');
}
}
private final Class extends Object[]> deriveArrayTypeFromComponentType(Object[] value) {
for (Object o : value)
if (o != null)
return arrayType(o.getClass());
// PostgreSQL often defaults to using varchar as well, so we can
// mimick this behaviour (without documenting it).
return String[].class;
}
@Override
final void sqlBind0(BindingSQLContext ctx, Object[] value) throws SQLException {
Cast.renderCastIf(ctx.render(),
c -> super.sqlBind0(ctx, value),
c -> {
// Postgres needs explicit casting for enum (array) types
if (EnumType.class.isAssignableFrom(dataType.getType().getComponentType()))
pgRenderEnumCast(ctx.render(), dataType.getType(), pgEnumValue(dataType.getType()));
// ... and also for other array types
else
ctx.render().sql(dataType.getCastTypeName(ctx.render().configuration()));
},
// In Postgres, some additional casting must be done in some cases...
() -> REQUIRES_ARRAY_CAST.contains(ctx.family())
);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
final void set0(BindingSetStatementContext ctx, Object[] value) throws SQLException {
switch (ctx.family()) {
case POSTGRES:
case YUGABYTEDB: {
ctx.statement().setString(ctx.index(), toPGArrayString(value));
break;
}
case HSQLDB: {
Object[] a = value;
Class> t = dataType.getType();
// [#2325] [#5823] Cannot bind UUID[] type in HSQLDB.
// See also: https://sourceforge.net/p/hsqldb/bugs/1466
if (t == UUID[].class) {
a = Convert.convertArray(a, byte[][].class);
t = byte[][].class;
}
// [#16585] Another HSQLDB bug regarding LocalTime:
// See also: https://sourceforge.net/p/hsqldb/bugs/1702/
else if (t == LocalTime[].class) {
a = (Object[]) Convert.convertArray(a, Time[].class);
t = Time[].class;
}
ctx.statement().setArray(ctx.index(), new MockArray(ctx.family(), a, t));
break;
}
case H2: {
ctx.statement().setObject(ctx.index(), value);
break;
}
default:
throw new SQLDialectNotSupportedException("Cannot bind ARRAY types in dialect " + ctx.family());
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
final void set0(BindingSetSQLOutputContext ctx, Object[] value) throws SQLException {
ctx.output().writeArray(new MockArray(ctx.family(), value, dataType.getType()));
}
@Override
final Object[] get0(BindingGetResultSetContext ctx) throws SQLException {
switch (ctx.family()) {
case POSTGRES:
case YUGABYTEDB:
return pgGetArray(ctx, ctx.resultSet(), dataType, ctx.index());
case HSQLDB: {
// [#13965] Some HSQLDB versions have trouble reading NULL values as arrays
// See also: https://sourceforge.net/p/hsqldb/bugs/1662/
if (ctx.resultSet().getObject(ctx.index()) == null)
return null;
// However, due to a historic HSQLDB bug, we better not rely on rs.getObject() here:
// See https://sourceforge.net/p/hsqldb/bugs/1102/
else
return convertArray(ctx.resultSet().getArray(ctx.index()), dataType.getType());
}
default:
return convertArray(ctx.resultSet().getArray(ctx.index()), dataType.getType());
}
}
@Override
final Object[] get0(BindingGetStatementContext ctx) throws SQLException {
return convertArray(ctx.statement().getObject(ctx.index()), dataType.getType());
}
@Override
final Object[] get0(BindingGetSQLInputContext ctx) throws SQLException {
Array array = ctx.input().readArray();
return array == null ? null : (Object[]) array.getArray();
}
@Override
final int sqltype(Statement statement, Configuration configuration) {
return Types.ARRAY;
}
/**
* Workarounds for the unimplemented Postgres JDBC driver features
*/
@SuppressWarnings("unchecked")
private static final T pgGetArray(ExecuteScope ctx, ResultSet rs, DataType dataType, int index) throws SQLException {
// Get the JDBC Array and check for null. If null, that's OK
Array array = null;
try {
array = rs.getArray(index);
if (array == null)
return null;
DataType> cdt = dataType.getArrayComponentDataType();
// Try fetching a Java Object[]. That's gonna work for non-UDT types
try {
// [#5586] [#5613] TODO: Improve PostgreSQL array deserialisation.
// [#5633] Special treatment for byte[][] types.
// [#14010] UDT arrays should skip the Convert utility
// [#16581] OffsetTime[] is returned as Time[] by Array::getArray
if (cdt.isBinary() || cdt.isUDT() || cdt.getType() == OffsetTime.class)
throw new ControlFlowSignal("GOTO the next array deserialisation strategy");
else
return (T) convertArray(array, (Class extends Object[]>) dataType.getType());
}
// This might be a UDT (not implemented exception...)
catch (Exception e) {
List result = new ArrayList<>();
// Try fetching the array as a JDBC ResultSet
try (ResultSet arrayRs = array.getResultSet()) {
Binding binding = binding((DataType) dataType.getArrayComponentDataType());
DefaultBindingGetResultSetContext out = new DefaultBindingGetResultSetContext<>(ctx.executeContext(), arrayRs, 2);
while (arrayRs.next()) {
binding.get(out);
result.add(out.value());
}
}
// That might fail too, then we don't know any further...
catch (Exception fatal) {
String string = null;
try {
string = rs.getString(index);
}
catch (SQLException ignore) {}
log.error("Cannot parse array", string, fatal);
return null;
}
return (T) convertArray(result.toArray(), (Class extends Object[]>) dataType.getType());
}
}
finally {
safeFree(array);
}
}
private static final Object[] convertArray(Object array, Class extends Object[]> type) throws SQLException {
if (array instanceof Object[])
return Convert.convert(array, type);
else if (array instanceof Array a)
return convertArray(a, type);
return null;
}
private static final Object[] convertArray(Array array, Class extends Object[]> type) throws SQLException {
if (array != null)
return Convert.convert(array.getArray(), type);
return null;
}
}
static final class DefaultBigDecimalBinding extends InternalBinding {
private static final Set BIND_AS_STRING = SQLDialect.supportedBy(SQLITE);
DefaultBigDecimalBinding(DataType dataType, Converter converter) {
super(dataType, converter);
}
@Override
final void sqlInline0(BindingSQLContext ctx, BigDecimal value) {
switch (ctx.family()) {
default:
ctx.render().sql(value.toString());
break;
}
}
@Override
final void setNull0(BindingSetStatementContext ctx) throws SQLException {
super.setNull0(ctx);
}
@Override
final void set0(BindingSetStatementContext ctx, BigDecimal value) throws SQLException {
if (BIND_AS_STRING.contains(ctx.dialect()))
ctx.statement().setString(ctx.index(), value.toString());
else
ctx.statement().setBigDecimal(ctx.index(), value);
}
@Override
final void set0(BindingSetSQLOutputContext ctx, BigDecimal value) throws SQLException {
ctx.output().writeBigDecimal(value);
}
@Override
final BigDecimal get0(BindingGetResultSetContext ctx) throws SQLException {
// The SQLite JDBC driver doesn't support BigDecimals
if (ctx.family() == SQLDialect.SQLITE)
return Convert.convert(ctx.resultSet().getString(ctx.index()), BigDecimal.class);
else
return ctx.resultSet().getBigDecimal(ctx.index());
}
@Override
final BigDecimal get0(BindingGetStatementContext ctx) throws SQLException {
return ctx.statement().getBigDecimal(ctx.index());
}
@Override
final BigDecimal get0(BindingGetSQLInputContext ctx) throws SQLException {
return ctx.input().readBigDecimal();
}
@Override
final int sqltype(Statement statement, Configuration configuration) {
return Types.DECIMAL;
}
}
static final class DefaultBigIntegerBinding extends InternalBinding {
private static final Set BIND_AS_STRING = SQLDialect.supportedBy(SQLITE);
DefaultBigIntegerBinding(DataType dataType, Converter converter) {
super(dataType, converter);
}
@Override
final void sqlInline0(BindingSQLContext ctx, BigInteger value) {
ctx.render().sql(value.toString());
}
@Override
final void setNull0(BindingSetStatementContext ctx) throws SQLException {
super.setNull0(ctx);
}
@Override
final void set0(BindingSetStatementContext ctx, BigInteger value) throws SQLException {
if (BIND_AS_STRING.contains(ctx.dialect()))
ctx.statement().setString(ctx.index(), value.toString());
else
ctx.statement().setBigDecimal(ctx.index(), new BigDecimal(value));
}
@Override
final void set0(BindingSetSQLOutputContext ctx, BigInteger value) throws SQLException {
ctx.output().writeBigDecimal(new BigDecimal(value));
}
@Override
final BigInteger get0(BindingGetResultSetContext ctx) throws SQLException {
// The SQLite JDBC driver doesn't support BigDecimals
if (ctx.family() == SQLDialect.SQLITE)
return Convert.convert(ctx.resultSet().getString(ctx.index()), BigInteger.class);
BigDecimal b = ctx.resultSet().getBigDecimal(ctx.index());
return (b == null ? null : b.toBigInteger());
}
@Override
final BigInteger get0(BindingGetStatementContext ctx) throws SQLException {
BigDecimal d = ctx.statement().getBigDecimal(ctx.index());
return (d == null ? null : d.toBigInteger());
}
@Override
final BigInteger get0(BindingGetSQLInputContext ctx) throws SQLException {
BigDecimal d = ctx.input().readBigDecimal();
return (d == null ? null : d.toBigInteger());
}
@Override
final int sqltype(Statement statement, Configuration configuration) {
return Types.DECIMAL;
}
}
static final class DefaultBlobBinding extends InternalBinding {
DefaultBlobBinding(DataType dataType, Converter converter) {
super(dataType, converter);
}
@Override
final void setNull0(BindingSetStatementContext ctx) throws SQLException {
super.setNull0(ctx);
}
@Override
final void set0(BindingSetStatementContext ctx, Blob value) throws SQLException {
ctx.statement().setBlob(ctx.index(), value);
}
@Override
final void set0(BindingSetSQLOutputContext ctx, Blob value) throws SQLException {
ctx.output().writeBlob(value);
}
@Override
final Blob get0(BindingGetResultSetContext ctx) throws SQLException {
return ctx.resultSet().getBlob(ctx.index());
}
@Override
final Blob get0(BindingGetStatementContext ctx) throws SQLException {
return ctx.statement().getBlob(ctx.index());
}
@Override
final Blob get0(BindingGetSQLInputContext ctx) throws SQLException {
return ctx.input().readBlob();
}
@Override
final int sqltype(Statement statement, Configuration configuration) {
switch (configuration.family()) {
// [#1225] [#1227] TODO Put this logic into DataType
// Some dialects have trouble binding binary data as BLOB
// Same logic in DefaultBytesBinding
case POSTGRES:
case YUGABYTEDB:
return Types.BINARY;
default:
return Types.BLOB;
}
}
}
static final class DefaultBooleanBinding extends InternalBinding {
private static final Set BIND_AS_1_0 = SQLDialect.supportedBy(FIREBIRD, SQLITE);
DefaultBooleanBinding(DataType dataType, Converter converter) {
super(dataType, converter);
}
@Override
final void sqlInline0(BindingSQLContext ctx, Boolean value) {
// [#1153] Some dialects don't support boolean literals TRUE and FALSE
if (BIND_AS_1_0.contains(ctx.dialect()))
ctx.render().sql(value ? "1" : "0");
else
ctx.render().visit(value ? K_TRUE : K_FALSE);
}
@Override
final void register0(BindingRegisterContext ctx) throws SQLException {
super.register0(ctx);
}
@Override
final void set0(BindingSetStatementContext ctx, Boolean value) throws SQLException {
switch (ctx.family()) {
default:
ctx.statement().setBoolean(ctx.index(), value);
break;
}
}
@Override
final void setNull0(BindingSetStatementContext ctx) throws SQLException {
super.setNull0(ctx);
}
@Override
final void set0(BindingSetSQLOutputContext ctx, Boolean value) throws SQLException {
ctx.output().writeBoolean(value);
}
@Override
final Boolean get0(BindingGetResultSetContext ctx) throws SQLException {
return wasNull(ctx.resultSet(), ctx.resultSet().getBoolean(ctx.index()));
}
@Override
final Boolean get0(BindingGetStatementContext ctx) throws SQLException {
return wasNull(ctx.statement(), ctx.statement().getBoolean(ctx.index()));
}
@Override
final Boolean get0(BindingGetSQLInputContext ctx) throws SQLException {
return wasNull(ctx.input(), ctx.input().readBoolean());
}
@Override
final int sqltype(Statement statement, Configuration configuration) throws SQLException {
switch (configuration.family()) {
default:
return Types.BOOLEAN;
}
}
}
static final class DefaultByteBinding extends InternalBinding {
DefaultByteBinding(DataType dataType, Converter converter) {
super(dataType, converter);
}
@Override
final void setNull0(BindingSetStatementContext ctx) throws SQLException {
super.setNull0(ctx);
}
@Override
final void sqlInline0(BindingSQLContext ctx, Byte value) {
ctx.render().sql(value);
}
@Override
final void set0(BindingSetStatementContext ctx, Byte value) throws SQLException {
ctx.statement().setByte(ctx.index(), value);
}
@Override
final void set0(BindingSetSQLOutputContext ctx, Byte value) throws SQLException {
ctx.output().writeByte(value);
}
@Override
final Byte get0(BindingGetResultSetContext ctx) throws SQLException {
return wasNull(ctx.resultSet(), ctx.resultSet().getByte(ctx.index()));
}
@Override
final Byte get0(BindingGetStatementContext ctx) throws SQLException {
return wasNull(ctx.statement(), ctx.statement().getByte(ctx.index()));
}
@Override
final Byte get0(BindingGetSQLInputContext ctx) throws SQLException {
return wasNull(ctx.input(), ctx.input().readByte());
}
@Override
final int sqltype(Statement statement, Configuration configuration) {
return Types.TINYINT;
}
}
static final class DefaultBytesBinding extends InternalBinding {
// [#12956] Starting from H2 2.0, we can't use byte[] for BLOB anymore, if they're
// larger than 1MB
private final BlobBinding blobs;
DefaultBytesBinding(DataType dataType, Converter converter) {
super(dataType, converter);
this.blobs = new BlobBinding();
}
@Override
final void setNull0(BindingSetStatementContext ctx) throws SQLException {
switch (ctx.family()) {
default:
super.setNull0(ctx);
break;
}
}
@Override
final void sqlInline0(BindingSQLContext ctx, byte[] value) {
// [#1154] Binary data cannot always be inlined
switch (ctx.family()) {
case H2:
case HSQLDB:
case MARIADB:
case MYSQL:
case SQLITE:
ctx.render()
.sql("X'")
.sql(convertBytesToHex(value))
.sql('\'');
break;
case DERBY:
ctx.render()
.visit(K_CAST)
.sql("(X'")
.sql(convertBytesToHex(value))
.sql("' ")
.visit(K_AS)
.sql(' ')
.visit(BLOB)
.sql(')');
break;
case POSTGRES:
case YUGABYTEDB:
Cast.renderCast(ctx.render(),
c -> c.sql("E'").sql(PostgresUtils.toPGString(value)).sql("'"),
c -> c.visit(N_BYTEA)
);
break;
default:
ctx.render()
.sql("X'")
.sql(convertBytesToHex(value))
.sql('\'');
break;
}
}
@Override
final void set0(BindingSetStatementContext ctx, byte[] value) throws SQLException {
switch (ctx.family()) {
case H2:
blobs.set(new DefaultBindingSetStatementContext<>(ctx.executeContext(), ctx.statement(), ctx.index(), value));
break;
default:
ctx.statement().setBytes(ctx.index(), value);
break;
}
}
@Override
final void set0(BindingSetSQLOutputContext ctx, byte[] value) throws SQLException {
ctx.output().writeBytes(value);
}
@Override
final byte[] get0(BindingGetResultSetContext ctx) throws SQLException {
switch (ctx.family()) {
case H2:
DefaultBindingGetResultSetContext x = new DefaultBindingGetResultSetContext<>(ctx.executeContext(), ctx.resultSet(), ctx.index());
blobs.get(x);
return x.value();
default:
return ctx.resultSet().getBytes(ctx.index());
}
}
@Override
final byte[] get0(BindingGetStatementContext ctx) throws SQLException {
switch (ctx.family()) {
case H2:
DefaultBindingGetStatementContext x = new DefaultBindingGetStatementContext<>(ctx.executeContext(), ctx.statement(), ctx.index());
blobs.get(x);
return x.value();
default:
return ctx.statement().getBytes(ctx.index());
}
}
@Override
final byte[] get0(BindingGetSQLInputContext ctx) throws SQLException {
return ctx.input().readBytes();
}
@Override
final int sqltype(Statement statement, Configuration configuration) {
switch (configuration.family()) {
// [#1225] [#1227] TODO Put this logic into DataType
// Some dialects have trouble binding binary data as BLOB
// Same logic in DefaultBlobBinding
case POSTGRES:
case YUGABYTEDB:
return Types.BINARY;
default:
return Types.BLOB;
}
}
}
static final class DefaultClobBinding extends InternalBinding {
DefaultClobBinding(DataType dataType, Converter converter) {
super(dataType, converter);
}
@Override
final void setNull0(BindingSetStatementContext ctx) throws SQLException {
super.setNull0(ctx);
}
@Override
final void set0(BindingSetStatementContext ctx, Clob value) throws SQLException {
ctx.statement().setClob(ctx.index(), value);
}
@Override
final void set0(BindingSetSQLOutputContext ctx, Clob value) throws SQLException {
ctx.output().writeClob(value);
}
@Override
final Clob get0(BindingGetResultSetContext ctx) throws SQLException {
return ctx.resultSet().getClob(ctx.index());
}
@Override
final Clob get0(BindingGetStatementContext ctx) throws SQLException {
return ctx.statement().getClob(ctx.index());
}
@Override
final Clob get0(BindingGetSQLInputContext ctx) throws SQLException {
return ctx.input().readClob();
}
@Override
final int sqltype(Statement statement, Configuration configuration) {
return Types.CLOB;
}
}
static final class DefaultDateBinding extends InternalBinding {
private static final Set INLINE_AS_STRING_LITERAL = SQLDialect.supportedBy(SQLITE);
DefaultDateBinding(DataType dataType, Converter converter) {
super(dataType, converter);
}
@Override
final void setNull0(BindingSetStatementContext ctx) throws SQLException {
super.setNull0(ctx);
}
@Override
final void sqlInline0(BindingSQLContext ctx, Date value) {
// [#1156] DATE / TIME inlining is very vendor-specific
// The SQLite JDBC driver does not implement the escape syntax
// [#1253] Sybase does not implement date literals
if (INLINE_AS_STRING_LITERAL.contains(ctx.dialect()))
ctx.render().sql('\'').sql(escape(value, ctx.render())).sql('\'');
// [#1253] Derby doesn't support the standard literal
else if (ctx.family() == DERBY)
ctx.render().visit(K_DATE).sql("('").sql(escape(value, ctx.render())).sql("')");
// [#3648] Circumvent a MySQL bug related to date literals
else if (REQUIRE_JDBC_DATE_LITERAL.contains(ctx.dialect()))
ctx.render().sql("{d '").sql(escape(value, ctx.render())).sql("'}");
// Most dialects implement SQL standard date literals
else
ctx.render().visit(K_DATE).sql(" '").sql(format(value, ctx.render())).sql('\'');
}
private final String format(Date value, RenderContext render) {
if (render.family() == POSTGRES)
if (value.getTime() == PG_DATE_POSITIVE_INFINITY)
return "infinity";
else if (value.getTime() == PG_DATE_NEGATIVE_INFINITY)
return "-infinity";
// [#9968] JDBC's java.sql.Date formats years as YYYY, which is wrong when the
// year exceeds 10000. We could use java.time.LocalDate, but that's not
// available in jOOQ's Java 6 distribution.
if (value.getYear() + 1900 >= 10000)
return (value.getYear() + 1900) +
"-" + leftPad("" + (value.getMonth() + 1), 2, '0') +
"-" + leftPad("" + value.getDate(), 2, '0');
else
return escape(value, render);
}
@Override
final void sqlBind0(BindingSQLContext ctx, Date value) throws SQLException {
super.sqlBind0(ctx, value);
}
@Override
final void register0(BindingRegisterContext ctx) throws SQLException {
super.register0(ctx);
}
@Override
final void set0(BindingSetStatementContext ctx, Date value) throws SQLException {
if (ctx.family() == SQLITE)
ctx.statement().setString(ctx.index(), value.toString());
else
ctx.statement().setDate(ctx.index(), value);
}
@Override
final void set0(BindingSetSQLOutputContext ctx, Date value) throws SQLException {
ctx.output().writeDate(value);
}
@Override
final Date get0(BindingGetResultSetContext ctx) throws SQLException {
SQLDialect family = ctx.family();
// SQLite's type affinity needs special care...
if (family == SQLITE) {
String date = ctx.resultSet().getString(ctx.index());
return date == null ? null : new Date(parse(Date.class, date));
}
else {
return ctx.resultSet().getDate(ctx.index());
}
}
@Override
final Date get0(BindingGetStatementContext ctx) throws SQLException {
return ctx.statement().getDate(ctx.index());
}
@Override
final Date get0(BindingGetSQLInputContext ctx) throws SQLException {
return ctx.input().readDate();
}
@Override
final int sqltype(Statement statement, Configuration configuration) {
return Types.DATE;
}
}
static final class DefaultDayToSecondBinding extends InternalBinding {
private static final Set REQUIRE_PG_INTERVAL = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB);
private static final Set REQUIRE_STANDARD_INTERVAL = SQLDialect.supportedBy(H2);
DefaultDayToSecondBinding(DataType dataType, Converter converter) {
super(dataType, converter);
}
@Override
void sqlInline0(BindingSQLContext