Skip to content

Commit cf98065

Browse files
authored
Core: Make totalRecordCount optional in PartitionStats (#12226)
1 parent 4dbcdfc commit cf98065

File tree

4 files changed

+191
-42
lines changed

4 files changed

+191
-42
lines changed

core/src/main/java/org/apache/iceberg/PartitionStats.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public class PartitionStats implements StructLike {
3333
private int positionDeleteFileCount;
3434
private long equalityDeleteRecordCount;
3535
private int equalityDeleteFileCount;
36-
private long totalRecordCount;
36+
private Long totalRecordCount; // null by default
3737
private Long lastUpdatedAt; // null by default
3838
private Long lastUpdatedSnapshotId; // null by default
3939

@@ -78,7 +78,15 @@ public int equalityDeleteFileCount() {
7878
return equalityDeleteFileCount;
7979
}
8080

81+
/**
82+
* @deprecated since 1.9.0, will be removed in 1.10.0, use {@link #totalRecords()} instead.
83+
*/
84+
@Deprecated
8185
public long totalRecordCount() {
86+
return totalRecordCount == null ? 0L : totalRecordCount;
87+
}
88+
89+
public Long totalRecords() {
8290
return totalRecordCount;
8391
}
8492

@@ -150,7 +158,14 @@ public void appendStats(PartitionStats entry) {
150158
this.positionDeleteFileCount += entry.positionDeleteFileCount;
151159
this.equalityDeleteRecordCount += entry.equalityDeleteRecordCount;
152160
this.equalityDeleteFileCount += entry.equalityDeleteFileCount;
153-
this.totalRecordCount += entry.totalRecordCount;
161+
162+
if (entry.totalRecordCount != null) {
163+
if (totalRecordCount == null) {
164+
this.totalRecordCount = entry.totalRecordCount;
165+
} else {
166+
this.totalRecordCount += entry.totalRecordCount;
167+
}
168+
}
154169

155170
if (entry.lastUpdatedAt != null) {
156171
updateSnapshotInfo(entry.lastUpdatedSnapshotId, entry.lastUpdatedAt);
@@ -236,8 +251,7 @@ public <T> void set(int pos, T value) {
236251
this.equalityDeleteFileCount = value == null ? 0 : (int) value;
237252
break;
238253
case 9:
239-
// optional field as per spec, implementation initialize to 0 for counters
240-
this.totalRecordCount = value == null ? 0L : (long) value;
254+
this.totalRecordCount = (Long) value;
241255
break;
242256
case 10:
243257
this.lastUpdatedAt = (Long) value;
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.iceberg;
20+
21+
import static org.assertj.core.api.Assertions.assertThat;
22+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
23+
24+
import org.apache.iceberg.types.Types;
25+
import org.junit.jupiter.api.Test;
26+
27+
public class TestPartitionStats {
28+
29+
private static final PartitionData PARTITION =
30+
new PartitionData(
31+
Types.StructType.of(Types.NestedField.required(1, "foo", Types.IntegerType.get())));
32+
33+
@Test
34+
public void testAppendWithAllValues() {
35+
PartitionStats stats1 =
36+
createStats(100L, 15, 1000L, 2L, 500, 1L, 200, 15L, 1625077800000L, 12345L);
37+
PartitionStats stats2 = createStats(200L, 7, 500L, 1L, 100, 0L, 50, 7L, 1625077900000L, 12346L);
38+
39+
stats1.appendStats(stats2);
40+
41+
validateStats(stats1, 300L, 22, 1500L, 3L, 600, 1L, 250, 22L, 1625077900000L, 12346L);
42+
}
43+
44+
@Test
45+
public void testAppendWithThisNullOptionalField() {
46+
PartitionStats stats1 = createStats(100L, 15, 1000L, 2L, 500, 1L, 200, null, null, null);
47+
PartitionStats stats2 = createStats(100L, 7, 500L, 1L, 100, 0L, 50, 7L, 1625077900000L, 12346L);
48+
49+
stats1.appendStats(stats2);
50+
51+
validateStats(stats1, 200L, 22, 1500L, 3L, 600, 1L, 250, 7L, 1625077900000L, 12346L);
52+
}
53+
54+
@Test
55+
public void testAppendWithBothNullOptionalFields() {
56+
PartitionStats stats1 = createStats(100L, 15, 1000L, 2L, 500, 1L, 200, null, null, null);
57+
PartitionStats stats2 = createStats(100L, 7, 500L, 1L, 100, 0L, 50, null, null, null);
58+
59+
stats1.appendStats(stats2);
60+
61+
validateStats(stats1, 200L, 22, 1500L, 3L, 600, 1L, 250, null, null, null);
62+
}
63+
64+
@Test
65+
public void testAppendWithOtherNullOptionalFields() {
66+
PartitionStats stats1 =
67+
createStats(100L, 15, 1000L, 2L, 500, 1L, 200, 15L, 1625077900000L, 12346L);
68+
PartitionStats stats2 = createStats(100L, 7, 500L, 1L, 100, 0L, 50, null, null, null);
69+
70+
stats1.appendStats(stats2);
71+
72+
validateStats(stats1, 200L, 22, 1500L, 3L, 600, 1L, 250, 15L, 1625077900000L, 12346L);
73+
}
74+
75+
@Test
76+
public void testAppendEmptyStats() {
77+
PartitionStats stats1 = new PartitionStats(PARTITION, 1);
78+
PartitionStats stats2 = new PartitionStats(PARTITION, 1);
79+
80+
stats1.appendStats(stats2);
81+
82+
validateStats(stats1, 0L, 0, 0L, 0L, 0, 0L, 0, null, null, null);
83+
}
84+
85+
@Test
86+
public void testAppendWithDifferentSpec() {
87+
PartitionStats stats1 = new PartitionStats(PARTITION, 1);
88+
PartitionStats stats2 = new PartitionStats(PARTITION, 2);
89+
90+
assertThatThrownBy(() -> stats1.appendStats(stats2))
91+
.isInstanceOf(IllegalArgumentException.class)
92+
.hasMessage("Spec IDs must match");
93+
}
94+
95+
private PartitionStats createStats(
96+
long dataRecordCount,
97+
int dataFileCount,
98+
long totalDataFileSizeInBytes,
99+
long positionDeleteRecordCount,
100+
int positionDeleteFileCount,
101+
long equalityDeleteRecordCount,
102+
int equalityDeleteFileCount,
103+
Long totalRecordCount,
104+
Long lastUpdatedAt,
105+
Long lastUpdatedSnapshotId) {
106+
107+
PartitionStats stats = new PartitionStats(PARTITION, 1);
108+
stats.set(2, dataRecordCount);
109+
stats.set(3, dataFileCount);
110+
stats.set(4, totalDataFileSizeInBytes);
111+
stats.set(5, positionDeleteRecordCount);
112+
stats.set(6, positionDeleteFileCount);
113+
stats.set(7, equalityDeleteRecordCount);
114+
stats.set(8, equalityDeleteFileCount);
115+
stats.set(9, totalRecordCount);
116+
stats.set(10, lastUpdatedAt);
117+
stats.set(11, lastUpdatedSnapshotId);
118+
119+
return stats;
120+
}
121+
122+
private void validateStats(PartitionStats stats, Object... expectedValues) {
123+
// Spec id and partition data should be unchanged
124+
assertThat(stats.get(0, PartitionData.class)).isEqualTo(PARTITION);
125+
assertThat(stats.get(1, Integer.class)).isEqualTo(1);
126+
127+
for (int i = 0; i < expectedValues.length; i++) {
128+
if (expectedValues[i] == null) {
129+
assertThat(stats.get(i + 2, Object.class)).isNull();
130+
} else {
131+
assertThat(stats.get(i + 2, Object.class)).isEqualTo(expectedValues[i]);
132+
}
133+
}
134+
}
135+
}

0 commit comments

Comments
 (0)