use of io.debezium.data.SpecialValueDecimal in project debezium by debezium.
the class Wal2JsonReplicationMessage method getValue.
/**
* Converts the value (string representation) coming from wal2json plugin to
* a Java value based on the type of the column from the message. This value will be converted later on if necessary by the
* {@link PostgresValueConverter#converter(Column, Field)} instance to match whatever the Connect schema type expects.
*
* Note that the logic here is tightly coupled (i.e. dependent) on the wal2json plugin logic which writes the actual
* JSON messages.
* @param a supplier to get a connection to Postgres instance for array handling
*
* @return the value; may be null
*/
public Object getValue(String columnName, PostgresType type, String fullType, Value rawValue, final PgConnectionSupplier connection, boolean includeUnknownDatatypes) {
if (rawValue.isNull()) {
// nulls are null
return null;
}
if (type.isArrayType()) {
try {
final String dataString = rawValue.asString();
PgArray arrayData = new PgArray(connection.get(), type.getOid(), dataString);
Object deserializedArray = arrayData.getArray();
return Arrays.asList((Object[]) deserializedArray);
} catch (SQLException e) {
LOGGER.warn("Unexpected exception trying to process PgArray ({}) column '{}', {}", fullType, columnName, e);
}
return null;
}
switch(type.getName()) {
// plus aliases from the shorter names produced by older wal2json
case "boolean":
case "bool":
return rawValue.asBoolean();
case "integer":
case "int":
case "int4":
case "smallint":
case "int2":
case "smallserial":
case "serial":
case "serial2":
case "serial4":
case "oid":
return rawValue.asInteger();
case "bigint":
case "bigserial":
case "int8":
return rawValue.asLong();
case "real":
case "float4":
return rawValue.isNumber() ? rawValue.asFloat() : Float.valueOf(rawValue.asString());
case "double precision":
case "float8":
return rawValue.isNumber() ? rawValue.asDouble() : Double.valueOf(rawValue.asString());
case "numeric":
case "decimal":
if (rawValue.isInteger()) {
return new SpecialValueDecimal(new BigDecimal(rawValue.asInteger()));
} else if (rawValue.isLong()) {
return new SpecialValueDecimal(new BigDecimal(rawValue.asLong()));
} else if (rawValue.isBigInteger()) {
return new SpecialValueDecimal(new BigDecimal(rawValue.asBigInteger()));
}
return SpecialValueDecimal.valueOf(rawValue.asString());
case "character":
case "char":
case "character varying":
case "varchar":
case "bpchar":
case "text":
return rawValue.asString();
case "date":
return DateTimeFormat.get().date(rawValue.asString());
case "timestamp with time zone":
case "timestamptz":
return DateTimeFormat.get().timestampWithTimeZone(rawValue.asString());
case "timestamp":
case "timestamp without time zone":
return DateTimeFormat.get().timestamp(rawValue.asString());
case "time":
case "time without time zone":
return DateTimeFormat.get().time(rawValue.asString());
case "time with time zone":
case "timetz":
return DateTimeFormat.get().timeWithTimeZone(rawValue.asString());
case "bytea":
return Strings.hexStringToByteArray(rawValue.asString());
// i.e. those values won't actually be propagated to the outbound message until that's the case
case "box":
try {
return new PGbox(rawValue.asString());
} catch (final SQLException e) {
LOGGER.error("Failed to parse point {}, {}", rawValue.asString(), e);
throw new ConnectException(e);
}
case "circle":
try {
return new PGcircle(rawValue.asString());
} catch (final SQLException e) {
LOGGER.error("Failed to parse circle {}, {}", rawValue.asString(), e);
throw new ConnectException(e);
}
case "interval":
try {
return new PGInterval(rawValue.asString());
} catch (final SQLException e) {
LOGGER.error("Failed to parse point {}, {}", rawValue.asString(), e);
throw new ConnectException(e);
}
case "line":
try {
return new PGline(rawValue.asString());
} catch (final SQLException e) {
LOGGER.error("Failed to parse point {}, {}", rawValue.asString(), e);
throw new ConnectException(e);
}
case "lseg":
try {
return new PGlseg(rawValue.asString());
} catch (final SQLException e) {
LOGGER.error("Failed to parse point {}, {}", rawValue.asString(), e);
throw new ConnectException(e);
}
case "money":
try {
return new PGmoney(rawValue.asString()).val;
} catch (final SQLException e) {
LOGGER.error("Failed to parse money {}, {}", rawValue.asString(), e);
throw new ConnectException(e);
}
case "path":
try {
return new PGpath(rawValue.asString());
} catch (final SQLException e) {
LOGGER.error("Failed to parse point {}, {}", rawValue.asString(), e);
throw new ConnectException(e);
}
case "point":
try {
return new PGpoint(rawValue.asString());
} catch (final SQLException e) {
LOGGER.error("Failed to parse point {}, {}", rawValue.asString(), e);
throw new ConnectException(e);
}
case "polygon":
try {
return new PGpolygon(rawValue.asString());
} catch (final SQLException e) {
LOGGER.error("Failed to parse point {}, {}", rawValue.asString(), e);
throw new ConnectException(e);
}
// ValueConverter turns them into the correct types
case "geometry":
case "geography":
return rawValue.asString();
case "bit":
case "bit varying":
case "varbit":
case "json":
case "jsonb":
case "xml":
case "uuid":
case "tstzrange":
return rawValue.asString();
// TODO: improve with more specific/useful classes here?
case "cidr":
case "inet":
case "macaddr":
case "macaddr8":
case "pg_lsn":
case "tsquery":
case "tsvector":
case "txid_snapshot":
// catch-all for unknown (extension module/custom) types
default:
break;
}
if (includeUnknownDatatypes) {
// this includes things like PostGIS geometries or other custom types.
// leave up to the downstream message recipient to deal with.
LOGGER.debug("processing column '{}' with unknown data type '{}' as byte array", columnName, fullType);
return rawValue.asString();
}
LOGGER.debug("Unknown column type {} for column {} – ignoring", fullType, columnName);
return null;
}
use of io.debezium.data.SpecialValueDecimal in project debezium by debezium.
the class CustomTypeEncodingTest method testVariableScaleDecimal.
@Test
public void testVariableScaleDecimal() {
final BigDecimal testValue = new BigDecimal("138.456");
final Struct struct = VariableScaleDecimal.fromLogical(VariableScaleDecimal.schema(), new SpecialValueDecimal(testValue));
final BigDecimal decodedValue = VariableScaleDecimal.toLogical(struct).getDecimalValue().get();
assertEquals("Number should be same after serde", testValue, decodedValue);
}
use of io.debezium.data.SpecialValueDecimal in project debezium by debezium.
the class PostgresValueConverter method convertDecimal.
protected Object convertDecimal(Column column, Field fieldDefn, Object data, DecimalMode mode) {
SpecialValueDecimal value;
BigDecimal newDecimal;
if (data instanceof SpecialValueDecimal) {
value = (SpecialValueDecimal) data;
if (!value.getDecimalValue().isPresent()) {
return SpecialValueDecimal.fromLogical(value, mode, column.name());
}
} else {
final Object o = toBigDecimal(column, fieldDefn, data);
if (o == null || !(o instanceof BigDecimal)) {
return o;
}
value = new SpecialValueDecimal((BigDecimal) o);
}
newDecimal = value.getDecimalValue().get();
if (column.scale() > newDecimal.scale()) {
newDecimal = newDecimal.setScale(column.scale());
}
if (isVariableScaleDecimal(column) && 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());
}
use of io.debezium.data.SpecialValueDecimal in project debezium by debezium.
the class RecordsSnapshotProducer method valueForColumn.
private Object valueForColumn(ResultSet rs, int colIdx, ResultSetMetaData metaData) throws SQLException {
try {
final String columnTypeName = metaData.getColumnTypeName(colIdx);
final PostgresType type = taskContext.schema().getTypeRegistry().get(columnTypeName);
if (type.isArrayType()) {
Array array = rs.getArray(colIdx);
if (array == null) {
return null;
}
return Arrays.asList((Object[]) array.getArray());
}
switch(type.getOid()) {
case PgOid.MONEY:
// TODO author=Horia Chiorean date=14/11/2016 description=workaround for https://github.com/pgjdbc/pgjdbc/issues/100
return new PGmoney(rs.getString(colIdx)).val;
case PgOid.BIT:
return rs.getString(colIdx);
case PgOid.NUMERIC:
final String s = rs.getString(colIdx);
if (s == null) {
return s;
}
Optional<SpecialValueDecimal> value = PostgresValueConverter.toSpecialValue(s);
return value.isPresent() ? value.get() : new SpecialValueDecimal(rs.getBigDecimal(colIdx));
default:
return rs.getObject(colIdx);
}
} catch (SQLException e) {
// not a known type
return rs.getObject(colIdx);
}
}
Aggregations