This is an automated email from the ASF dual-hosted git repository.

lzljs3620320 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink-table-store.git


The following commit(s) were added to refs/heads/master by this push:
     new 8e63f1f6 [FLINK-29554] Add partial-update.ignore-delete option to 
avoid exception after join
8e63f1f6 is described below

commit 8e63f1f67fcfe83773a7264068687c7340bc01cb
Author: Jingsong Lee <[email protected]>
AuthorDate: Thu Oct 13 19:52:38 2022 +0800

    [FLINK-29554] Add partial-update.ignore-delete option to avoid exception 
after join
    
    This closes #311
---
 .../shortcodes/generated/core_configuration.html   |  6 +++
 .../table/store/connector/PartialUpdateITCase.java | 57 ++++++++++++++++++++--
 .../org/apache/flink/table/store/CoreOptions.java  |  6 +++
 .../compact/PartialUpdateMergeFunction.java        | 37 +++++++++-----
 .../table/ChangelogWithKeyFileStoreTable.java      |  3 +-
 .../table/ChangelogWithKeyFileStoreTableTest.java  | 25 ++++++++++
 6 files changed, 119 insertions(+), 15 deletions(-)

diff --git a/docs/layouts/shortcodes/generated/core_configuration.html 
b/docs/layouts/shortcodes/generated/core_configuration.html
index 81aa677d..6e6da2bd 100644
--- a/docs/layouts/shortcodes/generated/core_configuration.html
+++ b/docs/layouts/shortcodes/generated/core_configuration.html
@@ -170,6 +170,12 @@
             <td>MemorySize</td>
             <td>Memory page size.</td>
         </tr>
+        <tr>
+            <td><h5>partial-update.ignore-delete</h5></td>
+            <td style="word-wrap: break-word;">false</td>
+            <td>Boolean</td>
+            <td>Whether to ignore delete records in partial-update mode.</td>
+        </tr>
         <tr>
             <td><h5>partition.default-name</h5></td>
             <td style="word-wrap: break-word;">"__DEFAULT_PARTITION__"</td>
diff --git 
a/flink-table-store-connector/src/test/java/org/apache/flink/table/store/connector/PartialUpdateITCase.java
 
b/flink-table-store-connector/src/test/java/org/apache/flink/table/store/connector/PartialUpdateITCase.java
index 2ec08e17..bd36bd55 100644
--- 
a/flink-table-store-connector/src/test/java/org/apache/flink/table/store/connector/PartialUpdateITCase.java
+++ 
b/flink-table-store-connector/src/test/java/org/apache/flink/table/store/connector/PartialUpdateITCase.java
@@ -18,12 +18,17 @@
 
 package org.apache.flink.table.store.connector;
 
+import org.apache.flink.table.api.config.ExecutionConfigOptions;
 import org.apache.flink.types.Row;
+import org.apache.flink.types.RowKind;
+import org.apache.flink.util.CloseableIterator;
 
 import org.junit.Test;
 
-import java.util.Collections;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -33,10 +38,15 @@ public class PartialUpdateITCase extends 
FileStoreTableITCase {
 
     @Override
     protected List<String> ddl() {
-        return Collections.singletonList(
+        return Arrays.asList(
                 "CREATE TABLE IF NOT EXISTS T ("
                         + "j INT, k INT, a INT, b INT, c STRING, PRIMARY KEY 
(j,k) NOT ENFORCED)"
-                        + " WITH ('merge-engine'='partial-update');");
+                        + " WITH ('merge-engine'='partial-update');",
+                "CREATE TABLE IF NOT EXISTS dwd_orders ("
+                        + "OrderID INT, OrderNumber INT, PersonID INT, 
LastName STRING, FirstName STRING, Age INT, PRIMARY KEY (OrderID) NOT ENFORCED)"
+                        + " WITH ('merge-engine'='partial-update', 
'partial-update.ignore-delete'='true');",
+                "CREATE TABLE IF NOT EXISTS ods_orders (OrderID INT, 
OrderNumber INT, PersonID INT, PRIMARY KEY (OrderID) NOT ENFORCED) WITH 
('changelog-producer'='input');",
+                "CREATE TABLE IF NOT EXISTS dim_persons (PersonID INT, 
LastName STRING, FirstName STRING, Age INT, PRIMARY KEY (PersonID) NOT 
ENFORCED) WITH ('changelog-producer'='input');");
     }
 
     @Test
@@ -78,6 +88,47 @@ public class PartialUpdateITCase extends 
FileStoreTableITCase {
                 .containsExactlyInAnyOrder(Row.of(1, 2, 4, 5, "6"), Row.of(1, 
3, 2, 4, "1"));
     }
 
+    @Test
+    public void testForeignKeyJoin() throws Exception {
+        sEnv.getConfig()
+                .set(
+                        
ExecutionConfigOptions.TABLE_EXEC_SINK_UPSERT_MATERIALIZE,
+                        ExecutionConfigOptions.UpsertMaterialize.NONE);
+        CloseableIterator<Row> iter =
+                streamSqlIter(
+                        "INSERT INTO dwd_orders "
+                                + "SELECT OrderID, OrderNumber, PersonID, 
CAST(NULL AS STRING), CAST(NULL AS STRING), CAST(NULL AS INT) FROM ods_orders "
+                                + "UNION ALL "
+                                + "SELECT OrderID, CAST(NULL AS INT), 
dim_persons.PersonID, LastName, FirstName, Age FROM dim_persons JOIN ods_orders 
ON dim_persons.PersonID = ods_orders.PersonID;");
+
+        batchSql("INSERT INTO ods_orders VALUES (1, 2, 3)");
+        batchSql("INSERT INTO dim_persons VALUES (3, 'snow', 'jon', 23)");
+        Thread.sleep(1000);
+        assertThat(rowsToList(batchSql("SELECT * FROM dwd_orders")))
+                .containsExactly(Arrays.asList(1, 2, 3, "snow", "jon", 23));
+
+        batchSql("INSERT INTO ods_orders VALUES (1, 4, 3)");
+        batchSql("INSERT INTO dim_persons VALUES (3, 'snow', 'targaryen', 
23)");
+        Thread.sleep(1000);
+        assertThat(rowsToList(batchSql("SELECT * FROM dwd_orders")))
+                .containsExactly(Arrays.asList(1, 4, 3, "snow", "targaryen", 
23));
+
+        iter.close();
+    }
+
+    private List<List<Object>> rowsToList(List<Row> rows) {
+        return rows.stream().map(this::toList).collect(Collectors.toList());
+    }
+
+    private List<Object> toList(Row row) {
+        assertThat(row.getKind()).isIn(RowKind.INSERT, RowKind.UPDATE_AFTER);
+        List<Object> result = new ArrayList<>();
+        for (int i = 0; i < row.getArity(); i++) {
+            result.add(row.getField(i));
+        }
+        return result;
+    }
+
     @Test
     public void testStreamingRead() {
         assertThatThrownBy(
diff --git 
a/flink-table-store-core/src/main/java/org/apache/flink/table/store/CoreOptions.java
 
b/flink-table-store-core/src/main/java/org/apache/flink/table/store/CoreOptions.java
index 72bce71a..de190ccf 100644
--- 
a/flink-table-store-core/src/main/java/org/apache/flink/table/store/CoreOptions.java
+++ 
b/flink-table-store-core/src/main/java/org/apache/flink/table/store/CoreOptions.java
@@ -149,6 +149,12 @@ public class CoreOptions implements Serializable {
                     .defaultValue(MergeEngine.DEDUPLICATE)
                     .withDescription("Specify the merge engine for table with 
primary key.");
 
+    public static final ConfigOption<Boolean> PARTIAL_UPDATE_IGNORE_DELETE =
+            ConfigOptions.key("partial-update.ignore-delete")
+                    .booleanType()
+                    .defaultValue(false)
+                    .withDescription("Whether to ignore delete records in 
partial-update mode.");
+
     @Immutable
     public static final ConfigOption<WriteMode> WRITE_MODE =
             ConfigOptions.key("write-mode")
diff --git 
a/flink-table-store-core/src/main/java/org/apache/flink/table/store/file/mergetree/compact/PartialUpdateMergeFunction.java
 
b/flink-table-store-core/src/main/java/org/apache/flink/table/store/file/mergetree/compact/PartialUpdateMergeFunction.java
index ed3a2bcc..fe9049bc 100644
--- 
a/flink-table-store-core/src/main/java/org/apache/flink/table/store/file/mergetree/compact/PartialUpdateMergeFunction.java
+++ 
b/flink-table-store-core/src/main/java/org/apache/flink/table/store/file/mergetree/compact/PartialUpdateMergeFunction.java
@@ -25,9 +25,6 @@ import org.apache.flink.types.RowKind;
 
 import javax.annotation.Nullable;
 
-import static org.apache.flink.util.Preconditions.checkArgument;
-import static org.apache.flink.util.Preconditions.checkNotNull;
-
 /**
  * A {@link MergeFunction} where key is primary key (unique) and value is the 
partial record, update
  * non-null fields on merge.
@@ -37,13 +34,15 @@ public class PartialUpdateMergeFunction implements 
MergeFunction<KeyValue> {
     private static final long serialVersionUID = 1L;
 
     private final RowData.FieldGetter[] getters;
+    private final boolean ignoreDelete;
 
     private transient KeyValue latestKv;
     private transient GenericRowData row;
     private transient KeyValue reused;
 
-    public PartialUpdateMergeFunction(RowData.FieldGetter[] getters) {
+    public PartialUpdateMergeFunction(RowData.FieldGetter[] getters, boolean 
ignoreDelete) {
         this.getters = getters;
+        this.ignoreDelete = ignoreDelete;
     }
 
     @Override
@@ -54,9 +53,20 @@ public class PartialUpdateMergeFunction implements 
MergeFunction<KeyValue> {
 
     @Override
     public void add(KeyValue kv) {
-        checkArgument(
-                kv.valueKind() == RowKind.INSERT || kv.valueKind() == 
RowKind.UPDATE_AFTER,
-                "Partial update can not accept delete records. Partial delete 
is not supported!");
+        if (kv.valueKind() == RowKind.UPDATE_BEFORE || kv.valueKind() == 
RowKind.DELETE) {
+            if (ignoreDelete) {
+                return;
+            }
+
+            if (kv.valueKind() == RowKind.UPDATE_BEFORE) {
+                throw new IllegalArgumentException(
+                        "Partial update can not accept update_before records, 
it is a bug.");
+            }
+
+            throw new IllegalArgumentException(
+                    "Partial update can not accept delete records. Partial 
delete is not supported!");
+        }
+
         latestKv = kv;
         for (int i = 0; i < getters.length; i++) {
             Object field = getters[i].getFieldOrNull(kv.value());
@@ -69,9 +79,14 @@ public class PartialUpdateMergeFunction implements 
MergeFunction<KeyValue> {
     @Override
     @Nullable
     public KeyValue getResult() {
-        checkNotNull(
-                latestKv,
-                "Trying to get result from merge function without any input. 
This is unexpected.");
+        if (latestKv == null) {
+            if (ignoreDelete) {
+                return null;
+            }
+
+            throw new IllegalArgumentException(
+                    "Trying to get result from merge function without any 
input. This is unexpected.");
+        }
 
         if (reused == null) {
             reused = new KeyValue();
@@ -82,6 +97,6 @@ public class PartialUpdateMergeFunction implements 
MergeFunction<KeyValue> {
     @Override
     public MergeFunction<KeyValue> copy() {
         // RowData.FieldGetter is thread safe
-        return new PartialUpdateMergeFunction(getters);
+        return new PartialUpdateMergeFunction(getters, ignoreDelete);
     }
 }
diff --git 
a/flink-table-store-core/src/main/java/org/apache/flink/table/store/table/ChangelogWithKeyFileStoreTable.java
 
b/flink-table-store-core/src/main/java/org/apache/flink/table/store/table/ChangelogWithKeyFileStoreTable.java
index 18f44f75..ed464435 100644
--- 
a/flink-table-store-core/src/main/java/org/apache/flink/table/store/table/ChangelogWithKeyFileStoreTable.java
+++ 
b/flink-table-store-core/src/main/java/org/apache/flink/table/store/table/ChangelogWithKeyFileStoreTable.java
@@ -86,7 +86,8 @@ public class ChangelogWithKeyFileStoreTable extends 
AbstractFileStoreTable {
                 mergeFunction = new DeduplicateMergeFunction();
                 break;
             case PARTIAL_UPDATE:
-                mergeFunction = new PartialUpdateMergeFunction(fieldGetters);
+                boolean ignoreDelete = 
conf.get(CoreOptions.PARTIAL_UPDATE_IGNORE_DELETE);
+                mergeFunction = new PartialUpdateMergeFunction(fieldGetters, 
ignoreDelete);
                 break;
             case AGGREGATE:
                 List<String> fieldNames = tableSchema.fieldNames();
diff --git 
a/flink-table-store-core/src/test/java/org/apache/flink/table/store/table/ChangelogWithKeyFileStoreTableTest.java
 
b/flink-table-store-core/src/test/java/org/apache/flink/table/store/table/ChangelogWithKeyFileStoreTableTest.java
index 3f4e908d..598269bc 100644
--- 
a/flink-table-store-core/src/test/java/org/apache/flink/table/store/table/ChangelogWithKeyFileStoreTableTest.java
+++ 
b/flink-table-store-core/src/test/java/org/apache/flink/table/store/table/ChangelogWithKeyFileStoreTableTest.java
@@ -283,6 +283,31 @@ public class ChangelogWithKeyFileStoreTableTest extends 
FileStoreTableTestBase {
                                 "1|60|500|binary|varbinary"));
     }
 
+    @Test
+    public void testPartialUpdateIgnoreDelete() throws Exception {
+        FileStoreTable table =
+                createFileStoreTable(
+                        conf -> {
+                            conf.set(
+                                    CoreOptions.MERGE_ENGINE,
+                                    CoreOptions.MergeEngine.PARTIAL_UPDATE);
+                            conf.set(CoreOptions.PARTIAL_UPDATE_IGNORE_DELETE, 
true);
+                        });
+        TableWrite write = table.newWrite();
+        TableCommit commit = table.newCommit("user");
+        write.write(rowData(1, 10, 200L));
+        write.write(rowDataWithKind(RowKind.DELETE, 1, 10, 100L));
+        write.write(rowDataWithKind(RowKind.UPDATE_BEFORE, 1, 10, 100L));
+        write.write(rowDataWithKind(RowKind.DELETE, 1, 20, 400L));
+        commit.commit("0", write.prepareCommit(true));
+        write.close();
+
+        List<Split> splits = table.newScan().plan().splits;
+        TableRead read = table.newRead();
+        assertThat(getResult(read, splits, binaryRow(1), 0, 
BATCH_ROW_TO_STRING))
+                
.isEqualTo(Collections.singletonList("1|10|200|binary|varbinary"));
+    }
+
     @Override
     protected FileStoreTable createFileStoreTable(Consumer<Configuration> 
configure)
             throws Exception {

Reply via email to