Search in sources :

Example 1 with SpecialValueDecimal

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;
}
Also used : PGpoint(org.postgresql.geometric.PGpoint) PGcircle(org.postgresql.geometric.PGcircle) SQLException(java.sql.SQLException) PGmoney(org.postgresql.util.PGmoney) PGpolygon(org.postgresql.geometric.PGpolygon) BigDecimal(java.math.BigDecimal) PGInterval(org.postgresql.util.PGInterval) PGline(org.postgresql.geometric.PGline) SpecialValueDecimal(io.debezium.data.SpecialValueDecimal) PGpath(org.postgresql.geometric.PGpath) PgArray(org.postgresql.jdbc.PgArray) PGbox(org.postgresql.geometric.PGbox) ConnectException(org.apache.kafka.connect.errors.ConnectException) PGlseg(org.postgresql.geometric.PGlseg)

Example 2 with SpecialValueDecimal

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);
}
Also used : SpecialValueDecimal(io.debezium.data.SpecialValueDecimal) BigDecimal(java.math.BigDecimal) Struct(org.apache.kafka.connect.data.Struct) Test(org.junit.Test)

Example 3 with SpecialValueDecimal

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());
}
Also used : SpecialValueDecimal(io.debezium.data.SpecialValueDecimal) BigDecimal(java.math.BigDecimal)

Example 4 with SpecialValueDecimal

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);
    }
}
Also used : Array(java.sql.Array) SQLException(java.sql.SQLException) PGmoney(org.postgresql.util.PGmoney) SpecialValueDecimal(io.debezium.data.SpecialValueDecimal)

Aggregations

SpecialValueDecimal (io.debezium.data.SpecialValueDecimal)4 BigDecimal (java.math.BigDecimal)3 SQLException (java.sql.SQLException)2 PGmoney (org.postgresql.util.PGmoney)2 Array (java.sql.Array)1 Struct (org.apache.kafka.connect.data.Struct)1 ConnectException (org.apache.kafka.connect.errors.ConnectException)1 Test (org.junit.Test)1 PGbox (org.postgresql.geometric.PGbox)1 PGcircle (org.postgresql.geometric.PGcircle)1 PGline (org.postgresql.geometric.PGline)1 PGlseg (org.postgresql.geometric.PGlseg)1 PGpath (org.postgresql.geometric.PGpath)1 PGpoint (org.postgresql.geometric.PGpoint)1 PGpolygon (org.postgresql.geometric.PGpolygon)1 PgArray (org.postgresql.jdbc.PgArray)1 PGInterval (org.postgresql.util.PGInterval)1