Class CellValueCodec

java.lang.Object
io.github.flink.gcp.connector.bigtable.table.CellValueCodec

@Internal public final class CellValueCodec extends Object
Turns a RowData field into the bytes of a Bigtable cell, and back.

The encoding is the HBase ecosystem's, and that is normative here. A Bigtable cell is an uninterpreted byte string, so some convention has to be picked; this connector inherits users from Bigtable-via-HBase, where the convention is org.apache.hadoop.hbase.util.Bytes as Flink's own HBase connector applies it in HBaseSerde. Reproducing it — rather than depending on hbase-common, which drags in Hadoop — is what lets a table written by either connector be read by the other. The layouts below were read from hbase-common 2.6.6 and flink-connector-hbase-base 4.0.0-1.19 on 2026-08-10; CellValueCodecTest pins each one to an exact byte array, so a refactor cannot quietly break the interop the choice was made for.

Two places where the RowData path and the older Row/java.sql path of that connector disagree, and this follows the RowData one because that is what a Flink SQL job writes today: DATE is a four-byte day count, not an eight-byte epoch-millis value, and TIME is a four-byte millisecond-of-day.

What is normative is the byte layouts, not the error policy: a decimal cell overflowing its declared precision is a decode failure here, where the HBase connector reads null — silently aliasing real data onto the null convention below (#1038, ADR-0135).

What a fixed-width decoder does with a value longer than its layout is TrailingBytes' choice, defaulting to the mirrored contract: Bytes.toLong(byte[]) and its siblings delegate to their offset-taking overloads, whose only bound is offset + length <= bytes.length, so they read the leading bytes of a longer array without complaint (verified against hbase-common 2.6.6 on 2026-08-23, ADR-0136). Bytes.toBoolean is the one member that checks its length, and the BOOLEAN decoder mirrors that under either choice. A value shorter than its layout fails under both.

A null is an empty cell for every type but a character string, where an empty cell is a legitimate value — that column writes the null-string-literal instead.

  • Method Details

    • checkSupported

      public static void checkSupported(String column, org.apache.flink.table.types.logical.LogicalType type)
      Rejects a column whose type has no cell encoding, naming the column.

      Called while the DDL is parsed rather than left to encoder(LogicalType), so that an unusable column is reported when the sink is built rather than when a row reaches it. That is when the table is first written to, not when it is created: Flink does not consult a connector while registering a table.

      Parameters:
      column - the column's name, for the message
      type - the declared type
      Throws:
      org.apache.flink.table.api.ValidationException - if the type cannot be stored in a cell
    • nullableEncoder

      public static CellValueCodec.FieldEncoder nullableEncoder(org.apache.flink.table.types.logical.LogicalType type, byte[] nullStringBytes)
      Returns an encoder that writes a null as an empty cell, or as nullStringBytes for a character string.
      Parameters:
      type - the declared type
      nullStringBytes - the null-string-literal, UTF-8 encoded
      Returns:
      the encoder
    • encoder

      public static CellValueCodec.FieldEncoder encoder(org.apache.flink.table.types.logical.LogicalType type)
      Returns an encoder for a field that is known to be present.
      Parameters:
      type - the declared type
      Returns:
      the encoder
    • nullableDecoder

      public static CellValueCodec.FieldDecoder nullableDecoder(org.apache.flink.table.types.logical.LogicalType type, byte[] nullStringBytes, TrailingBytes trailingBytes)
      Returns a decoder that reads a null the way nullableEncoder(LogicalType, byte[]) wrote it: an empty cell for every type but a character string, where it is the null-string-literal — an empty string cell is a value in its own right and decodes as one.
      Parameters:
      type - the declared type
      nullStringBytes - the null-string-literal, UTF-8 encoded
      trailingBytes - what a fixed-width decode does with bytes past the declared layout
      Returns:
      the decoder
    • isTrailingBytesGoverned

      public static boolean isTrailingBytesGoverned(org.apache.flink.table.types.logical.LogicalType type)
      Whether type's cell layout is one the trailing-bytes policy governs — the fixed-width numeric, temporal and interval layouts decoded through the exact-width check. BOOLEAN is outside it (its one-byte rule holds under either policy), and so are the variable-width and self-delimiting layouts, which have no trailing bytes to decide about.
      Parameters:
      type - the declared type
      Returns:
      whether the policy decides this type's overlong decode
    • decoder

      public static CellValueCodec.FieldDecoder decoder(org.apache.flink.table.types.logical.LogicalType type, TrailingBytes trailingBytes)
      Returns a decoder for a cell that is known to hold a value.
      Parameters:
      type - the declared type
      trailingBytes - what a fixed-width decode does with bytes past the declared layout
      Returns:
      the decoder