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();