Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 91 additions & 1 deletion docs/content.zh/docs/dev/table/functions/ptfs.md
Original file line number Diff line number Diff line change
Expand Up @@ -2620,6 +2620,97 @@ void testTimerWithState() throws Exception {
{{< /tab >}}
{{< /tabs >}}

#### Testing Updating (Changelog) Tables

A table argument that declares `ArgumentTrait.SUPPORT_UPDATES` receives an updating changelog rather
than an insert-only stream. You can configure the changelog mode of a particular table argument
via `.changelogMode()` `TableArgument.Builder`

When processing elements for a table argument with a specific changelog mode, the harness will
reject any row kind that the mode does not contain.

{{< tabs "changelog-input" >}}
{{< tab "Java" >}}
```java
// A row-semantic PTF that accepts an updating input and echoes each row's kind and value.
@DataTypeHint("ROW<seen STRING>")
public class UpdatingConsumerPTF extends ProcessTableFunction<Row> {
public void eval(
@ArgumentHint({ArgumentTrait.ROW_SEMANTIC_TABLE, ArgumentTrait.SUPPORT_UPDATES}) Row input) {
collect(Row.of(input.getKind() + ":" + input.getFieldAs("value")));
}
}

@Test
void testUpdatingInput() throws Exception {
try (ProcessTableFunctionTestHarness<Row> harness =
ProcessTableFunctionTestHarness.ofClass(UpdatingConsumerPTF.class)
.withTableArgument(
TableArgument.forName("input")
.type(DataTypes.of("ROW<value INT>"))
.changelogMode(ChangelogMode.all())
.build())
.build()) {

harness.processElement(RowKind.INSERT, 10);
harness.processElement(RowKind.UPDATE_AFTER, 20);
harness.processElement(RowKind.DELETE, 10);

List<Row> output = harness.getFunctionOutput();
assertThat(output).containsExactly(
Row.of("INSERT:10"), Row.of("UPDATE_AFTER:20"), Row.of("DELETE:10"));
}
}
```
{{< /tab >}}
{{< /tabs >}}

**Upsert Input**: Upsert changelog modes (`ChangelogMode.upsert(...)`) apply to `SET_SEMANTIC_TABLE`
arguments only. You can configure the upsert key with `.upsertKey()` on `TableArgument.Builder`.
Call it more than once to declare additional candidate keys; the partition columns must match one
of them.

{{< tabs "changelog-upsert" >}}
{{< tab "Java" >}}
```java
@DataTypeHint("ROW<v INT>")
public class UpsertConsumerPTF extends ProcessTableFunction<Row> {
public void eval(
@ArgumentHint({ArgumentTrait.SET_SEMANTIC_TABLE, ArgumentTrait.SUPPORT_UPDATES}) Row input) {
collect(Row.of((Integer) input.getFieldAs("value")));
}
}

@Test
void testUpsertInput() throws Exception {
try (ProcessTableFunctionTestHarness<Row> harness =
ProcessTableFunctionTestHarness.ofClass(UpsertConsumerPTF.class)
.withTableArgument(
TableArgument.forName("input")
.type(DataTypes.of("ROW<key STRING, value INT>"))
.partitionBy("key")
.upsertKey("key")
.changelogMode(ChangelogMode.upsert(true))
.build())
.build()) {

harness.processElement(RowKind.INSERT, "A", 1);
harness.processElement(RowKind.UPDATE_AFTER, "A", 2);

List<Row> output = harness.getFunctionOutput();
assertThat(output).containsExactly(Row.of(1), Row.of(2));
}
}
```
{{< /tab >}}
{{< /tabs >}}

**Output Changelog Mode**: The harness validates collected rows against the PTF's resolved output
changelog mode (derived from `ChangelogFunction`, or insert-only otherwise) and rejects any row
whose kind that mode does not contain.

{{< top >}}

#### Optional Partitioning

For PTFs with `OPTIONAL_PARTITION_BY`, you can omit `.partitionBy(...)` on `TableArgument.Builder`
Expand Down Expand Up @@ -2797,5 +2888,4 @@ void testAtomicOutputFunctionOutput() throws Exception {

### PTF Features Unsupported by the TestHarness

- Update traits (`SUPPORTS_UPDATES`, `REQUIRE_UPDATE_BEFORE`)
- State TTL (state is supported but TTL expiration is not yet implemented)
92 changes: 91 additions & 1 deletion docs/content/docs/dev/table/functions/ptfs.md
Original file line number Diff line number Diff line change
Expand Up @@ -2623,6 +2623,97 @@ void testTimerWithState() throws Exception {
{{< /tab >}}
{{< /tabs >}}

#### Testing Updating (Changelog) Tables

A table argument that declares `ArgumentTrait.SUPPORT_UPDATES` receives an updating changelog rather
than an insert-only stream. You can configure the changelog mode of a particular table argument
via `.changelogMode()` `TableArgument.Builder`

When processing elements for a table argument with a specific changelog mode, the harness will
reject any row kind that the mode does not contain.

{{< tabs "changelog-input" >}}
{{< tab "Java" >}}
```java
// A row-semantic PTF that accepts an updating input and echoes each row's kind and value.
@DataTypeHint("ROW<seen STRING>")
public class UpdatingConsumerPTF extends ProcessTableFunction<Row> {
public void eval(
@ArgumentHint({ArgumentTrait.ROW_SEMANTIC_TABLE, ArgumentTrait.SUPPORT_UPDATES}) Row input) {
collect(Row.of(input.getKind() + ":" + input.getFieldAs("value")));
}
}

@Test
void testUpdatingInput() throws Exception {
try (ProcessTableFunctionTestHarness<Row> harness =
ProcessTableFunctionTestHarness.ofClass(UpdatingConsumerPTF.class)
.withTableArgument(
TableArgument.forName("input")
.type(DataTypes.of("ROW<value INT>"))
.changelogMode(ChangelogMode.all())
.build())
.build()) {

harness.processElement(RowKind.INSERT, 10);
harness.processElement(RowKind.UPDATE_AFTER, 20);
harness.processElement(RowKind.DELETE, 10);

List<Row> output = harness.getFunctionOutput();
assertThat(output).containsExactly(
Row.of("INSERT:10"), Row.of("UPDATE_AFTER:20"), Row.of("DELETE:10"));
}
}
```
{{< /tab >}}
{{< /tabs >}}

**Upsert Input**: Upsert changelog modes (`ChangelogMode.upsert(...)`) apply to `SET_SEMANTIC_TABLE`
arguments only. You can configure the upsert key with `.upsertKey()` on `TableArgument.Builder`.
Call it more than once to declare additional candidate keys; the partition columns must match one
of them.

{{< tabs "changelog-upsert" >}}
{{< tab "Java" >}}
```java
@DataTypeHint("ROW<v INT>")
public class UpsertConsumerPTF extends ProcessTableFunction<Row> {
public void eval(
@ArgumentHint({ArgumentTrait.SET_SEMANTIC_TABLE, ArgumentTrait.SUPPORT_UPDATES}) Row input) {
collect(Row.of((Integer) input.getFieldAs("value")));
}
}

@Test
void testUpsertInput() throws Exception {
try (ProcessTableFunctionTestHarness<Row> harness =
ProcessTableFunctionTestHarness.ofClass(UpsertConsumerPTF.class)
.withTableArgument(
TableArgument.forName("input")
.type(DataTypes.of("ROW<key STRING, value INT>"))
.partitionBy("key")
.upsertKey("key")
.changelogMode(ChangelogMode.upsert(true))
.build())
.build()) {

harness.processElement(RowKind.INSERT, "A", 1);
harness.processElement(RowKind.UPDATE_AFTER, "A", 2);

List<Row> output = harness.getFunctionOutput();
assertThat(output).containsExactly(Row.of(1), Row.of(2));
}
}
```
{{< /tab >}}
{{< /tabs >}}

**Output Changelog Mode**: The harness validates collected rows against the PTF's resolved output
changelog mode (derived from `ChangelogFunction`, or insert-only otherwise) and rejects any row
whose kind that mode does not contain.

{{< top >}}

#### Optional Partitioning

For PTFs with `OPTIONAL_PARTITION_BY`, you can omit `.partitionBy(...)` on `TableArgument.Builder`
Expand Down Expand Up @@ -2800,5 +2891,4 @@ void testAtomicOutputFunctionOutput() throws Exception {

### PTF Features Unsupported by the TestHarness

- Update traits (`SUPPORTS_UPDATES`, `REQUIRE_UPDATE_BEFORE`)
- State TTL (state is supported but TTL expiration is not yet implemented)
Loading