Implementation:Apache Paimon RowKind
| Knowledge Sources | |
|---|---|
| Domains | Changelog, Stream Processing |
| Last Updated | 2026-02-08 00:00 GMT |
Overview
RowKind is an enumeration that lists all kinds of changes a row can describe in a changelog, supporting streaming table semantics.
Description
RowKind is a public enum defining four change operations for row-level modifications in streaming and changelog scenarios: INSERT (+I), UPDATE_BEFORE (-U), UPDATE_AFTER (+U), and DELETE (-D). Each enum constant is associated with a short string representation and a stable byte value for serialization purposes, ensuring consistency across different JVM instances where enum hash codes may vary.
The enum provides utility methods for classification: isRetract() returns true for UPDATE_BEFORE and DELETE operations that remove previous state, while isAdd() returns true for INSERT and UPDATE_AFTER operations that add new state. This distinction is crucial for maintaining correct changelog semantics in streaming systems where some operations must be paired (UPDATE_BEFORE with UPDATE_AFTER for non-idempotent updates) while others can stand alone (INSERT, DELETE, or idempotent UPDATE_AFTER).
RowKind supports bidirectional conversion between its representations via fromByteValue() and toByteValue() methods for byte-based serialization, and fromShortString() and shortString() for human-readable formats. The byte values (0-3) are stable and intended for persistent storage, while short strings (+I, -U, +U, -D) provide intuitive visualization of change operations. Available since version 0.4.0 as @Public API, RowKind is fundamental to Paimon's streaming table implementation and change data capture (CDC) support.
Usage
Use RowKind when working with changelog streams, implementing change data capture, processing streaming table updates, or building systems that need to track and replay row-level modifications. It is essential for maintaining consistency in streaming data pipelines.
Code Reference
Source Location
- Repository: Apache_Paimon
- File: paimon-api/src/main/java/org/apache/paimon/types/RowKind.java
Signature
@Public
public enum RowKind {
INSERT("+I", (byte) 0),
UPDATE_BEFORE("-U", (byte) 1),
UPDATE_AFTER("+U", (byte) 2),
DELETE("-D", (byte) 3);
public String shortString()
public byte toByteValue()
public boolean isRetract()
public boolean isAdd()
public static RowKind fromByteValue(byte value)
public static RowKind fromShortString(String value)
}
Import
import org.apache.paimon.types.RowKind;
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| Byte value | byte | Context-dependent | Value 0-3 for deserialization |
| Short string | String | Context-dependent | String "+I", "-U", "+U", or "-D" for parsing |
Outputs
| Name | Type | Description |
|---|---|---|
| RowKind | RowKind | The enum constant representing the change type |
| Short string | String | Human-readable representation (+I, -U, +U, -D) |
| Byte value | byte | Stable numeric value (0-3) for serialization |
| Classification | boolean | Whether operation is retract or add |
Usage Examples
// Using RowKind constants
RowKind insert = RowKind.INSERT;
RowKind updateBefore = RowKind.UPDATE_BEFORE;
RowKind updateAfter = RowKind.UPDATE_AFTER;
RowKind delete = RowKind.DELETE;
// Get short string representation
String insertStr = insert.shortString(); // "+I"
String deleteStr = delete.shortString(); // "-D"
// Get byte value for serialization
byte insertByte = insert.toByteValue(); // 0
byte updateBeforeByte = updateBefore.toByteValue(); // 1
byte updateAfterByte = updateAfter.toByteValue(); // 2
byte deleteByte = delete.toByteValue(); // 3
// Check if operation is retract (removes state)
boolean isInsertRetract = insert.isRetract(); // false
boolean isDeleteRetract = delete.isRetract(); // true
boolean isUpdateBeforeRetract = updateBefore.isRetract(); // true
boolean isUpdateAfterRetract = updateAfter.isRetract(); // false
// Check if operation is add (adds state)
boolean isInsertAdd = insert.isAdd(); // true
boolean isDeleteAdd = delete.isAdd(); // false
boolean isUpdateAfterAdd = updateAfter.isAdd(); // true
// Convert from byte value
RowKind fromByte0 = RowKind.fromByteValue((byte) 0); // INSERT
RowKind fromByte3 = RowKind.fromByteValue((byte) 3); // DELETE
// Convert from short string (case-insensitive)
RowKind fromString1 = RowKind.fromShortString("+I"); // INSERT
RowKind fromString2 = RowKind.fromShortString("-u"); // UPDATE_BEFORE
RowKind fromString3 = RowKind.fromShortString("+U"); // UPDATE_AFTER
// Example: Processing changelog entries
public void processChange(RowKind kind, Object[] row) {
if (kind.isRetract()) {
// Remove previous state
removeFromState(row);
}
if (kind.isAdd()) {
// Add new state
addToState(row);
}
}
// Example: Handling update operations
public void handleUpdate(RowKind kind, Object[] row) {
switch (kind) {
case INSERT:
insertRow(row);
break;
case UPDATE_BEFORE:
// Retract old version
retractRow(row);
break;
case UPDATE_AFTER:
// Apply new version
upsertRow(row);
break;
case DELETE:
deleteRow(row);
break;
}
}
// Example: Idempotent vs non-idempotent updates
// Non-idempotent: Must send both UPDATE_BEFORE and UPDATE_AFTER
emitChange(RowKind.UPDATE_BEFORE, oldRow);
emitChange(RowKind.UPDATE_AFTER, newRow);
// Idempotent: Only UPDATE_AFTER needed (with primary key)
emitChange(RowKind.UPDATE_AFTER, newRowWithKey);