From 9a0b43ff2ad3985d79f98be7371dccc6f9287f55 Mon Sep 17 00:00:00 2001 From: Liudmila Kornilova Date: Thu, 23 May 2024 16:53:28 +0200 Subject: [PATCH] Add missing codecs --- .../cassandra/jdbc/CassandraConnection.java | 30 ++++++++ .../data/cassandra/jdbc/SessionHolder.java | 26 +------ .../cassandra/jdbc/codec/BaseLongCodec.java | 56 ++++++++++++++ .../jdbc/codec/BlobToByteArrayCodec.java | 53 +++++++++++++ .../cassandra/jdbc/codec/DateToDateCodec.java | 58 ++++++++++++++ .../jdbc/codec/DecimalToDoubleCodec.java | 60 +++++---------- .../jdbc/codec/DecimalToLongCodec.java | 75 +++++++++++++++++++ .../jdbc/codec/DoubleToLongCodec.java | 34 +++++++++ .../jdbc/codec/DurationToStringCodec.java | 58 ++++++++++++++ .../jdbc/codec/FloatToDoubleCodec.java | 43 ++++------- .../jdbc/codec/FloatToLongCodec.java | 33 ++++++++ .../jdbc/codec/InetToStringCodec.java | 60 +++++++++++++++ .../jdbc/codec/SmallintToLongCodec.java | 36 +++++++++ .../cassandra/jdbc/codec/TimeToTimeCodec.java | 56 ++++++++++++++ .../jdbc/codec/TimeuuidToStringCodec.java | 55 ++++++++++++++ .../jdbc/codec/TinyintToLongCodec.java | 37 +++++++++ .../jdbc/codec/UuidToStringCodec.java | 60 +++++++++++++++ .../jdbc/codec/VarintToLongCodec.java | 56 ++++++++++++++ .../jdbc/codec/DecimalToDoubleCodecTest.java | 13 +++- .../jdbc/codec/FloatToDoubleCodecTest.java | 6 +- 20 files changed, 807 insertions(+), 98 deletions(-) create mode 100644 src/main/java/com/ing/data/cassandra/jdbc/codec/BaseLongCodec.java create mode 100644 src/main/java/com/ing/data/cassandra/jdbc/codec/BlobToByteArrayCodec.java create mode 100644 src/main/java/com/ing/data/cassandra/jdbc/codec/DateToDateCodec.java create mode 100644 src/main/java/com/ing/data/cassandra/jdbc/codec/DecimalToLongCodec.java create mode 100644 src/main/java/com/ing/data/cassandra/jdbc/codec/DoubleToLongCodec.java create mode 100644 src/main/java/com/ing/data/cassandra/jdbc/codec/DurationToStringCodec.java create mode 100644 src/main/java/com/ing/data/cassandra/jdbc/codec/FloatToLongCodec.java create mode 100644 src/main/java/com/ing/data/cassandra/jdbc/codec/InetToStringCodec.java create mode 100644 src/main/java/com/ing/data/cassandra/jdbc/codec/SmallintToLongCodec.java create mode 100644 src/main/java/com/ing/data/cassandra/jdbc/codec/TimeToTimeCodec.java create mode 100644 src/main/java/com/ing/data/cassandra/jdbc/codec/TimeuuidToStringCodec.java create mode 100644 src/main/java/com/ing/data/cassandra/jdbc/codec/TinyintToLongCodec.java create mode 100644 src/main/java/com/ing/data/cassandra/jdbc/codec/UuidToStringCodec.java create mode 100644 src/main/java/com/ing/data/cassandra/jdbc/codec/VarintToLongCodec.java diff --git a/src/main/java/com/ing/data/cassandra/jdbc/CassandraConnection.java b/src/main/java/com/ing/data/cassandra/jdbc/CassandraConnection.java index 67bab1e..7922b8e 100644 --- a/src/main/java/com/ing/data/cassandra/jdbc/CassandraConnection.java +++ b/src/main/java/com/ing/data/cassandra/jdbc/CassandraConnection.java @@ -25,14 +25,25 @@ import com.datastax.oss.driver.api.core.type.codec.TypeCodec; import com.datastax.oss.driver.internal.core.type.codec.registry.DefaultCodecRegistry; import com.ing.data.cassandra.jdbc.codec.BigintToBigDecimalCodec; +import com.ing.data.cassandra.jdbc.codec.BlobToByteArrayCodec; +import com.ing.data.cassandra.jdbc.codec.DateToDateCodec; import com.ing.data.cassandra.jdbc.codec.DecimalToDoubleCodec; +import com.ing.data.cassandra.jdbc.codec.DecimalToLongCodec; +import com.ing.data.cassandra.jdbc.codec.DoubleToLongCodec; import com.ing.data.cassandra.jdbc.codec.FloatToDoubleCodec; +import com.ing.data.cassandra.jdbc.codec.FloatToLongCodec; +import com.ing.data.cassandra.jdbc.codec.InetToStringCodec; import com.ing.data.cassandra.jdbc.codec.IntToLongCodec; import com.ing.data.cassandra.jdbc.codec.LongToIntCodec; import com.ing.data.cassandra.jdbc.codec.SmallintToIntCodec; +import com.ing.data.cassandra.jdbc.codec.SmallintToLongCodec; import com.ing.data.cassandra.jdbc.codec.TimestampToLongCodec; +import com.ing.data.cassandra.jdbc.codec.TimeuuidToStringCodec; import com.ing.data.cassandra.jdbc.codec.TinyintToIntCodec; +import com.ing.data.cassandra.jdbc.codec.TinyintToLongCodec; +import com.ing.data.cassandra.jdbc.codec.UuidToStringCodec; import com.ing.data.cassandra.jdbc.codec.VarintToIntCodec; +import com.ing.data.cassandra.jdbc.codec.VarintToLongCodec; import com.ing.data.cassandra.jdbc.optionset.Default; import com.ing.data.cassandra.jdbc.optionset.OptionSet; import org.apache.commons.lang3.StringUtils; @@ -193,6 +204,8 @@ public class CassandraConnection extends AbstractConnection implements Connectio LOG.info("Node: {} runs Cassandra v.{}", entry.getValue().getEndPoint().resolve(), cassandraVersion); } }); + + registerCodecs(cSession); } /** @@ -221,6 +234,10 @@ public CassandraConnection(final Session cSession, final String currentKeyspace, this.metadata = cSession.getMetadata(); this.defaultConsistencyLevel = defaultConsistencyLevel; this.debugMode = debugMode; + registerCodecs(cSession); + } + + private static void registerCodecs(Session cSession) { final List> codecs = new ArrayList<>(); codecs.add(new TimestampToLongCodec()); codecs.add(new LongToIntCodec()); @@ -231,6 +248,19 @@ public CassandraConnection(final Session cSession, final String currentKeyspace, codecs.add(new VarintToIntCodec()); codecs.add(new SmallintToIntCodec()); codecs.add(new TinyintToIntCodec()); + codecs.add(new BlobToByteArrayCodec()); + codecs.add(new DateToDateCodec()); + codecs.add(new DecimalToLongCodec()); + codecs.add(new DoubleToLongCodec()); +// codecs.add(new DurationToStringCodec()); + codecs.add(new InetToStringCodec()); +// codecs.add(new TimeToTimeCodec()); + codecs.add(new TimeuuidToStringCodec()); + codecs.add(new UuidToStringCodec()); + codecs.add(new VarintToLongCodec()); + codecs.add(new FloatToLongCodec()); + codecs.add(new SmallintToLongCodec()); + codecs.add(new TinyintToLongCodec()); codecs.forEach(codec -> ((DefaultCodecRegistry) cSession.getContext().getCodecRegistry()).register(codec)); } diff --git a/src/main/java/com/ing/data/cassandra/jdbc/SessionHolder.java b/src/main/java/com/ing/data/cassandra/jdbc/SessionHolder.java index 4316ac2..bbe1965 100644 --- a/src/main/java/com/ing/data/cassandra/jdbc/SessionHolder.java +++ b/src/main/java/com/ing/data/cassandra/jdbc/SessionHolder.java @@ -26,20 +26,10 @@ import com.datastax.oss.driver.api.core.session.ProgrammaticArguments; import com.datastax.oss.driver.api.core.session.Session; import com.datastax.oss.driver.api.core.ssl.SslEngineFactory; -import com.datastax.oss.driver.api.core.type.codec.TypeCodec; import com.datastax.oss.driver.internal.core.context.DefaultDriverContext; import com.datastax.oss.driver.internal.core.loadbalancing.DefaultLoadBalancingPolicy; import com.datastax.oss.driver.internal.core.ssl.DefaultSslEngineFactory; import com.github.benmanes.caffeine.cache.LoadingCache; -import com.ing.data.cassandra.jdbc.codec.BigintToBigDecimalCodec; -import com.ing.data.cassandra.jdbc.codec.DecimalToDoubleCodec; -import com.ing.data.cassandra.jdbc.codec.FloatToDoubleCodec; -import com.ing.data.cassandra.jdbc.codec.IntToLongCodec; -import com.ing.data.cassandra.jdbc.codec.LongToIntCodec; -import com.ing.data.cassandra.jdbc.codec.SmallintToIntCodec; -import com.ing.data.cassandra.jdbc.codec.TimestampToLongCodec; -import com.ing.data.cassandra.jdbc.codec.TinyintToIntCodec; -import com.ing.data.cassandra.jdbc.codec.VarintToIntCodec; import com.ing.data.cassandra.jdbc.utils.ContactPoint; import com.instaclustr.cassandra.driver.auth.KerberosAuthProviderBase; import com.instaclustr.cassandra.driver.auth.ProgrammaticKerberosAuthProvider; @@ -53,7 +43,6 @@ import java.sql.SQLNonTransientConnectionException; import java.time.Duration; import java.time.temporal.ChronoUnit; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -67,7 +56,6 @@ import static com.ing.data.cassandra.jdbc.utils.DriverUtil.JSSE_TRUSTSTORE_PASSWORD_PROPERTY; import static com.ing.data.cassandra.jdbc.utils.DriverUtil.JSSE_TRUSTSTORE_PROPERTY; import static com.ing.data.cassandra.jdbc.utils.ErrorConstants.SSL_CONFIG_FAILED; -import static com.ing.data.cassandra.jdbc.utils.JdbcUrlUtil.TAG_USE_KERBEROS; import static com.ing.data.cassandra.jdbc.utils.JdbcUrlUtil.TAG_CLOUD_SECURE_CONNECT_BUNDLE; import static com.ing.data.cassandra.jdbc.utils.JdbcUrlUtil.TAG_CONFIG_FILE; import static com.ing.data.cassandra.jdbc.utils.JdbcUrlUtil.TAG_CONNECT_TIMEOUT; @@ -88,6 +76,7 @@ import static com.ing.data.cassandra.jdbc.utils.JdbcUrlUtil.TAG_SSL_HOSTNAME_VERIFICATION; import static com.ing.data.cassandra.jdbc.utils.JdbcUrlUtil.TAG_TCP_NO_DELAY; import static com.ing.data.cassandra.jdbc.utils.JdbcUrlUtil.TAG_USER; +import static com.ing.data.cassandra.jdbc.utils.JdbcUrlUtil.TAG_USE_KERBEROS; import static com.ing.data.cassandra.jdbc.utils.JdbcUrlUtil.parseReconnectionPolicy; import static com.ing.data.cassandra.jdbc.utils.JdbcUrlUtil.parseURL; @@ -341,19 +330,6 @@ private Session createSession(final Properties properties) throws SQLException { )); } - // Declare and register codecs. - final List> codecs = new ArrayList<>(); - codecs.add(new TimestampToLongCodec()); - codecs.add(new LongToIntCodec()); - codecs.add(new IntToLongCodec()); - codecs.add(new BigintToBigDecimalCodec()); - codecs.add(new DecimalToDoubleCodec()); - codecs.add(new FloatToDoubleCodec()); - codecs.add(new VarintToIntCodec()); - codecs.add(new SmallintToIntCodec()); - codecs.add(new TinyintToIntCodec()); - builder.addTypeCodecs(codecs.toArray(new TypeCodec[]{})); - builder.withKeyspace(keyspace); builder.withConfigLoader(driverConfigLoaderBuilder.build()); diff --git a/src/main/java/com/ing/data/cassandra/jdbc/codec/BaseLongCodec.java b/src/main/java/com/ing/data/cassandra/jdbc/codec/BaseLongCodec.java new file mode 100644 index 0000000..acb8210 --- /dev/null +++ b/src/main/java/com/ing/data/cassandra/jdbc/codec/BaseLongCodec.java @@ -0,0 +1,56 @@ +package com.ing.data.cassandra.jdbc.codec; + +import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.type.codec.PrimitiveLongCodec; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +import java.nio.ByteBuffer; + +/** + * @author Liudmila Kornilova + **/ +public abstract class BaseLongCodec implements PrimitiveLongCodec { + abstract int getNumberOfBytes(); + + abstract void serializeNoBoxingInner(long value, ByteBuffer bb); + + abstract long deserializeNoBoxingInner(ByteBuffer bytes); + + @NonNull + @Override + public GenericType getJavaType() { + return GenericType.LONG; + } + + @Override + public Long parse(String value) { + return value == null || value.isEmpty() || value.equalsIgnoreCase("NULL") ? null : Long.parseLong(value); + } + + @Override + @NonNull + public String format(@Nullable Long value) { + if (value == null) return "NULL"; + return Long.toString(value); + } + + @Override + public ByteBuffer encodePrimitive(long value, @NonNull ProtocolVersion protocolVersion) { + ByteBuffer bb = ByteBuffer.allocate(getNumberOfBytes()); + serializeNoBoxingInner(value, bb); + bb.flip(); + return bb; + } + + @Override + public long decodePrimitive(ByteBuffer bytes, @NonNull ProtocolVersion protocolVersion) { + if (bytes == null || bytes.remaining() == 0) return 0; + if (bytes.remaining() != getNumberOfBytes()) { + throw new IllegalStateException("Invalid value, expecting " + getNumberOfBytes() + " bytes but got " + bytes.remaining()); + } + + return deserializeNoBoxingInner(bytes); + } +} diff --git a/src/main/java/com/ing/data/cassandra/jdbc/codec/BlobToByteArrayCodec.java b/src/main/java/com/ing/data/cassandra/jdbc/codec/BlobToByteArrayCodec.java new file mode 100644 index 0000000..d4b4dba --- /dev/null +++ b/src/main/java/com/ing/data/cassandra/jdbc/codec/BlobToByteArrayCodec.java @@ -0,0 +1,53 @@ +package com.ing.data.cassandra.jdbc.codec; + + +import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.DataTypes; +import com.datastax.oss.driver.api.core.type.codec.TypeCodec; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import com.datastax.oss.protocol.internal.util.Bytes; +import edu.umd.cs.findbugs.annotations.NonNull; + +import java.nio.ByteBuffer; + +/** + * @author Liudmila Kornilova + **/ +public class BlobToByteArrayCodec implements TypeCodec { + @NonNull + @Override + public DataType getCqlType() { + return DataTypes.BLOB; + } + + @NonNull + @Override + public GenericType getJavaType() { + return GenericType.of(byte[].class); + } + + @Override + public ByteBuffer encode(byte[] value, @NonNull ProtocolVersion protocolVersion) { + return value == null ? null : ByteBuffer.wrap(value); + } + + @Override + public byte[] decode(ByteBuffer bytes, @NonNull ProtocolVersion protocolVersion) { + return bytes == null ? null : Bytes.getArray(bytes); + } + + @Override + public byte[] parse(String value) { + return value == null || value.isEmpty() || value.equalsIgnoreCase("NULL") + ? null + : Bytes.getArray(Bytes.fromHexString(value)); + } + + @Override + @NonNull + public String format(byte[] value) { + if (value == null) return "NULL"; + return Bytes.toHexString(value); + } +} diff --git a/src/main/java/com/ing/data/cassandra/jdbc/codec/DateToDateCodec.java b/src/main/java/com/ing/data/cassandra/jdbc/codec/DateToDateCodec.java new file mode 100644 index 0000000..3791ae6 --- /dev/null +++ b/src/main/java/com/ing/data/cassandra/jdbc/codec/DateToDateCodec.java @@ -0,0 +1,58 @@ +package com.ing.data.cassandra.jdbc.codec; + +import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.DataTypes; +import com.datastax.oss.driver.api.core.type.codec.TypeCodec; +import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import edu.umd.cs.findbugs.annotations.NonNull; + +import java.nio.ByteBuffer; +import java.sql.Date; +import java.time.Instant; +import java.time.LocalDate; + +/** + * @author Liudmila Kornilova + **/ +public class DateToDateCodec implements TypeCodec { + private final TypeCodec dateCodec = CodecRegistry.DEFAULT.codecFor(DataTypes.DATE, LocalDate.class); + + @NonNull + @Override + public DataType getCqlType() { + return DataTypes.DATE; + } + + @NonNull + @Override + public GenericType getJavaType() { + return GenericType.of(Date.class); + } + + @Override + public ByteBuffer encode(Date value, @NonNull ProtocolVersion protocolVersion) { + if (value == null) return null; + LocalDate localDate = LocalDate.from(Instant.ofEpochMilli(value.getTime())); + return dateCodec.encode(localDate, protocolVersion); + } + + @Override + public Date decode(ByteBuffer bytes, @NonNull ProtocolVersion protocolVersion) { + if (bytes == null) return null; + LocalDate localDate = dateCodec.decode(bytes, protocolVersion); + return localDate == null ? null : new Date(localDate.toEpochDay() * 86400L); + } + + @Override + public Date parse(String value) { + throw new RuntimeException("Not supported"); + } + + @Override + @NonNull + public String format(Date value) { + throw new RuntimeException("Not supported"); + } +} diff --git a/src/main/java/com/ing/data/cassandra/jdbc/codec/DecimalToDoubleCodec.java b/src/main/java/com/ing/data/cassandra/jdbc/codec/DecimalToDoubleCodec.java index 0cbd766..050b4c1 100644 --- a/src/main/java/com/ing/data/cassandra/jdbc/codec/DecimalToDoubleCodec.java +++ b/src/main/java/com/ing/data/cassandra/jdbc/codec/DecimalToDoubleCodec.java @@ -1,69 +1,46 @@ -/* - * - * 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. - */ - package com.ing.data.cassandra.jdbc.codec; import com.datastax.oss.driver.api.core.ProtocolVersion; import com.datastax.oss.driver.api.core.type.DataType; import com.datastax.oss.driver.api.core.type.DataTypes; +import com.datastax.oss.driver.api.core.type.codec.PrimitiveDoubleCodec; import com.datastax.oss.driver.api.core.type.codec.TypeCodec; +import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry; import com.datastax.oss.driver.api.core.type.reflect.GenericType; -import com.ing.data.cassandra.jdbc.utils.ByteBufferUtil; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.annotation.Nonnull; +import java.math.BigDecimal; import java.nio.ByteBuffer; -/** - * Manages the two-way conversion between the CQL type {@link DataTypes#DECIMAL} and the Java type {@link Double}. - */ -public class DecimalToDoubleCodec extends AbstractCodec implements TypeCodec { - /** - * Constructor for {@code DecimalToDoubleCodec}. - */ - public DecimalToDoubleCodec() { +public class DecimalToDoubleCodec extends AbstractCodec implements PrimitiveDoubleCodec { + private final TypeCodec decimalCodec = CodecRegistry.DEFAULT.codecFor(DataTypes.DECIMAL, BigDecimal.class); + + @NonNull + @Override + public DataType getCqlType() { + return DataTypes.DECIMAL; } - @Nonnull + @NonNull @Override public GenericType getJavaType() { return GenericType.DOUBLE; } - @Nonnull @Override - public DataType getCqlType() { - return DataTypes.DECIMAL; + public ByteBuffer encodePrimitive(double value, @NonNull ProtocolVersion protocolVersion) { + return decimalCodec.encode(BigDecimal.valueOf(value), protocolVersion); } @Override - public ByteBuffer encode(final Double value, @Nonnull final ProtocolVersion protocolVersion) { - if (value == null) { - return null; - } - return ByteBufferUtil.bytes(value); + public double decodePrimitive(ByteBuffer bytes, @NonNull ProtocolVersion protocolVersion) { + BigDecimal bigDecimal = decimalCodec.decode(bytes, protocolVersion); + if (bigDecimal == null) return 0; + return bigDecimal.doubleValue(); } - @Override - public Double decode(final ByteBuffer bytes, @Nonnull final ProtocolVersion protocolVersion) { - if (bytes == null) { - return null; - } - // always duplicate the ByteBuffer instance before consuming it! - return ByteBufferUtil.toDouble(bytes.duplicate()); - } @Override Double parseNonNull(@Nonnull final String value) { @@ -74,5 +51,4 @@ Double parseNonNull(@Nonnull final String value) { String formatNonNull(@Nonnull final Double value) { return String.valueOf(value); } - } diff --git a/src/main/java/com/ing/data/cassandra/jdbc/codec/DecimalToLongCodec.java b/src/main/java/com/ing/data/cassandra/jdbc/codec/DecimalToLongCodec.java new file mode 100644 index 0000000..990c49f --- /dev/null +++ b/src/main/java/com/ing/data/cassandra/jdbc/codec/DecimalToLongCodec.java @@ -0,0 +1,75 @@ +package com.ing.data.cassandra.jdbc.codec; + + +import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.DataTypes; +import com.datastax.oss.driver.api.core.type.codec.TypeCodec; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import edu.umd.cs.findbugs.annotations.NonNull; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; + +/** + * @author Liudmila Kornilova + **/ +public class DecimalToLongCodec implements TypeCodec { + @NonNull + @Override + public GenericType getJavaType() { + return GenericType.LONG; + } + + @NonNull + @Override + public DataType getCqlType() { + return DataTypes.DECIMAL; + } + + @Override + public Long parse(String value) { + return value == null || value.isEmpty() || value.equalsIgnoreCase("NULL") + ? null + : Long.valueOf(value); + } + + @Override + @NonNull + public String format(Long value) { + if (value == null) return "NULL"; + return value.toString(); + } + + @Override + public ByteBuffer encode(Long value, @NonNull ProtocolVersion protocolVersion) { + if (value == null) return null; + BigDecimal bigDecimal = new BigDecimal(value); + BigInteger bi = bigDecimal.unscaledValue(); + int scale = bigDecimal.scale(); + byte[] bibytes = bi.toByteArray(); + + ByteBuffer bytes = ByteBuffer.allocate(4 + bibytes.length); + bytes.putInt(scale); + bytes.put(bibytes); + bytes.rewind(); + return bytes; + } + + @Override + public Long decode(ByteBuffer bytes, @NonNull ProtocolVersion protocolVersion) { + if (bytes == null || bytes.remaining() == 0) return null; + if (bytes.remaining() < 4) { + throw new IllegalStateException("Invalid decimal value, expecting at least 4 bytes but got " + bytes.remaining()); + } + + bytes = bytes.duplicate(); + int scale = bytes.getInt(); + byte[] bibytes = new byte[bytes.remaining()]; + bytes.get(bibytes); + + BigInteger bi = new BigInteger(bibytes); + return new BigDecimal(bi, scale).longValue(); + } +} diff --git a/src/main/java/com/ing/data/cassandra/jdbc/codec/DoubleToLongCodec.java b/src/main/java/com/ing/data/cassandra/jdbc/codec/DoubleToLongCodec.java new file mode 100644 index 0000000..5839ca7 --- /dev/null +++ b/src/main/java/com/ing/data/cassandra/jdbc/codec/DoubleToLongCodec.java @@ -0,0 +1,34 @@ +package com.ing.data.cassandra.jdbc.codec; + +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.DataTypes; +import edu.umd.cs.findbugs.annotations.NonNull; + +import java.nio.ByteBuffer; + +/** + * @author Liudmila Kornilova + **/ +public class DoubleToLongCodec extends BaseLongCodec { + + @NonNull + @Override + public DataType getCqlType() { + return DataTypes.DOUBLE; + } + + @Override + int getNumberOfBytes() { + return 8; + } + + @Override + void serializeNoBoxingInner(long value, ByteBuffer bb) { + bb.putDouble(value); + } + + @Override + long deserializeNoBoxingInner(ByteBuffer bytes) { + return (long) bytes.getDouble(); + } +} diff --git a/src/main/java/com/ing/data/cassandra/jdbc/codec/DurationToStringCodec.java b/src/main/java/com/ing/data/cassandra/jdbc/codec/DurationToStringCodec.java new file mode 100644 index 0000000..8ebd34e --- /dev/null +++ b/src/main/java/com/ing/data/cassandra/jdbc/codec/DurationToStringCodec.java @@ -0,0 +1,58 @@ +package com.ing.data.cassandra.jdbc.codec; + +import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.DataTypes; +import com.datastax.oss.driver.api.core.type.codec.TypeCodec; +import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +import java.nio.ByteBuffer; +import java.time.Duration; + +/** + * @author Liudmila Kornilova + **/ +public class DurationToStringCodec implements TypeCodec { + private final TypeCodec durationCodec = CodecRegistry.DEFAULT.codecFor(DataTypes.DURATION, Duration.class); + + @NonNull + @Override + public DataType getCqlType() { + return DataTypes.DURATION; + } + + @NonNull + @Override + public GenericType getJavaType() { + return GenericType.STRING; + } + + @Override + public ByteBuffer encode(String value, @NonNull ProtocolVersion protocolVersion) { + if (value == null) return null; + Duration duration = Duration.parse(value); + return durationCodec.encode(duration, protocolVersion); + } + + @Override + public String decode(ByteBuffer bytes, @NonNull ProtocolVersion protocolVersion) { + if (bytes == null) return null; + Duration duration = durationCodec.decode(bytes, protocolVersion); + return duration == null ? null : duration.toString(); + } + + @Override + @Nullable + public String parse(@Nullable String value) { + throw new RuntimeException("Not supported"); + } + + @Override + @NonNull + public String format(@Nullable String value) { + throw new RuntimeException("Not supported"); + } +} diff --git a/src/main/java/com/ing/data/cassandra/jdbc/codec/FloatToDoubleCodec.java b/src/main/java/com/ing/data/cassandra/jdbc/codec/FloatToDoubleCodec.java index 7262414..e0eac09 100644 --- a/src/main/java/com/ing/data/cassandra/jdbc/codec/FloatToDoubleCodec.java +++ b/src/main/java/com/ing/data/cassandra/jdbc/codec/FloatToDoubleCodec.java @@ -18,52 +18,42 @@ import com.datastax.oss.driver.api.core.ProtocolVersion; import com.datastax.oss.driver.api.core.type.DataType; import com.datastax.oss.driver.api.core.type.DataTypes; -import com.datastax.oss.driver.api.core.type.codec.TypeCodec; +import com.datastax.oss.driver.api.core.type.codec.PrimitiveDoubleCodec; import com.datastax.oss.driver.api.core.type.reflect.GenericType; -import com.ing.data.cassandra.jdbc.utils.ByteBufferUtil; +import edu.umd.cs.findbugs.annotations.NonNull; import javax.annotation.Nonnull; import java.nio.ByteBuffer; -/** - * Manages the two-way conversion between the CQL type {@link DataTypes#FLOAT} and the Java type {@link Double}. - */ -public class FloatToDoubleCodec extends AbstractCodec implements TypeCodec { - - /** - * Constructor for {@code FloatToDoubleCodec}. - */ - public FloatToDoubleCodec() { - } +public class FloatToDoubleCodec extends AbstractCodec implements PrimitiveDoubleCodec { - @Nonnull + @NonNull @Override public GenericType getJavaType() { return GenericType.DOUBLE; } - @Nonnull + @NonNull @Override public DataType getCqlType() { return DataTypes.FLOAT; } @Override - public ByteBuffer encode(final Double value, @Nonnull final ProtocolVersion protocolVersion) { - if (value == null) { - return null; - } - return ByteBufferUtil.bytes(value.floatValue()); + public ByteBuffer encodePrimitive(double value, @NonNull ProtocolVersion protocolVersion) { + ByteBuffer bb = ByteBuffer.allocate(4); + bb.putFloat((float) value); + bb.flip(); + return bb; } @Override - public Double decode(final ByteBuffer bytes, @Nonnull final ProtocolVersion protocolVersion) { - if (bytes == null) { - return null; - } - // always duplicate the ByteBuffer instance before consuming it! - final float value = ByteBufferUtil.toFloat(bytes.duplicate()); - return (double) value; + public double decodePrimitive(ByteBuffer bytes, @NonNull ProtocolVersion protocolVersion) { + if (bytes == null || bytes.remaining() != 4) + throw new IllegalArgumentException( + "Invalid 32-bits float value, expecting 4 bytes but got " + (bytes == null ? null : bytes.remaining())); + + return bytes.getFloat(); } @Override @@ -75,5 +65,4 @@ Double parseNonNull(@Nonnull final String value) { String formatNonNull(@Nonnull final Double value) { return String.valueOf(value); } - } diff --git a/src/main/java/com/ing/data/cassandra/jdbc/codec/FloatToLongCodec.java b/src/main/java/com/ing/data/cassandra/jdbc/codec/FloatToLongCodec.java new file mode 100644 index 0000000..832ec6d --- /dev/null +++ b/src/main/java/com/ing/data/cassandra/jdbc/codec/FloatToLongCodec.java @@ -0,0 +1,33 @@ +package com.ing.data.cassandra.jdbc.codec; + +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.DataTypes; +import edu.umd.cs.findbugs.annotations.NonNull; + +import java.nio.ByteBuffer; + +/** + * @author Liudmila Kornilova + **/ +public class FloatToLongCodec extends BaseLongCodec { + @NonNull + @Override + public DataType getCqlType() { + return DataTypes.FLOAT; + } + + @Override + int getNumberOfBytes() { + return 4; + } + + @Override + void serializeNoBoxingInner(long value, ByteBuffer bb) { + bb.putFloat(value); + } + + @Override + long deserializeNoBoxingInner(ByteBuffer bytes) { + return (long) bytes.getFloat(); + } +} diff --git a/src/main/java/com/ing/data/cassandra/jdbc/codec/InetToStringCodec.java b/src/main/java/com/ing/data/cassandra/jdbc/codec/InetToStringCodec.java new file mode 100644 index 0000000..19d9f3a --- /dev/null +++ b/src/main/java/com/ing/data/cassandra/jdbc/codec/InetToStringCodec.java @@ -0,0 +1,60 @@ +package com.ing.data.cassandra.jdbc.codec; + +import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.DataTypes; +import com.datastax.oss.driver.api.core.type.codec.TypeCodec; +import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import edu.umd.cs.findbugs.annotations.NonNull; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; + +/** + * @author Liudmila Kornilova + **/ +public class InetToStringCodec implements TypeCodec { + private final TypeCodec inetCodec = CodecRegistry.DEFAULT.codecFor(DataTypes.INET, InetAddress.class); + + @NonNull + @Override + public DataType getCqlType() { + return DataTypes.INET; + } + + @NonNull + @Override + public GenericType getJavaType() { + return GenericType.STRING; + } + + @Override + public ByteBuffer encode(String value, @NonNull ProtocolVersion protocolVersion) { + if (value == null) return null; + try { + return inetCodec.encode(InetAddress.getByName(value), protocolVersion); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + } + + @Override + public String decode(ByteBuffer bytes, @NonNull ProtocolVersion protocolVersion) { + if (bytes == null) return null; + InetAddress address = inetCodec.decode(bytes, protocolVersion); + return address == null ? null : address.getHostAddress(); + } + + @Override + public String parse(String value) { + throw new RuntimeException("Not implemented"); + } + + @Override + @NonNull + public String format(String value) { + throw new RuntimeException("Not implemented"); + } +} diff --git a/src/main/java/com/ing/data/cassandra/jdbc/codec/SmallintToLongCodec.java b/src/main/java/com/ing/data/cassandra/jdbc/codec/SmallintToLongCodec.java new file mode 100644 index 0000000..cd7015d --- /dev/null +++ b/src/main/java/com/ing/data/cassandra/jdbc/codec/SmallintToLongCodec.java @@ -0,0 +1,36 @@ +package com.ing.data.cassandra.jdbc.codec; + +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.DataTypes; +import edu.umd.cs.findbugs.annotations.NonNull; + +import java.nio.ByteBuffer; + +/** + * @author Liudmila Kornilova + **/ +public class SmallintToLongCodec extends BaseLongCodec { + @NonNull + @Override + public DataType getCqlType() { + return DataTypes.SMALLINT; + } + + @Override + int getNumberOfBytes() { + return 2; + } + + @Override + void serializeNoBoxingInner(long value, ByteBuffer bb) { + if (value > Short.MAX_VALUE || value < Short.MIN_VALUE) { + throw new IllegalArgumentException("Long value " + value + " does not fit into cql type smallint"); + } + bb.putShort((short) value); + } + + @Override + long deserializeNoBoxingInner(ByteBuffer bytes) { + return bytes.getShort(); + } +} diff --git a/src/main/java/com/ing/data/cassandra/jdbc/codec/TimeToTimeCodec.java b/src/main/java/com/ing/data/cassandra/jdbc/codec/TimeToTimeCodec.java new file mode 100644 index 0000000..303d984 --- /dev/null +++ b/src/main/java/com/ing/data/cassandra/jdbc/codec/TimeToTimeCodec.java @@ -0,0 +1,56 @@ +package com.ing.data.cassandra.jdbc.codec; + +import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.DataTypes; +import com.datastax.oss.driver.api.core.type.codec.TypeCodec; +import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import edu.umd.cs.findbugs.annotations.NonNull; + +import java.nio.ByteBuffer; +import java.sql.Time; + +/** + * @author Liudmila Kornilova + **/ +public class TimeToTimeCodec implements TypeCodec