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

asolimando pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 6f1cc7a710 [CALCITE-7917] RelMdSize returns negative sizes for 
CHARACTER and BINARY types with unspecified precision
6f1cc7a710 is described below

commit 6f1cc7a7100de9a74648de93b6f13c000cffbd57
Author: Alessandro Solimando <[email protected]>
AuthorDate: Wed Nov 4 21:32:19 2026 +0100

    [CALCITE-7917] RelMdSize returns negative sizes for CHARACTER and BINARY 
types with unspecified precision
---
 .../org/apache/calcite/rel/metadata/RelMdSize.java | 14 ++++++-
 .../org/apache/calcite/test/RelMetadataTest.java   | 44 ++++++++++++++++++++++
 2 files changed, 56 insertions(+), 2 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
index 675aa1e363..f1b8a7341a 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
@@ -74,6 +74,10 @@ public class RelMdSize implements 
MetadataHandler<BuiltInMetadata.Size> {
   /** Bytes per character (2). */
   public static final int BYTES_PER_CHARACTER = Character.SIZE / Byte.SIZE;
 
+  /** Maximum estimated average size, in bytes, of a CHARACTER or BINARY
+   * family value whose length can be large or is not specified. */
+  public static final double MAX_STRING_VALUE_SIZE = 100d;
+
   //~ Constructors -----------------------------------------------------------
 
   protected RelMdSize() {}
@@ -306,6 +310,11 @@ protected RelMdSize() {}
    * is nullable.
    */
   public @Nullable Double averageTypeValueSize(RelDataType type) {
+    if (type.getPrecision() == RelDataType.PRECISION_NOT_SPECIFIED
+        && SqlTypeUtil.inCharOrBinaryFamilies(type)) {
+      // Unspecified precision: use the same estimate as for large precisions
+      return MAX_STRING_VALUE_SIZE;
+    }
     switch (type.getSqlTypeName()) {
     case BOOLEAN:
     case TINYINT:
@@ -347,12 +356,13 @@ protected RelMdSize() {}
     case BINARY:
       return (double) type.getPrecision();
     case VARBINARY:
-      return Math.min((double) type.getPrecision(), 100d);
+      return Math.min((double) type.getPrecision(), MAX_STRING_VALUE_SIZE);
     case CHAR:
       return (double) type.getPrecision() * BYTES_PER_CHARACTER;
     case VARCHAR:
       // Even in large (say VARCHAR(2000)) columns most strings are small
-      return Math.min((double) type.getPrecision() * BYTES_PER_CHARACTER, 
100d);
+      return Math.min((double) type.getPrecision() * BYTES_PER_CHARACTER,
+          MAX_STRING_VALUE_SIZE);
     case ROW:
       double average = 0.0;
       for (RelDataTypeField field : type.getFieldList()) {
diff --git a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java 
b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
index daa4fc66c7..3a5d301788 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
@@ -88,6 +88,7 @@
 import org.apache.calcite.rel.metadata.RelMdMaxRowCount;
 import org.apache.calcite.rel.metadata.RelMdPopulationSize;
 import org.apache.calcite.rel.metadata.RelMdPredicates;
+import org.apache.calcite.rel.metadata.RelMdSize;
 import org.apache.calcite.rel.metadata.RelMdUniqueKeys;
 import org.apache.calcite.rel.metadata.RelMdUtil;
 import org.apache.calcite.rel.metadata.RelMetadataProvider;
@@ -97,6 +98,7 @@
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rel.type.RelDataTypeSystemImpl;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexCall;
 import org.apache.calcite.rex.RexCorrelVariable;
@@ -115,6 +117,7 @@
 import org.apache.calcite.sql.test.SqlValidatorTester;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlTypeFactoryImpl;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.test.catalog.MockCatalogReaderSimple;
 import org.apache.calcite.tools.Frameworks;
@@ -3629,6 +3632,47 @@ private void checkAverageRowSize(RelOptCluster cluster, 
RelOptTable empTable,
     checkSize("SELECT CAST (1 AS BIGINT UNSIGNED)", 8d);
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7917";>[CALCITE-7917]
+   * RelMdSize returns negative sizes for CHARACTER and BINARY types with
+   * unspecified precision</a>. */
+  @Test void testSizeUnspecifiedPrecision() {
+    checkSize("SELECT CAST(empno AS VARCHAR) FROM emp", 100d);
+
+    final RelOptCluster cluster = sql("values 1").toRel().getCluster();
+    final RelDataType rowType = cluster.getTypeFactory().builder()
+        .add("a", SqlTypeName.VARCHAR)
+        .add("b", SqlTypeName.VARBINARY)
+        .build();
+    final LogicalValues values = LogicalValues.createEmpty(cluster, rowType);
+    final RelMetadataQuery mq = cluster.getMetadataQuery();
+    assertThat(mq.getAverageColumnSizes(values),
+        equalTo(Arrays.asList(100d, 100d)));
+    assertThat(mq.getAverageRowSize(values), equalTo(200d));
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7917";>[CALCITE-7917]
+   * RelMdSize returns negative sizes for CHARACTER and BINARY types with
+   * unspecified precision</a>. */
+  @Test void testSizeUnspecifiedPrecisionCustomTypeSystem() {
+    final RelDataTypeFactory typeFactory =
+        new SqlTypeFactoryImpl(new RelDataTypeSystemImpl() {
+          @Override public int getDefaultPrecision(SqlTypeName typeName) {
+            return RelDataType.PRECISION_NOT_SPECIFIED;
+          }
+        });
+    final RelMdSize relMdSize = new RelMdSize() { };
+    assertThat(
+        relMdSize.averageTypeValueSize(
+            typeFactory.createSqlType(SqlTypeName.CHAR)),
+        equalTo(100d));
+    assertThat(
+        relMdSize.averageTypeValueSize(
+            typeFactory.createSqlType(SqlTypeName.BINARY)),
+        equalTo(100d));
+  }
+
   private void checkSize(String query, double expected) {
     final RelNode rel = sql(query).toRel();
     final RelMetadataQuery mq = rel.getCluster().getMetadataQuery();

Reply via email to