io.debezium.connector.db2.Db2ValueConverters Maven / Gradle / Ivy
/*
* Copyright Debezium Authors.
*
* Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package io.debezium.connector.db2;
import java.math.BigDecimal;
import java.sql.Types;
import java.time.ZoneOffset;
import org.apache.kafka.connect.data.Field;
import org.apache.kafka.connect.data.SchemaBuilder;
import io.debezium.data.SpecialValueDecimal;
import io.debezium.data.VariableScaleDecimal;
import io.debezium.jdbc.JdbcValueConverters;
import io.debezium.jdbc.JdbcValueConverters.DecimalMode;
import io.debezium.jdbc.TemporalPrecisionMode;
import io.debezium.relational.Column;
import io.debezium.relational.ValueConverter;
/**
* Conversion of DB2 specific datatypes.
*
* @author Jiri Pechanec, Peter Urbanetz
*
*/
public class Db2ValueConverters extends JdbcValueConverters {
public Db2ValueConverters() {
}
/**
* Create a new instance that always uses UTC for the default time zone when
* converting values without timezone information to values that require
* timezones.
*
*
* @param decimalMode
* how {@code DECIMAL} and {@code NUMERIC} values should be
* treated; may be null if
* {@link io.debezium.jdbc.JdbcValueConverters.DecimalMode#PRECISE}
* is to be used
* @param temporalPrecisionMode
* date/time value will be represented either as Connect datatypes or Debezium specific datatypes
*/
public Db2ValueConverters(DecimalMode decimalMode, TemporalPrecisionMode temporalPrecisionMode) {
super(decimalMode, temporalPrecisionMode, ZoneOffset.UTC, null, null, null);
}
@Override
public SchemaBuilder schemaBuilder(Column column) {
switch (column.jdbcType()) {
// Numeric integers
case Types.TINYINT:
// values are an 8-bit unsigned integer value between 0 and 255, we thus need to store it in short int
return SchemaBuilder.int16();
case Types.OTHER:
if (matches(column.typeName().toUpperCase(), "DECFLOAT")) {
return decfloatSchema(column);
}
default:
return super.schemaBuilder(column);
}
}
@Override
public ValueConverter converter(Column column, Field fieldDefn) {
switch (column.jdbcType()) {
// Numeric integers
case Types.TINYINT:
// values are an 8-bit unsigned integer value between 0 and 255, we thus need to store it in short int
return (data) -> convertSmallInt(column, fieldDefn, data);
case Types.OTHER:
if (matches(column.typeName().toUpperCase(), "DECFLOAT")) {
return (data) -> convertDecfloat(column, fieldDefn, data, decimalMode);
}
default:
return super.converter(column, fieldDefn);
}
}
protected Object convertDecfloat(Column column, Field fieldDefn, Object data, DecimalMode mode) {
SpecialValueDecimal value;
BigDecimal newDecimal;
if (data instanceof SpecialValueDecimal) {
value = (SpecialValueDecimal) data;
if (value.getDecimalValue().isEmpty()) {
return SpecialValueDecimal.fromLogical(value, mode, column.name());
}
}
else {
final Object o = toBigDecimal(column, fieldDefn, data);
if (!(o instanceof BigDecimal)) {
return o;
}
value = new SpecialValueDecimal((BigDecimal) o);
}
newDecimal = withScaleAdjustedIfNeeded(column, value.getDecimalValue().get());
if (mode == DecimalMode.PRECISE) {
newDecimal = newDecimal.stripTrailingZeros();
if (newDecimal.scale() < 0) {
newDecimal = newDecimal.setScale(0);
}
return VariableScaleDecimal.fromLogical(fieldDefn.schema(), new SpecialValueDecimal(newDecimal));
}
return SpecialValueDecimal.fromLogical(new SpecialValueDecimal(newDecimal), mode, column.name());
}
/**
* Time precision in DB2 is defined in scale, the default one is 7
*/
@Override
protected int getTimePrecision(Column column) {
return column.scale().get();
}
protected Object convertTimestampWithZone(Column column, Field fieldDefn, Object data) {
// dummy return
return super.convertTimestampWithZone(column, fieldDefn, data);
}
/**
* Determine if the uppercase form of a column's type exactly matches or begins with the specified prefix.
* Note that this logic works when the column's {@link Column#typeName() type} contains the type name followed by parentheses.
*
* @param upperCaseTypeName the upper case form of the column's {@link Column#typeName() type name}
* @param upperCaseMatch the upper case form of the expected type or prefix of the type; may not be null
* @return {@code true} if the type matches the specified type, or {@code false} otherwise
*/
protected static boolean matches(String upperCaseTypeName, String upperCaseMatch) {
if (upperCaseTypeName == null) {
return false;
}
return upperCaseMatch.equals(upperCaseTypeName) || upperCaseTypeName.startsWith(upperCaseMatch + "(");
}
private SchemaBuilder decfloatSchema(Column column) {
if (decimalMode == DecimalMode.PRECISE) {
return VariableScaleDecimal.builder();
}
return SpecialValueDecimal.builder(decimalMode, column.length(), column.scale().orElse(0));
}
}