Add statistic compatibility (#382)

* add statistic compatibility

(cherry picked from commit 03c229d21de4322de36c292e3fa411cb1b856500)
(cherry picked from commit c97ec476c812bfa9d0bacc11f33c6077269adc3c)

* fix test

(cherry picked from commit 92a06d16863f90eab61d270b9bd5d7e4d1e89cf1)

* add column compatibility

(cherry picked from commit 8dd5f1c18b0b0c0e0268ea3fe04f5c9ed953058c)

* Add exception
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/BinaryStatistics.java b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/BinaryStatistics.java
index 34db11b..9915121 100644
--- a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/BinaryStatistics.java
+++ b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/BinaryStatistics.java
@@ -125,18 +125,22 @@
         String.format(STATS_UNSUPPORTED_MSG, TSDataType.TEXT, "long sum"));
   }
 
+  @SuppressWarnings("rawtypes")
   @Override
-  protected void mergeStatisticsValue(Statistics<Binary> stats) {
-    BinaryStatistics stringStats = (BinaryStatistics) stats;
-    if (isEmpty) {
-      initializeStats(stringStats.getFirstValue(), stringStats.getLastValue());
-      isEmpty = false;
+  protected void mergeStatisticsValue(Statistics stats) {
+    if (stats instanceof BinaryStatistics || stats instanceof StringStatistics) {
+      if (isEmpty) {
+        initializeStats(((Binary) stats.getFirstValue()), ((Binary) stats.getLastValue()));
+        isEmpty = false;
+      } else {
+        updateStats(
+            ((Binary) stats.getFirstValue()),
+            ((Binary) stats.getLastValue()),
+            stats.getStartTime(),
+            stats.getEndTime());
+      }
     } else {
-      updateStats(
-          stringStats.getFirstValue(),
-          stringStats.getLastValue(),
-          stats.getStartTime(),
-          stats.getEndTime());
+      throw new StatisticsClassException(this.getClass(), stats.getClass());
     }
   }
 
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/BlobStatistics.java b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/BlobStatistics.java
index f8cb18f..65c7515 100644
--- a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/BlobStatistics.java
+++ b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/BlobStatistics.java
@@ -110,8 +110,9 @@
         String.format(STATS_UNSUPPORTED_MSG, TSDataType.BLOB, "sum"));
   }
 
+  @SuppressWarnings("rawtypes")
   @Override
-  protected void mergeStatisticsValue(Statistics<Binary> stats) {
+  protected void mergeStatisticsValue(Statistics stats) {
     // do nothing
     if (isEmpty) {
       isEmpty = false;
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/BooleanStatistics.java b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/BooleanStatistics.java
index 63e0128..d519a6c 100644
--- a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/BooleanStatistics.java
+++ b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/BooleanStatistics.java
@@ -127,8 +127,7 @@
 
   @Override
   public double getSumDoubleValue() {
-    throw new StatisticsClassException(
-        String.format(STATS_UNSUPPORTED_MSG, TSDataType.BOOLEAN, "double sum"));
+    return sumValue;
   }
 
   @Override
@@ -137,18 +136,22 @@
   }
 
   @Override
-  protected void mergeStatisticsValue(Statistics<Boolean> stats) {
-    BooleanStatistics boolStats = (BooleanStatistics) stats;
-    if (isEmpty) {
-      initializeStats(boolStats.getFirstValue(), boolStats.getLastValue(), boolStats.sumValue);
-      isEmpty = false;
+  protected void mergeStatisticsValue(Statistics stats) {
+    if (stats instanceof BooleanStatistics) {
+      BooleanStatistics boolStats = (BooleanStatistics) stats;
+      if (isEmpty) {
+        initializeStats(boolStats.getFirstValue(), boolStats.getLastValue(), boolStats.sumValue);
+        isEmpty = false;
+      } else {
+        updateStats(
+            boolStats.getFirstValue(),
+            boolStats.getLastValue(),
+            stats.getStartTime(),
+            stats.getEndTime(),
+            boolStats.sumValue);
+      }
     } else {
-      updateStats(
-          boolStats.getFirstValue(),
-          boolStats.getLastValue(),
-          stats.getStartTime(),
-          stats.getEndTime(),
-          boolStats.sumValue);
+      throw new StatisticsClassException(this.getClass(), stats.getClass());
     }
   }
 
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/DoubleStatistics.java b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/DoubleStatistics.java
index d79c4c2..1d259ca 100644
--- a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/DoubleStatistics.java
+++ b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/DoubleStatistics.java
@@ -159,26 +159,33 @@
         String.format(STATS_UNSUPPORTED_MSG, TSDataType.DOUBLE, "long sum"));
   }
 
+  @SuppressWarnings("rawtypes")
   @Override
-  protected void mergeStatisticsValue(Statistics<Double> stats) {
-    DoubleStatistics doubleStats = (DoubleStatistics) stats;
-    if (this.isEmpty) {
-      initializeStats(
-          doubleStats.getMinValue(),
-          doubleStats.getMaxValue(),
-          doubleStats.getFirstValue(),
-          doubleStats.getLastValue(),
-          doubleStats.sumValue);
-      isEmpty = false;
+  protected void mergeStatisticsValue(Statistics stats) {
+    if (stats instanceof DoubleStatistics
+        || stats instanceof FloatStatistics
+        || stats instanceof IntegerStatistics
+        || stats instanceof LongStatistics) {
+      if (this.isEmpty) {
+        initializeStats(
+            ((Number) stats.getMinValue()).doubleValue(),
+            ((Number) stats.getMaxValue()).doubleValue(),
+            ((Number) stats.getFirstValue()).doubleValue(),
+            ((Number) stats.getLastValue()).doubleValue(),
+            stats.getSumDoubleValue());
+        isEmpty = false;
+      } else {
+        updateStats(
+            ((Number) stats.getMinValue()).doubleValue(),
+            ((Number) stats.getMaxValue()).doubleValue(),
+            ((Number) stats.getFirstValue()).doubleValue(),
+            ((Number) stats.getLastValue()).doubleValue(),
+            stats.getSumDoubleValue(),
+            stats.getStartTime(),
+            stats.getEndTime());
+      }
     } else {
-      updateStats(
-          doubleStats.getMinValue(),
-          doubleStats.getMaxValue(),
-          doubleStats.getFirstValue(),
-          doubleStats.getLastValue(),
-          doubleStats.sumValue,
-          stats.getStartTime(),
-          stats.getEndTime());
+      throw new StatisticsClassException(this.getClass(), stats.getClass());
     }
   }
 
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/FloatStatistics.java b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/FloatStatistics.java
index 8df5200..0d9595a 100644
--- a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/FloatStatistics.java
+++ b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/FloatStatistics.java
@@ -150,26 +150,30 @@
         String.format(STATS_UNSUPPORTED_MSG, TSDataType.FLOAT, "long sum"));
   }
 
+  @SuppressWarnings("rawtypes")
   @Override
-  protected void mergeStatisticsValue(Statistics<Float> stats) {
-    FloatStatistics floatStats = (FloatStatistics) stats;
-    if (isEmpty) {
-      initializeStats(
-          floatStats.getMinValue(),
-          floatStats.getMaxValue(),
-          floatStats.getFirstValue(),
-          floatStats.getLastValue(),
-          floatStats.sumValue);
-      isEmpty = false;
+  protected void mergeStatisticsValue(Statistics stats) {
+    if (stats instanceof FloatStatistics || stats instanceof IntegerStatistics) {
+      if (isEmpty) {
+        initializeStats(
+            ((Number) stats.getMinValue()).floatValue(),
+            ((Number) stats.getMaxValue()).floatValue(),
+            ((Number) stats.getFirstValue()).floatValue(),
+            ((Number) stats.getLastValue()).floatValue(),
+            stats.getSumDoubleValue());
+        isEmpty = false;
+      } else {
+        updateStats(
+            ((Number) stats.getMinValue()).floatValue(),
+            ((Number) stats.getMaxValue()).floatValue(),
+            ((Number) stats.getFirstValue()).floatValue(),
+            ((Number) stats.getLastValue()).floatValue(),
+            stats.getSumDoubleValue(),
+            stats.getStartTime(),
+            stats.getEndTime());
+      }
     } else {
-      updateStats(
-          floatStats.getMinValue(),
-          floatStats.getMaxValue(),
-          floatStats.getFirstValue(),
-          floatStats.getLastValue(),
-          floatStats.sumValue,
-          stats.getStartTime(),
-          stats.getEndTime());
+      throw new StatisticsClassException(this.getClass(), stats.getClass());
     }
   }
 
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/IntegerStatistics.java b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/IntegerStatistics.java
index 45f1716..22db0aa 100644
--- a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/IntegerStatistics.java
+++ b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/IntegerStatistics.java
@@ -142,8 +142,7 @@
 
   @Override
   public double getSumDoubleValue() {
-    throw new StatisticsClassException(
-        String.format(STATS_UNSUPPORTED_MSG, TSDataType.INT32, "double sum"));
+    return sumValue;
   }
 
   @Override
@@ -151,26 +150,31 @@
     return sumValue;
   }
 
+  @SuppressWarnings("rawtypes")
   @Override
-  protected void mergeStatisticsValue(Statistics<Integer> stats) {
-    IntegerStatistics intStats = (IntegerStatistics) stats;
-    if (isEmpty) {
-      initializeStats(
-          intStats.getMinValue(),
-          intStats.getMaxValue(),
-          intStats.getFirstValue(),
-          intStats.getLastValue(),
-          intStats.sumValue);
-      isEmpty = false;
+  protected void mergeStatisticsValue(Statistics stats) {
+    if (stats instanceof IntegerStatistics) {
+      IntegerStatistics intStats = (IntegerStatistics) stats;
+      if (isEmpty) {
+        initializeStats(
+            intStats.getMinValue(),
+            intStats.getMaxValue(),
+            intStats.getFirstValue(),
+            intStats.getLastValue(),
+            intStats.sumValue);
+        isEmpty = false;
+      } else {
+        updateStats(
+            intStats.getMinValue(),
+            intStats.getMaxValue(),
+            intStats.getFirstValue(),
+            intStats.getLastValue(),
+            intStats.sumValue,
+            stats.getStartTime(),
+            stats.getEndTime());
+      }
     } else {
-      updateStats(
-          intStats.getMinValue(),
-          intStats.getMaxValue(),
-          intStats.getFirstValue(),
-          intStats.getLastValue(),
-          intStats.sumValue,
-          stats.getStartTime(),
-          stats.getEndTime());
+      throw new StatisticsClassException(this.getClass(), stats.getClass());
     }
   }
 
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/LongStatistics.java b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/LongStatistics.java
index c72668c..5f36242 100644
--- a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/LongStatistics.java
+++ b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/LongStatistics.java
@@ -160,26 +160,30 @@
     }
   }
 
+  @SuppressWarnings("rawtypes")
   @Override
-  protected void mergeStatisticsValue(Statistics<Long> stats) {
-    LongStatistics longStats = (LongStatistics) stats;
-    if (isEmpty) {
-      initializeStats(
-          longStats.getMinValue(),
-          longStats.getMaxValue(),
-          longStats.getFirstValue(),
-          longStats.getLastValue(),
-          longStats.sumValue);
-      isEmpty = false;
+  protected void mergeStatisticsValue(Statistics stats) {
+    if (stats instanceof LongStatistics || stats instanceof IntegerStatistics) {
+      if (isEmpty) {
+        initializeStats(
+            ((Number) stats.getMinValue()).longValue(),
+            ((Number) stats.getMaxValue()).longValue(),
+            ((Number) stats.getFirstValue()).longValue(),
+            ((Number) stats.getLastValue()).longValue(),
+            stats.getSumDoubleValue());
+        isEmpty = false;
+      } else {
+        updateStats(
+            ((Number) stats.getMinValue()).longValue(),
+            ((Number) stats.getMaxValue()).longValue(),
+            ((Number) stats.getFirstValue()).longValue(),
+            ((Number) stats.getLastValue()).longValue(),
+            stats.getSumDoubleValue(),
+            stats.getStartTime(),
+            stats.getEndTime());
+      }
     } else {
-      updateStats(
-          longStats.getMinValue(),
-          longStats.getMaxValue(),
-          longStats.getFirstValue(),
-          longStats.getLastValue(),
-          longStats.sumValue,
-          stats.getStartTime(),
-          stats.getEndTime());
+      throw new StatisticsClassException(this.getClass(), stats.getClass());
     }
   }
 
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/Statistics.java b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/Statistics.java
index 56b3bf7..3fc0f71 100644
--- a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/Statistics.java
+++ b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/Statistics.java
@@ -203,7 +203,7 @@
    */
   @SuppressWarnings("unchecked")
   public void mergeStatistics(Statistics<? extends Serializable> stats) {
-    if (this.getClass() == stats.getClass()) {
+    if (this.getClass() == stats.getClass() || canMerge(stats.getType(), this.getType())) {
       if (!stats.isEmpty) {
         if (stats.startTime < this.startTime) {
           this.startTime = stats.startTime;
@@ -213,7 +213,7 @@
         }
         // must be sure no overlap between two statistics
         this.count += stats.count;
-        mergeStatisticsValue((Statistics<T>) stats);
+        mergeStatisticsValue(stats);
         isEmpty = false;
       }
     } else {
@@ -225,6 +225,13 @@
     }
   }
 
+  public static boolean canMerge(TSDataType from, TSDataType to) {
+    return to.isCompatible(from)
+        &&
+        // cannot alter from TEXT to STRING because we cannot add statistic to the existing chunks
+        !(from == TSDataType.TEXT && to == TSDataType.STRING);
+  }
+
   public void update(long time, boolean value) {
     update(time);
     updateStats(value);
@@ -315,7 +322,8 @@
     count += batchSize;
   }
 
-  protected abstract void mergeStatisticsValue(Statistics<T> stats);
+  @SuppressWarnings("rawtypes")
+  protected abstract void mergeStatisticsValue(Statistics stats);
 
   public boolean isEmpty() {
     return isEmpty;
@@ -393,7 +401,7 @@
     return endTime;
   }
 
-  public long getCount() {
+  public int getCount() {
     return count;
   }
 
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/StringStatistics.java b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/StringStatistics.java
index 5997fb9..5da57af 100644
--- a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/StringStatistics.java
+++ b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/StringStatistics.java
@@ -143,24 +143,28 @@
         String.format(STATS_UNSUPPORTED_MSG, TSDataType.STRING, "long sum"));
   }
 
+  @SuppressWarnings("rawtypes")
   @Override
-  protected void mergeStatisticsValue(Statistics<Binary> stats) {
-    StringStatistics stringStats = (StringStatistics) stats;
-    if (isEmpty) {
-      initializeStats(
-          stringStats.getFirstValue(),
-          stringStats.getLastValue(),
-          stringStats.getMinValue(),
-          stringStats.getMaxValue());
-      isEmpty = false;
+  protected void mergeStatisticsValue(Statistics stats) {
+    if (stats instanceof StringStatistics) {
+      if (isEmpty) {
+        initializeStats(
+            ((Binary) stats.getFirstValue()),
+            ((Binary) stats.getLastValue()),
+            ((Binary) stats.getMinValue()),
+            ((Binary) stats.getMaxValue()));
+        isEmpty = false;
+      } else {
+        updateStats(
+            ((Binary) stats.getFirstValue()),
+            ((Binary) stats.getLastValue()),
+            ((Binary) stats.getMinValue()),
+            ((Binary) stats.getMaxValue()),
+            stats.getStartTime(),
+            stats.getEndTime());
+      }
     } else {
-      updateStats(
-          stringStats.getFirstValue(),
-          stringStats.getLastValue(),
-          stringStats.getMinValue(),
-          stringStats.getMaxValue(),
-          stats.getStartTime(),
-          stats.getEndTime());
+      throw new StatisticsClassException(this.getClass(), stats.getClass());
     }
   }
 
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/TimeStatistics.java b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/TimeStatistics.java
index 48fcb32..db12ac4 100644
--- a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/TimeStatistics.java
+++ b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/statistics/TimeStatistics.java
@@ -119,8 +119,9 @@
     throw new StatisticsClassException(String.format(STATS_UNSUPPORTED_MSG, TIME, UPDATE_STATS));
   }
 
+  @SuppressWarnings("rawtypes")
   @Override
-  protected void mergeStatisticsValue(Statistics<Long> stats) {}
+  protected void mergeStatisticsValue(Statistics stats) {}
 
   @Override
   public int serializeStats(OutputStream outputStream) {
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/FloatColumn.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/FloatColumn.java
index 98bcd90..c008d93 100644
--- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/FloatColumn.java
+++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/FloatColumn.java
@@ -34,6 +34,7 @@
 import static org.apache.tsfile.utils.RamUsageEstimator.sizeOfBooleanArray;
 import static org.apache.tsfile.utils.RamUsageEstimator.sizeOfFloatArray;
 
+@SuppressWarnings("java:S3012")
 public class FloatColumn implements Column {
 
   private static final int INSTANCE_SIZE =
@@ -95,11 +96,25 @@
   }
 
   @Override
+  public double getDouble(int position) {
+    return values[position + arrayOffset];
+  }
+
+  @Override
   public float[] getFloats() {
     return values;
   }
 
   @Override
+  public double[] getDoubles() {
+    double[] doubles = new double[values.length];
+    for (int i = 0; i < values.length; i++) {
+      doubles[i] = values[i];
+    }
+    return doubles;
+  }
+
+  @Override
   public Object getObject(int position) {
     return getFloat(position);
   }
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/IntColumn.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/IntColumn.java
index 3e0caa9..c2065ee 100644
--- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/IntColumn.java
+++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/IntColumn.java
@@ -34,6 +34,7 @@
 import static org.apache.tsfile.utils.RamUsageEstimator.sizeOfBooleanArray;
 import static org.apache.tsfile.utils.RamUsageEstimator.sizeOfIntArray;
 
+@SuppressWarnings("java:S3012")
 public class IntColumn implements Column {
 
   private static final int INSTANCE_SIZE =
@@ -95,11 +96,53 @@
   }
 
   @Override
+  public long getLong(int position) {
+    return values[position + arrayOffset];
+  }
+
+  @Override
+  public float getFloat(int position) {
+    return values[position + arrayOffset];
+  }
+
+  @Override
+  public double getDouble(int position) {
+    return values[position + arrayOffset];
+  }
+
+  @Override
   public int[] getInts() {
     return values;
   }
 
   @Override
+  public float[] getFloats() {
+    float[] result = new float[values.length];
+    for (int i = 0; i < values.length; i++) {
+      result[i] = values[i];
+    }
+    return result;
+  }
+
+  @Override
+  public long[] getLongs() {
+    long[] result = new long[values.length];
+    for (int i = 0; i < values.length; i++) {
+      result[i] = values[i];
+    }
+    return result;
+  }
+
+  @Override
+  public double[] getDoubles() {
+    double[] result = new double[values.length];
+    for (int i = 0; i < values.length; i++) {
+      result[i] = values[i];
+    }
+    return result;
+  }
+
+  @Override
   public Object getObject(int position) {
     return getInt(position);
   }
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/LongColumn.java b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/LongColumn.java
index bbbb066..ecba901 100644
--- a/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/LongColumn.java
+++ b/java/tsfile/src/main/java/org/apache/tsfile/read/common/block/column/LongColumn.java
@@ -34,6 +34,7 @@
 import static org.apache.tsfile.utils.RamUsageEstimator.sizeOfBooleanArray;
 import static org.apache.tsfile.utils.RamUsageEstimator.sizeOfLongArray;
 
+@SuppressWarnings("java:S3012")
 public class LongColumn implements Column {
 
   private static final int INSTANCE_SIZE =
@@ -95,11 +96,25 @@
   }
 
   @Override
+  public double getDouble(int position) {
+    return values[position + arrayOffset];
+  }
+
+  @Override
   public long[] getLongs() {
     return values;
   }
 
   @Override
+  public double[] getDoubles() {
+    double[] doubles = new double[values.length];
+    for (int i = 0; i < values.length; i++) {
+      doubles[i] = values[i];
+    }
+    return doubles;
+  }
+
+  @Override
   public Object getObject(int position) {
     return getLong(position);
   }
diff --git a/java/tsfile/src/test/java/org/apache/tsfile/file/metadata/statistics/LongStatisticsTest.java b/java/tsfile/src/test/java/org/apache/tsfile/file/metadata/statistics/LongStatisticsTest.java
index 47a3469..39b95e1 100644
--- a/java/tsfile/src/test/java/org/apache/tsfile/file/metadata/statistics/LongStatisticsTest.java
+++ b/java/tsfile/src/test/java/org/apache/tsfile/file/metadata/statistics/LongStatisticsTest.java
@@ -18,14 +18,11 @@
  */
 package org.apache.tsfile.file.metadata.statistics;
 
-import org.apache.tsfile.exception.filter.StatisticsClassException;
-
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 public class LongStatisticsTest {
 
@@ -78,25 +75,6 @@
     assertEquals(1, (long) longStats3.getFirstValue());
     assertEquals(max2, (long) longStats3.getLastValue());
 
-    // Test mismatch
-    IntegerStatistics intStats5 = new IntegerStatistics();
-    intStats5.updateStats(-10000);
-    try {
-      longStats3.mergeStatistics(intStats5);
-    } catch (StatisticsClassException e) {
-      // that's true route
-    } catch (Exception e) {
-      fail();
-    }
-
-    assertEquals(max2, (long) longStats3.getMaxValue());
-    // if not merge, the min value will not be changed by smaller value in
-    // intStats5
-    assertEquals(1, (long) longStats3.getMinValue());
-    assertEquals(max2 + max1 + 1, (long) longStats3.getSumDoubleValue());
-    assertEquals(1, (long) longStats3.getFirstValue());
-    assertEquals(max2, (long) longStats3.getLastValue());
-
     // Unseq Merge
     LongStatistics longStats4 = new LongStatistics();
     longStats4.setStartTime(0);
diff --git a/java/tsfile/src/test/java/org/apache/tsfile/file/metadata/statistics/StatisticsTest.java b/java/tsfile/src/test/java/org/apache/tsfile/file/metadata/statistics/StatisticsTest.java
new file mode 100644
index 0000000..06aeca3
--- /dev/null
+++ b/java/tsfile/src/test/java/org/apache/tsfile/file/metadata/statistics/StatisticsTest.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tsfile.file.metadata.statistics;
+
+import org.apache.tsfile.common.conf.TSFileConfig;
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.utils.Binary;
+
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class StatisticsTest {
+
+  @Test
+  public void testCrossTypeMerge() {
+    Set<TSDataType> dataTypes = new HashSet<>();
+    Collections.addAll(dataTypes, TSDataType.values());
+    dataTypes.remove(TSDataType.VECTOR);
+    dataTypes.remove(TSDataType.UNKNOWN);
+
+    for (TSDataType from : dataTypes) {
+      for (TSDataType to : dataTypes) {
+        Statistics fromStatistics = genStatistics(from, 0);
+        Statistics toStatistics = genStatistics(to, 1);
+        if (Statistics.canMerge(from, to)) {
+          toStatistics.mergeStatistics(fromStatistics);
+          checkStatistics(toStatistics, 0, 1, 1.0);
+        } else {
+          try {
+            toStatistics.mergeStatistics(fromStatistics);
+            fail("Expected MergeException");
+          } catch (Exception e) {
+            assertEquals(
+                String.format(
+                    "Statistics classes mismatched: %s vs. %s",
+                    toStatistics.getClass(), fromStatistics.getClass()),
+                e.getMessage());
+          }
+          checkStatistics(toStatistics, 1, 1, 1.0);
+        }
+      }
+    }
+  }
+
+  @SuppressWarnings("SameParameterValue")
+  private static void checkStatistics(Statistics statistics, int min, int max, double sum) {
+    assertEquals(min, statistics.getStartTime());
+    assertEquals(max, statistics.getEndTime());
+    switch (statistics.getType()) {
+      case INT32:
+      case INT64:
+      case FLOAT:
+      case DOUBLE:
+      case TIMESTAMP:
+      case DATE:
+        assertEquals(min, ((Number) statistics.getMinValue()).intValue());
+        assertEquals(max, ((Number) statistics.getMaxValue()).intValue());
+        assertEquals(min, ((Number) statistics.getFirstValue()).intValue());
+        assertEquals(max, ((Number) statistics.getLastValue()).intValue());
+        assertEquals(sum, statistics.getSumDoubleValue(), 0.001);
+        break;
+      case BOOLEAN:
+        assertEquals(min % 2 == 1, statistics.getFirstValue());
+        assertEquals(max % 2 == 1, statistics.getLastValue());
+        assertEquals(sum, statistics.getSumDoubleValue(), 0.001);
+        break;
+      case TEXT:
+        assertEquals(
+            new Binary(String.valueOf(min), TSFileConfig.STRING_CHARSET),
+            statistics.getFirstValue());
+        assertEquals(
+            new Binary(String.valueOf(max), TSFileConfig.STRING_CHARSET),
+            statistics.getLastValue());
+        break;
+      case STRING:
+        assertEquals(
+            new Binary(String.valueOf(min), TSFileConfig.STRING_CHARSET), statistics.getMinValue());
+        assertEquals(
+            new Binary(String.valueOf(max), TSFileConfig.STRING_CHARSET), statistics.getMaxValue());
+        assertEquals(
+            new Binary(String.valueOf(min), TSFileConfig.STRING_CHARSET),
+            statistics.getFirstValue());
+        assertEquals(
+            new Binary(String.valueOf(max), TSFileConfig.STRING_CHARSET),
+            statistics.getLastValue());
+        break;
+      case BLOB:
+        break;
+      default:
+        throw new IllegalArgumentException(statistics.getType().toString());
+    }
+  }
+
+  private static Statistics genStatistics(TSDataType dataType, int val) {
+    Statistics result;
+    switch (dataType) {
+      case INT32:
+        IntegerStatistics intStat = new IntegerStatistics();
+        intStat.initializeStats(val, val, val, val, val);
+        result = intStat;
+        break;
+      case INT64:
+        LongStatistics longStat = new LongStatistics();
+        longStat.initializeStats(val, val, val, val, val);
+        result = longStat;
+        break;
+      case FLOAT:
+        FloatStatistics floatStat = new FloatStatistics();
+        floatStat.initializeStats(val, val, val, val, val);
+        result = floatStat;
+        break;
+      case DOUBLE:
+        DoubleStatistics doubleStat = new DoubleStatistics();
+        doubleStat.initializeStats(val, val, val, val, val);
+        result = doubleStat;
+        break;
+      case TEXT:
+        BinaryStatistics binaryStat = new BinaryStatistics();
+        binaryStat.initializeStats(
+            new Binary(String.valueOf(val), TSFileConfig.STRING_CHARSET),
+            new Binary(String.valueOf(val), TSFileConfig.STRING_CHARSET));
+        result = binaryStat;
+        break;
+      case STRING:
+        StringStatistics stringStat = new StringStatistics();
+        stringStat.initializeStats(
+            new Binary(String.valueOf(val), TSFileConfig.STRING_CHARSET),
+            new Binary(String.valueOf(val), TSFileConfig.STRING_CHARSET),
+            new Binary(String.valueOf(val), TSFileConfig.STRING_CHARSET),
+            new Binary(String.valueOf(val), TSFileConfig.STRING_CHARSET));
+        result = stringStat;
+        break;
+      case BOOLEAN:
+        BooleanStatistics boolStat = new BooleanStatistics();
+        boolStat.initializeStats(val % 2 == 1, val % 2 == 1, val % 2 == 1 ? 1 : 0);
+        result = boolStat;
+        break;
+      case BLOB:
+        BlobStatistics blobStat = new BlobStatistics();
+        result = blobStat;
+        break;
+      case DATE:
+        DateStatistics dateStat = new DateStatistics();
+        dateStat.initializeStats(val, val, val, val, val);
+        result = dateStat;
+        break;
+      case TIMESTAMP:
+        TimestampStatistics timestampStat = new TimestampStatistics();
+        timestampStat.initializeStats(val, val, val, val, val);
+        result = timestampStat;
+        break;
+      default:
+        throw new IllegalArgumentException(dataType.toString());
+    }
+    result.setStartTime(val);
+    result.setEndTime(val);
+    result.setEmpty(false);
+    return result;
+  }
+}