You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by pv...@apache.org on 2023/05/26 09:24:54 UTC

[iceberg] branch master updated: Core: Make all of the BaseFile stat maps immutable (#7643)

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

pvary pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iceberg.git


The following commit(s) were added to refs/heads/master by this push:
     new b69e84b6cf Core: Make all of the BaseFile stat maps immutable (#7643)
b69e84b6cf is described below

commit b69e84b6cf93a330adb31bf4a46d09c98202873d
Author: pvary <pe...@gmail.com>
AuthorDate: Fri May 26 11:24:46 2023 +0200

    Core: Make all of the BaseFile stat maps immutable (#7643)
---
 .../src/main/java/org/apache/iceberg/BaseFile.java | 21 +++++++++---
 .../apache/iceberg/SerializableByteBufferMap.java  | 14 ++++++++
 .../apache/iceberg/TestManifestReaderStats.java    | 37 ++++++++++++++++++++++
 3 files changed, 68 insertions(+), 4 deletions(-)

diff --git a/core/src/main/java/org/apache/iceberg/BaseFile.java b/core/src/main/java/org/apache/iceberg/BaseFile.java
index 3ecf68f9ac..72f7579995 100644
--- a/core/src/main/java/org/apache/iceberg/BaseFile.java
+++ b/core/src/main/java/org/apache/iceberg/BaseFile.java
@@ -21,6 +21,7 @@ package org.apache.iceberg;
 import java.io.Serializable;
 import java.nio.ByteBuffer;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -444,12 +445,12 @@ abstract class BaseFile<F>
 
   @Override
   public Map<Integer, ByteBuffer> lowerBounds() {
-    return toReadableMap(lowerBounds);
+    return toReadableByteBufferMap(lowerBounds);
   }
 
   @Override
   public Map<Integer, ByteBuffer> upperBounds() {
-    return toReadableMap(upperBounds);
+    return toReadableByteBufferMap(upperBounds);
   }
 
   @Override
@@ -473,10 +474,22 @@ abstract class BaseFile<F>
   }
 
   private static <K, V> Map<K, V> toReadableMap(Map<K, V> map) {
-    if (map instanceof SerializableMap) {
+    if (map == null) {
+      return null;
+    } else if (map instanceof SerializableMap) {
       return ((SerializableMap<K, V>) map).immutableMap();
     } else {
-      return map;
+      return Collections.unmodifiableMap(map);
+    }
+  }
+
+  private static Map<Integer, ByteBuffer> toReadableByteBufferMap(Map<Integer, ByteBuffer> map) {
+    if (map == null) {
+      return null;
+    } else if (map instanceof SerializableByteBufferMap) {
+      return ((SerializableByteBufferMap) map).immutableMap();
+    } else {
+      return Collections.unmodifiableMap(map);
     }
   }
 
diff --git a/core/src/main/java/org/apache/iceberg/SerializableByteBufferMap.java b/core/src/main/java/org/apache/iceberg/SerializableByteBufferMap.java
index 3aabefa23b..a8bf61e9ee 100644
--- a/core/src/main/java/org/apache/iceberg/SerializableByteBufferMap.java
+++ b/core/src/main/java/org/apache/iceberg/SerializableByteBufferMap.java
@@ -22,6 +22,7 @@ import java.io.ObjectStreamException;
 import java.io.Serializable;
 import java.nio.ByteBuffer;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 import org.apache.iceberg.relocated.com.google.common.collect.Maps;
@@ -29,6 +30,7 @@ import org.apache.iceberg.util.ByteBuffers;
 
 class SerializableByteBufferMap implements Map<Integer, ByteBuffer>, Serializable {
   private final Map<Integer, ByteBuffer> wrapped;
+  private transient volatile Map<Integer, ByteBuffer> immutableMap;
 
   static Map<Integer, ByteBuffer> wrap(Map<Integer, ByteBuffer> map) {
     if (map == null) {
@@ -88,6 +90,18 @@ class SerializableByteBufferMap implements Map<Integer, ByteBuffer>, Serializabl
     return new MapSerializationProxy(keys, values);
   }
 
+  public Map<Integer, ByteBuffer> immutableMap() {
+    if (immutableMap == null) {
+      synchronized (this) {
+        if (immutableMap == null) {
+          immutableMap = Collections.unmodifiableMap(wrapped);
+        }
+      }
+    }
+
+    return immutableMap;
+  }
+
   @Override
   public int size() {
     return wrapped.size();
diff --git a/core/src/test/java/org/apache/iceberg/TestManifestReaderStats.java b/core/src/test/java/org/apache/iceberg/TestManifestReaderStats.java
index 53ec3c40cc..e794539d6d 100644
--- a/core/src/test/java/org/apache/iceberg/TestManifestReaderStats.java
+++ b/core/src/test/java/org/apache/iceberg/TestManifestReaderStats.java
@@ -27,6 +27,7 @@ import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
 import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
 import org.apache.iceberg.types.Conversions;
 import org.apache.iceberg.types.Types;
+import org.assertj.core.api.Assertions;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -220,6 +221,42 @@ public class TestManifestReaderStats extends TableTestBase {
     Assert.assertEquals(LOWER_BOUNDS, dataFile.lowerBounds());
     Assert.assertEquals(UPPER_BOUNDS, dataFile.upperBounds());
 
+    if (dataFile.valueCounts() != null) {
+      Assertions.assertThatThrownBy(
+              () -> dataFile.valueCounts().clear(), "Should not be modifiable")
+          .isInstanceOf(UnsupportedOperationException.class);
+    }
+
+    if (dataFile.nullValueCounts() != null) {
+      Assertions.assertThatThrownBy(
+              () -> dataFile.nullValueCounts().clear(), "Should not be modifiable")
+          .isInstanceOf(UnsupportedOperationException.class);
+    }
+
+    if (dataFile.nanValueCounts() != null) {
+      Assertions.assertThatThrownBy(
+              () -> dataFile.nanValueCounts().clear(), "Should not be modifiable")
+          .isInstanceOf(UnsupportedOperationException.class);
+    }
+
+    if (dataFile.upperBounds() != null) {
+      Assertions.assertThatThrownBy(
+              () -> dataFile.upperBounds().clear(), "Should not be modifiable")
+          .isInstanceOf(UnsupportedOperationException.class);
+    }
+
+    if (dataFile.lowerBounds() != null) {
+      Assertions.assertThatThrownBy(
+              () -> dataFile.lowerBounds().clear(), "Should not be modifiable")
+          .isInstanceOf(UnsupportedOperationException.class);
+    }
+
+    if (dataFile.columnSizes() != null) {
+      Assertions.assertThatThrownBy(
+              () -> dataFile.columnSizes().clear(), "Should not be modifiable")
+          .isInstanceOf(UnsupportedOperationException.class);
+    }
+
     Assert.assertEquals(FILE_PATH, dataFile.path()); // always select file path in all test cases
   }