This is an automated email from the ASF dual-hosted git repository.
JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new e103dd2c40 [Bug] Reject partially overlapping sequence-group ordering
fields (#10513)
e103dd2c40 is described below
commit e103dd2c40187d927a7215eea5d37494b92171fe
Author: Ran Tao <[email protected]>
AuthorDate: Tue Oct 13 13:30:40 2026 +0800
[Bug] Reject partially overlapping sequence-group ordering fields (#10513)
---
.../merge-engine/partial-update.md | 2 ++
.../compact/PartialUpdateMergeFunction.java | 14 +++++++++++++
.../apache/paimon/schema/SchemaValidationTest.java | 24 ++++++++++++++++++++++
3 files changed, 40 insertions(+)
diff --git a/docs/docs/primary-key-table/merge-engine/partial-update.md
b/docs/docs/primary-key-table/merge-engine/partial-update.md
index 2299de1c1d..491b85c221 100644
--- a/docs/docs/primary-key-table/merge-engine/partial-update.md
+++ b/docs/docs/primary-key-table/merge-engine/partial-update.md
@@ -73,6 +73,8 @@ independent streams update different columns, give each
stream its own sequence
stream's version does not determine whether the other stream's update is
accepted.
Configure `fields.<ordering-field>.sequence-group` with the columns that the
version protects.
+Ordering fields cannot be shared by different sequence groups. If multiple
fields use the same
+ordering tuple, list those protected value fields in one group.
This example separates profile updates from score updates:
```sql
diff --git
a/paimon-core/src/main/java/org/apache/paimon/mergetree/compact/PartialUpdateMergeFunction.java
b/paimon-core/src/main/java/org/apache/paimon/mergetree/compact/PartialUpdateMergeFunction.java
index 467524ee11..f40bcde8a3 100644
---
a/paimon-core/src/main/java/org/apache/paimon/mergetree/compact/PartialUpdateMergeFunction.java
+++
b/paimon-core/src/main/java/org/apache/paimon/mergetree/compact/PartialUpdateMergeFunction.java
@@ -436,6 +436,7 @@ public class PartialUpdateMergeFunction implements
MergeFunction<KeyValue> {
List<String> fieldNames = rowType.getFieldNames();
this.fieldSeqComparators = new HashMap<>();
Map<String, Integer> sequenceGroupMap = new HashMap<>();
+ Map<String, String> sequenceGroupByOrderingField = new HashMap<>();
List<String> allSequenceFields = new ArrayList<>();
List<String> fieldsProtectedBySequenceGroup = new ArrayList<>();
for (Map.Entry<String, String> entry : options.toMap().entrySet())
{
@@ -447,6 +448,19 @@ public class PartialUpdateMergeFunction implements
MergeFunction<KeyValue> {
.mapToInt(fieldName ->
requireField(fieldName, fieldNames))
.toArray();
+ for (int field : sequenceFields) {
+ String fieldName = fieldNames.get(field);
+ String previousGroup =
+
sequenceGroupByOrderingField.putIfAbsent(fieldName, k);
+ checkArgument(
+ previousGroup == null ||
previousGroup.equals(k),
+ "Sequence field '%s' is shared by sequence
groups '%s' and '%s'. "
+ + "Ordering fields must not overlap
between sequence groups.",
+ fieldName,
+ previousGroup,
+ k);
+ }
+
Supplier<FieldsComparator> userDefinedSeqComparator =
() -> UserDefinedSeqComparator.create(rowType,
sequenceFields, true);
sequenceGroupProtectedFields(v).stream()
diff --git
a/paimon-core/src/test/java/org/apache/paimon/schema/SchemaValidationTest.java
b/paimon-core/src/test/java/org/apache/paimon/schema/SchemaValidationTest.java
index 681edbf1da..8c4ac570af 100644
---
a/paimon-core/src/test/java/org/apache/paimon/schema/SchemaValidationTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/schema/SchemaValidationTest.java
@@ -700,6 +700,30 @@ class SchemaValidationTest {
.hasMessageContaining("Invalid sequence-group option:
fields.ts.xsequence-group");
}
+ @Test
+ public void
testPartialUpdateRejectsOverlappingSequenceGroupOrderingFields() {
+ List<DataField> fields =
+ Arrays.asList(
+ new DataField(0, "id", DataTypes.INT()),
+ new DataField(1, "a", DataTypes.INT()),
+ new DataField(2, "b", DataTypes.INT()),
+ new DataField(3, "s", DataTypes.INT()),
+ new DataField(4, "t", DataTypes.INT()));
+ Map<String, String> options = new HashMap<>();
+ options.put(BUCKET.key(), "1");
+ options.put(CoreOptions.MERGE_ENGINE.key(), "partial-update");
+ options.put("fields.s.sequence-group", "a");
+ options.put("fields.t,s.sequence-group", "b");
+ TableSchema schema =
+ new TableSchema(1, fields, 10, emptyList(),
singletonList("id"), options, "");
+
+ assertThatThrownBy(() -> validateTableSchema(schema))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("Sequence field 's' is shared by
sequence groups")
+ .hasMessageContaining("fields.s.sequence-group")
+ .hasMessageContaining("fields.t,s.sequence-group");
+ }
+
@Test
public void testPrimaryKeyBlobViewCoexistsWithManagedBlob() {
List<DataField> fields =