From 552553483639893878d9d71f6641bc7f9f633e02 Mon Sep 17 00:00:00 2001 From: Chandrakant Vankayalapati <104664857+ceekay47@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:28:22 -0800 Subject: [PATCH 1/4] fix: Resolve table names in MV query optimizer for consistent matching (#27059) Summary: MV query optimizer fails to rewrite queries when the specified table name differs between the MV definition and the incoming query (ex: `base_table` vs `schema.base_table`). This fix resolves table references to schema-qualified names, ensuring consistent table matching regardless of how the table was specified. Reviewed By: zation99 Differential Revision: D91699496 ## Summary by Sourcery Ensure materialized view query optimization consistently matches base tables regardless of schema qualification in table names. Bug Fixes: - Fix materialized view rewrites failing when base tables are referenced with different schema qualifications between the MV definition and the incoming query. Tests: - Add coverage to verify materialized view query optimization works when base tables are referenced both with and without schema-qualified names in various query shapes. ## Release Notes ``` == RELEASE NOTES == General Changes * Fix MV query optimizer by correctly resolving table references to schema-qualified names. ``` --- .../presto/sql/MaterializedViewUtils.java | 13 +++++++ .../MaterializedViewQueryOptimizer.java | 4 ++- .../TestMaterializedViewQueryOptimizer.java | 36 +++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/presto-main-base/src/main/java/com/facebook/presto/sql/MaterializedViewUtils.java b/presto-main-base/src/main/java/com/facebook/presto/sql/MaterializedViewUtils.java index dcc80d61ab1ad..98399ccc19ef4 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/sql/MaterializedViewUtils.java +++ b/presto-main-base/src/main/java/com/facebook/presto/sql/MaterializedViewUtils.java @@ -15,6 +15,7 @@ package com.facebook.presto.sql; import com.facebook.presto.Session; +import com.facebook.presto.common.QualifiedObjectName; import com.facebook.presto.common.predicate.NullableValue; import com.facebook.presto.common.predicate.TupleDomain; import com.facebook.presto.metadata.Metadata; @@ -40,7 +41,9 @@ import com.facebook.presto.sql.tree.IsNullPredicate; import com.facebook.presto.sql.tree.LogicalBinaryExpression; import com.facebook.presto.sql.tree.QualifiedName; +import com.facebook.presto.sql.tree.Relation; import com.facebook.presto.sql.tree.SymbolReference; +import com.facebook.presto.sql.tree.Table; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -60,6 +63,7 @@ import static com.facebook.presto.common.predicate.TupleDomain.extractFixedValues; import static com.facebook.presto.common.type.StandardTypes.HYPER_LOG_LOG; import static com.facebook.presto.common.type.StandardTypes.VARBINARY; +import static com.facebook.presto.metadata.MetadataUtil.createQualifiedObjectName; import static com.facebook.presto.sql.ExpressionUtils.combineDisjuncts; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.NOT_SUPPORTED; import static com.facebook.presto.sql.tree.ArithmeticBinaryExpression.Operator.DIVIDE; @@ -399,6 +403,15 @@ public static Expression convertMaterializedDataPredicatesToExpression( } } + public static Relation resolveTableName(Relation relation, Session session, Metadata metadata) + { + if (!(relation instanceof Table)) { + return relation; + } + QualifiedObjectName qualifiedTableName = createQualifiedObjectName(session, relation, ((Table) relation).getName(), metadata); + return new Table(QualifiedName.of(qualifiedTableName.getSchemaName(), qualifiedTableName.getObjectName())); + } + private static Expression convertSymbolReferencesToIdentifiers(Expression expression) { return ExpressionTreeRewriter.rewriteWith(new ExpressionRewriter() diff --git a/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/MaterializedViewQueryOptimizer.java b/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/MaterializedViewQueryOptimizer.java index 3b6355bdfd317..ac0b7591ab38b 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/MaterializedViewQueryOptimizer.java +++ b/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/MaterializedViewQueryOptimizer.java @@ -101,6 +101,7 @@ import static com.facebook.presto.sql.MaterializedViewUtils.COUNT; import static com.facebook.presto.sql.MaterializedViewUtils.NON_ASSOCIATIVE_REWRITE_FUNCTIONS; import static com.facebook.presto.sql.MaterializedViewUtils.SUM; +import static com.facebook.presto.sql.MaterializedViewUtils.resolveTableName; import static com.facebook.presto.sql.analyzer.MaterializedViewInformationExtractor.MaterializedViewInfo; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISSING_TABLE; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.NOT_SUPPORTED; @@ -641,7 +642,8 @@ protected Node visitAliasedRelation(AliasedRelation node, Void context) @Override protected Node visitRelation(Relation node, Void context) { - if (materializedViewInfo.getBaseTable().isPresent() && node.equals(materializedViewInfo.getBaseTable().get())) { + if (materializedViewInfo.getBaseTable().isPresent() && resolveTableName(node, session, metadata) + .equals(resolveTableName(materializedViewInfo.getBaseTable().get(), session, metadata))) { return materializedView; } throw new IllegalStateException("Mismatching table or non-supporting relation format in base query"); diff --git a/presto-main-base/src/test/java/com/facebook/presto/sql/analyzer/TestMaterializedViewQueryOptimizer.java b/presto-main-base/src/test/java/com/facebook/presto/sql/analyzer/TestMaterializedViewQueryOptimizer.java index 108ebb4da54e3..bf70110ea0eb5 100644 --- a/presto-main-base/src/test/java/com/facebook/presto/sql/analyzer/TestMaterializedViewQueryOptimizer.java +++ b/presto-main-base/src/test/java/com/facebook/presto/sql/analyzer/TestMaterializedViewQueryOptimizer.java @@ -450,6 +450,42 @@ public void testWithTableAlias() assertOptimizedQuery(baseQuerySqlWithTablePrefix, expectedRewrittenSql, originalViewSqlWithTablePrefix, BASE_TABLE_1, VIEW_1); } + @Test + public void testWithSchemaQualifiedTableName() + { + String schemaQualifiedTable = SESSION_SCHEMA + "." + BASE_TABLE_1; + + String originalViewSql = format("SELECT a, b FROM %s", BASE_TABLE_1); + String baseQuerySql = format("SELECT a, b FROM %s", schemaQualifiedTable); + String expectedRewrittenSql = format("SELECT a, b FROM %s", VIEW_1); + + assertOptimizedQuery(baseQuerySql, expectedRewrittenSql, originalViewSql, BASE_TABLE_1, VIEW_1); + + originalViewSql = format("SELECT a, b FROM %s", schemaQualifiedTable); + baseQuerySql = format("SELECT a, b FROM %s", BASE_TABLE_1); + expectedRewrittenSql = format("SELECT a, b FROM %s", VIEW_1); + + assertOptimizedQuery(baseQuerySql, expectedRewrittenSql, originalViewSql, BASE_TABLE_1, VIEW_1); + + originalViewSql = format("SELECT a, b FROM %s", schemaQualifiedTable); + baseQuerySql = format("SELECT a, b FROM %s", schemaQualifiedTable); + expectedRewrittenSql = format("SELECT a, b FROM %s", VIEW_1); + + assertOptimizedQuery(baseQuerySql, expectedRewrittenSql, originalViewSql, BASE_TABLE_1, VIEW_1); + + originalViewSql = format("SELECT a, b, c FROM %s", BASE_TABLE_1); + baseQuerySql = format("SELECT a, b FROM %s WHERE c > 10", schemaQualifiedTable); + expectedRewrittenSql = format("SELECT a, b FROM %s WHERE c > 10", VIEW_1); + + assertOptimizedQuery(baseQuerySql, expectedRewrittenSql, originalViewSql, BASE_TABLE_1, VIEW_1); + + originalViewSql = format("SELECT SUM(a) as sum_a, b FROM %s GROUP BY b", BASE_TABLE_1); + baseQuerySql = format("SELECT SUM(a), b FROM %s GROUP BY b", schemaQualifiedTable); + expectedRewrittenSql = format("SELECT SUM(sum_a), b FROM %s GROUP BY b", VIEW_1); + + assertOptimizedQuery(baseQuerySql, expectedRewrittenSql, originalViewSql, BASE_TABLE_1, VIEW_1); + } + @Test public void testAggregationWithTableAlias() { From 9ab52f5938bec1107a9c6449a0c57daabed4e4f6 Mon Sep 17 00:00:00 2001 From: Kyle Wong <63994386+wongquijote@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:33:47 -0800 Subject: [PATCH 2/4] test(native): Add ipprefix and ipaddress tests to native-tests (#26905) Summary: Ported the IpPrefix and IpAddress tests in https://github.com/prestodb/presto/blob/master/presto-main-base/src/test/java/com/facebook/presto/operator/scalar/TestIpPrefixFunctions.java to run with Presto Native engine in presto-native-tests. This is a continuation of the work to refactor scalar function tests from `presto-main-base` to `presto-main-tests` from this PR: https://github.com/prestodb/presto/pull/26013 Also moved IpPrefixType and IpAddressType into `presto-common` from `presto-main-base` due to some dependency cycles that appeared after refactoring. == NO RELEASE NOTE == --- presto-common/pom.xml | 1 - .../presto/common}/type/IpAddressType.java | 5 +- .../presto/common}/type/IpPrefixType.java | 5 +- ...uiltInTypeAndFunctionNamespaceManager.java | 4 +- .../operator/scalar/IpPrefixFunctions.java | 4 +- .../CheckUnsupportedPrestissimoTypes.java | 2 +- .../presto/type/IpAddressOperators.java | 2 +- .../presto/type/IpPrefixOperators.java | 4 +- ...TestApproximateCountDistinctIpAddress.java | 2 +- .../TestNoisyCountGaussianAggregation.java | 4 +- ...isyCountGaussianRandomSeedAggregation.java | 4 +- .../scalar/AbstractTestFunctions.java | 6 +- .../scalar/TestIpPrefixFunctions.java | 305 +---------------- .../TestCheckUnsupportedPrestissimoTypes.java | 2 +- .../presto/type/TestIpAddressOperators.java | 2 +- .../presto/type/TestIpAddressType.java | 2 +- .../presto/type/TestIpPrefixOperators.java | 4 +- .../presto/type/TestIpPrefixType.java | 2 +- .../operator/scalar/AbstractTestIpPrefix.java | 324 ++++++++++++++++++ .../tests/operator/scalar/TestFunctions.java | 12 + .../scalar/AbstractTestNativeFunctions.java | 28 ++ .../scalar/TestIpPrefixFunctions.java | 22 ++ presto-spi/pom.xml | 12 + .../presto/tests/TestingPrestoClient.java | 6 +- 24 files changed, 430 insertions(+), 334 deletions(-) rename {presto-main-base/src/main/java/com/facebook/presto => presto-common/src/main/java/com/facebook/presto/common}/type/IpAddressType.java (96%) rename {presto-main-base/src/main/java/com/facebook/presto => presto-common/src/main/java/com/facebook/presto/common}/type/IpPrefixType.java (96%) create mode 100644 presto-main-tests/src/main/java/com/facebook/presto/tests/operator/scalar/AbstractTestIpPrefix.java create mode 100644 presto-native-tests/src/test/java/com/facebook/presto/nativetests/operator/scalar/TestIpPrefixFunctions.java diff --git a/presto-common/pom.xml b/presto-common/pom.xml index 07e80d39313e7..cbc4eea221a3c 100644 --- a/presto-common/pom.xml +++ b/presto-common/pom.xml @@ -78,7 +78,6 @@ com.google.guava guava - test diff --git a/presto-main-base/src/main/java/com/facebook/presto/type/IpAddressType.java b/presto-common/src/main/java/com/facebook/presto/common/type/IpAddressType.java similarity index 96% rename from presto-main-base/src/main/java/com/facebook/presto/type/IpAddressType.java rename to presto-common/src/main/java/com/facebook/presto/common/type/IpAddressType.java index dbcc9d46ee587..5d2b1b11380dd 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/type/IpAddressType.java +++ b/presto-common/src/main/java/com/facebook/presto/common/type/IpAddressType.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.type; +package com.facebook.presto.common.type; import com.facebook.presto.common.block.Block; import com.facebook.presto.common.block.BlockBuilder; @@ -19,9 +19,6 @@ import com.facebook.presto.common.block.Int128ArrayBlockBuilder; import com.facebook.presto.common.block.PageBuilderStatus; import com.facebook.presto.common.function.SqlFunctionProperties; -import com.facebook.presto.common.type.AbstractPrimitiveType; -import com.facebook.presto.common.type.FixedWidthType; -import com.facebook.presto.common.type.StandardTypes; import com.google.common.net.InetAddresses; import io.airlift.slice.Slice; import io.airlift.slice.Slices; diff --git a/presto-main-base/src/main/java/com/facebook/presto/type/IpPrefixType.java b/presto-common/src/main/java/com/facebook/presto/common/type/IpPrefixType.java similarity index 96% rename from presto-main-base/src/main/java/com/facebook/presto/type/IpPrefixType.java rename to presto-common/src/main/java/com/facebook/presto/common/type/IpPrefixType.java index 9381ff5c0ea34..dbf624672f903 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/type/IpPrefixType.java +++ b/presto-common/src/main/java/com/facebook/presto/common/type/IpPrefixType.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.type; +package com.facebook.presto.common.type; import com.facebook.presto.common.block.Block; import com.facebook.presto.common.block.BlockBuilder; @@ -19,9 +19,6 @@ import com.facebook.presto.common.block.PageBuilderStatus; import com.facebook.presto.common.block.VariableWidthBlockBuilder; import com.facebook.presto.common.function.SqlFunctionProperties; -import com.facebook.presto.common.type.AbstractPrimitiveType; -import com.facebook.presto.common.type.FixedWidthType; -import com.facebook.presto.common.type.StandardTypes; import com.google.common.net.InetAddresses; import io.airlift.slice.Slice; import io.airlift.slice.XxHash64; diff --git a/presto-main-base/src/main/java/com/facebook/presto/metadata/BuiltInTypeAndFunctionNamespaceManager.java b/presto-main-base/src/main/java/com/facebook/presto/metadata/BuiltInTypeAndFunctionNamespaceManager.java index 4984cd8fe065d..4bca843875cef 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/metadata/BuiltInTypeAndFunctionNamespaceManager.java +++ b/presto-main-base/src/main/java/com/facebook/presto/metadata/BuiltInTypeAndFunctionNamespaceManager.java @@ -317,6 +317,8 @@ import static com.facebook.presto.common.type.DoubleType.OLD_NAN_DOUBLE; import static com.facebook.presto.common.type.HyperLogLogType.HYPER_LOG_LOG; import static com.facebook.presto.common.type.IntegerType.INTEGER; +import static com.facebook.presto.common.type.IpAddressType.IPADDRESS; +import static com.facebook.presto.common.type.IpPrefixType.IPPREFIX; import static com.facebook.presto.common.type.JsonType.JSON; import static com.facebook.presto.common.type.KdbTreeType.KDB_TREE; import static com.facebook.presto.common.type.KllSketchParametricType.KLL_SKETCH; @@ -504,8 +506,6 @@ import static com.facebook.presto.type.FunctionParametricType.FUNCTION; import static com.facebook.presto.type.IntervalDayTimeType.INTERVAL_DAY_TIME; import static com.facebook.presto.type.IntervalYearMonthType.INTERVAL_YEAR_MONTH; -import static com.facebook.presto.type.IpAddressType.IPADDRESS; -import static com.facebook.presto.type.IpPrefixType.IPPREFIX; import static com.facebook.presto.type.JoniRegexpType.JONI_REGEXP; import static com.facebook.presto.type.JsonPathType.JSON_PATH; import static com.facebook.presto.type.LikePatternType.LIKE_PATTERN; diff --git a/presto-main-base/src/main/java/com/facebook/presto/operator/scalar/IpPrefixFunctions.java b/presto-main-base/src/main/java/com/facebook/presto/operator/scalar/IpPrefixFunctions.java index 5f7f9a27d19f7..ace3200d78e8f 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/operator/scalar/IpPrefixFunctions.java +++ b/presto-main-base/src/main/java/com/facebook/presto/operator/scalar/IpPrefixFunctions.java @@ -32,15 +32,15 @@ import java.util.Comparator; import java.util.List; +import static com.facebook.presto.common.type.IpAddressType.IPADDRESS; +import static com.facebook.presto.common.type.IpPrefixType.IPPREFIX; import static com.facebook.presto.operator.scalar.ArraySortFunction.sort; import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; import static com.facebook.presto.type.IpAddressOperators.between; import static com.facebook.presto.type.IpAddressOperators.castFromVarcharToIpAddress; -import static com.facebook.presto.type.IpAddressType.IPADDRESS; import static com.facebook.presto.type.IpPrefixOperators.castFromIpPrefixToIpAddress; import static com.facebook.presto.type.IpPrefixOperators.castFromVarcharToIpPrefix; -import static com.facebook.presto.type.IpPrefixType.IPPREFIX; import static com.facebook.presto.util.Failures.checkCondition; import static io.airlift.slice.Slices.utf8Slice; import static io.airlift.slice.Slices.wrappedBuffer; diff --git a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/sanity/CheckUnsupportedPrestissimoTypes.java b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/sanity/CheckUnsupportedPrestissimoTypes.java index 29217e5451577..a7755d49e6148 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/sanity/CheckUnsupportedPrestissimoTypes.java +++ b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/sanity/CheckUnsupportedPrestissimoTypes.java @@ -41,8 +41,8 @@ import java.util.Objects; import java.util.Optional; +import static com.facebook.presto.common.type.IpAddressType.IPADDRESS; import static com.facebook.presto.common.type.TimestampWithTimeZoneType.TIMESTAMP_WITH_TIME_ZONE; -import static com.facebook.presto.type.IpAddressType.IPADDRESS; import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; diff --git a/presto-main-base/src/main/java/com/facebook/presto/type/IpAddressOperators.java b/presto-main-base/src/main/java/com/facebook/presto/type/IpAddressOperators.java index 5d68bb96e5195..108f27b820140 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/type/IpAddressOperators.java +++ b/presto-main-base/src/main/java/com/facebook/presto/type/IpAddressOperators.java @@ -42,9 +42,9 @@ import static com.facebook.presto.common.function.OperatorType.LESS_THAN_OR_EQUAL; import static com.facebook.presto.common.function.OperatorType.NOT_EQUAL; import static com.facebook.presto.common.function.OperatorType.XX_HASH_64; +import static com.facebook.presto.common.type.IpAddressType.IPADDRESS; import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; import static com.facebook.presto.spi.StandardErrorCode.INVALID_CAST_ARGUMENT; -import static com.facebook.presto.type.IpAddressType.IPADDRESS; import static io.airlift.slice.Slices.utf8Slice; import static io.airlift.slice.Slices.wrappedBuffer; import static java.lang.System.arraycopy; diff --git a/presto-main-base/src/main/java/com/facebook/presto/type/IpPrefixOperators.java b/presto-main-base/src/main/java/com/facebook/presto/type/IpPrefixOperators.java index 44f23753ea08e..029e5b27a892b 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/type/IpPrefixOperators.java +++ b/presto-main-base/src/main/java/com/facebook/presto/type/IpPrefixOperators.java @@ -42,10 +42,10 @@ import static com.facebook.presto.common.function.OperatorType.LESS_THAN_OR_EQUAL; import static com.facebook.presto.common.function.OperatorType.NOT_EQUAL; import static com.facebook.presto.common.function.OperatorType.XX_HASH_64; +import static com.facebook.presto.common.type.IpAddressType.IPADDRESS; +import static com.facebook.presto.common.type.IpPrefixType.IPPREFIX; import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; import static com.facebook.presto.spi.StandardErrorCode.INVALID_CAST_ARGUMENT; -import static com.facebook.presto.type.IpAddressType.IPADDRESS; -import static com.facebook.presto.type.IpPrefixType.IPPREFIX; import static io.airlift.slice.Slices.utf8Slice; import static io.airlift.slice.Slices.wrappedBuffer; import static java.lang.Math.max; diff --git a/presto-main-base/src/test/java/com/facebook/presto/operator/aggregation/TestApproximateCountDistinctIpAddress.java b/presto-main-base/src/test/java/com/facebook/presto/operator/aggregation/TestApproximateCountDistinctIpAddress.java index 64bc0afd92f01..c837923ea9911 100644 --- a/presto-main-base/src/test/java/com/facebook/presto/operator/aggregation/TestApproximateCountDistinctIpAddress.java +++ b/presto-main-base/src/test/java/com/facebook/presto/operator/aggregation/TestApproximateCountDistinctIpAddress.java @@ -20,8 +20,8 @@ import java.util.concurrent.ThreadLocalRandom; import static com.facebook.presto.common.type.DoubleType.DOUBLE; +import static com.facebook.presto.common.type.IpAddressType.IPADDRESS; import static com.facebook.presto.sql.analyzer.TypeSignatureProvider.fromTypes; -import static com.facebook.presto.type.IpAddressType.IPADDRESS; public class TestApproximateCountDistinctIpAddress extends AbstractTestApproximateCountDistinct diff --git a/presto-main-base/src/test/java/com/facebook/presto/operator/aggregation/noisyaggregation/TestNoisyCountGaussianAggregation.java b/presto-main-base/src/test/java/com/facebook/presto/operator/aggregation/noisyaggregation/TestNoisyCountGaussianAggregation.java index e27fc7f2e57d1..349b2a190fe43 100644 --- a/presto-main-base/src/test/java/com/facebook/presto/operator/aggregation/noisyaggregation/TestNoisyCountGaussianAggregation.java +++ b/presto-main-base/src/test/java/com/facebook/presto/operator/aggregation/noisyaggregation/TestNoisyCountGaussianAggregation.java @@ -43,6 +43,8 @@ import static com.facebook.presto.common.type.DoubleType.DOUBLE; import static com.facebook.presto.common.type.HyperLogLogType.HYPER_LOG_LOG; import static com.facebook.presto.common.type.IntegerType.INTEGER; +import static com.facebook.presto.common.type.IpAddressType.IPADDRESS; +import static com.facebook.presto.common.type.IpPrefixType.IPPREFIX; import static com.facebook.presto.common.type.JsonType.JSON; import static com.facebook.presto.common.type.P4HyperLogLogType.P4_HYPER_LOG_LOG; import static com.facebook.presto.common.type.QuantileDigestParametricType.QDIGEST; @@ -68,8 +70,6 @@ import static com.facebook.presto.type.ArrayParametricType.ARRAY; import static com.facebook.presto.type.IntervalDayTimeType.INTERVAL_DAY_TIME; import static com.facebook.presto.type.IntervalYearMonthType.INTERVAL_YEAR_MONTH; -import static com.facebook.presto.type.IpAddressType.IPADDRESS; -import static com.facebook.presto.type.IpPrefixType.IPPREFIX; import static com.facebook.presto.type.MapParametricType.MAP; import static com.facebook.presto.type.RowParametricType.ROW; import static com.facebook.presto.type.khyperloglog.KHyperLogLogType.K_HYPER_LOG_LOG; diff --git a/presto-main-base/src/test/java/com/facebook/presto/operator/aggregation/noisyaggregation/TestNoisyCountGaussianRandomSeedAggregation.java b/presto-main-base/src/test/java/com/facebook/presto/operator/aggregation/noisyaggregation/TestNoisyCountGaussianRandomSeedAggregation.java index 34a84c7c8a3bc..815a396bd27d7 100644 --- a/presto-main-base/src/test/java/com/facebook/presto/operator/aggregation/noisyaggregation/TestNoisyCountGaussianRandomSeedAggregation.java +++ b/presto-main-base/src/test/java/com/facebook/presto/operator/aggregation/noisyaggregation/TestNoisyCountGaussianRandomSeedAggregation.java @@ -43,6 +43,8 @@ import static com.facebook.presto.common.type.DoubleType.DOUBLE; import static com.facebook.presto.common.type.HyperLogLogType.HYPER_LOG_LOG; import static com.facebook.presto.common.type.IntegerType.INTEGER; +import static com.facebook.presto.common.type.IpAddressType.IPADDRESS; +import static com.facebook.presto.common.type.IpPrefixType.IPPREFIX; import static com.facebook.presto.common.type.JsonType.JSON; import static com.facebook.presto.common.type.P4HyperLogLogType.P4_HYPER_LOG_LOG; import static com.facebook.presto.common.type.QuantileDigestParametricType.QDIGEST; @@ -68,8 +70,6 @@ import static com.facebook.presto.type.ArrayParametricType.ARRAY; import static com.facebook.presto.type.IntervalDayTimeType.INTERVAL_DAY_TIME; import static com.facebook.presto.type.IntervalYearMonthType.INTERVAL_YEAR_MONTH; -import static com.facebook.presto.type.IpAddressType.IPADDRESS; -import static com.facebook.presto.type.IpPrefixType.IPPREFIX; import static com.facebook.presto.type.MapParametricType.MAP; import static com.facebook.presto.type.RowParametricType.ROW; import static com.facebook.presto.type.khyperloglog.KHyperLogLogType.K_HYPER_LOG_LOG; diff --git a/presto-main-base/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java b/presto-main-base/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java index e9b3e8641c885..dc8cf971df256 100644 --- a/presto-main-base/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java +++ b/presto-main-base/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java @@ -168,7 +168,8 @@ protected void assertInvalidFunction(String projection, StandardErrorCode errorC functionAssertions.assertInvalidFunction(projection, errorCode, messagePattern); } - protected void assertInvalidFunction(String projection, String messagePattern) + @Override + public void assertInvalidFunction(String projection, String messagePattern) { functionAssertions.assertInvalidFunction(projection, INVALID_FUNCTION_ARGUMENT, messagePattern); } @@ -208,7 +209,8 @@ protected void assertInvalidCast(String projection) functionAssertions.assertInvalidCast(projection); } - protected void assertInvalidCast(@Language("SQL") String projection, String message) + @Override + public void assertInvalidCast(@Language("SQL") String projection, String message) { functionAssertions.assertInvalidCast(projection, message); } diff --git a/presto-main-base/src/test/java/com/facebook/presto/operator/scalar/TestIpPrefixFunctions.java b/presto-main-base/src/test/java/com/facebook/presto/operator/scalar/TestIpPrefixFunctions.java index 21f2b615887c9..61f80a92e5170 100644 --- a/presto-main-base/src/test/java/com/facebook/presto/operator/scalar/TestIpPrefixFunctions.java +++ b/presto-main-base/src/test/java/com/facebook/presto/operator/scalar/TestIpPrefixFunctions.java @@ -13,311 +13,10 @@ */ package com.facebook.presto.operator.scalar; -import com.facebook.presto.common.type.ArrayType; -import com.google.common.collect.ImmutableList; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import static com.facebook.presto.common.type.BooleanType.BOOLEAN; -import static com.facebook.presto.type.IpAddressType.IPADDRESS; -import static com.facebook.presto.type.IpPrefixType.IPPREFIX; +import com.facebook.presto.tests.operator.scalar.AbstractTestIpPrefix; public class TestIpPrefixFunctions extends AbstractTestFunctions + implements AbstractTestIpPrefix { - @DataProvider(name = "public-ip-provider") - public Object[] publicIpProvider() - { - return new Object[] { - "6.7.8.9", - "157.240.200.99", - "8.8.8.8", - "128.1.2.8", - "2a03:2880:f031:12:face:b00c:0:2", - "2600:1406:6c00::173c:ad43", - "2607:f8b0:4007:818::2004" - }; - } - - @DataProvider(name = "private-ip-provider") - public Object[][] privateIpProvider() - { - return new Object[][] { - // The first and last IP address in each private range - {"0.0.0.0"}, {"0.255.255.255"}, // 0.0.0.0/8 RFC1122: "This host on this network" - {"10.0.0.0"}, {"10.255.255.255"}, // 10.0.0.0/8 RFC1918: Private-Use - {"100.64.0.0"}, {"100.127.255.255"}, // 100.64.0.0/10 RFC6598: Shared Address Space - {"127.0.0.0"}, {"127.255.255.255"}, // 127.0.0.0/8 RFC1122: Loopback - {"169.254.0.0"}, {"169.254.255.255"}, // 169.254.0.0/16 RFC3927: Link Local - {"172.16.0.0"}, {"172.31.255.255"}, // 172.16.0.0/12 RFC1918: Private-Use - {"192.0.0.0"}, {"192.0.0.255"}, // 192.0.0.0/24 RFC6890: IETF Protocol Assignments - {"192.0.2.0"}, {"192.0.2.255"}, // 192.0.2.0/24 RFC5737: Documentation (TEST-NET-1) - {"192.88.99.0"}, {"192.88.99.255"}, // 192.88.99.0/24 RFC3068: 6to4 Relay anycast - {"192.168.0.0"}, {"192.168.255.255"}, // 192.168.0.0/16 RFC1918: Private-Use - {"198.18.0.0"}, {"198.19.255.255"}, // 198.18.0.0/15 RFC2544: Benchmarking - {"198.51.100.0"}, {"198.51.100.255"}, // 198.51.100.0/24 RFC5737: Documentation (TEST-NET-2) - {"203.0.113.0"}, {"203.0.113.255"}, // 203.0.113.0/24 RFC5737: Documentation (TEST-NET-3) - {"240.0.0.0"}, {"255.255.255.255"}, // 240.0.0.0/4 RFC1112: Reserved - {"::"}, {"::"}, // ::/128 RFC4291: Unspecified address - {"::1"}, {"::1"}, // ::1/128 RFC4291: Loopback address - {"100::"}, {"100::ffff:ffff:ffff:ffff"}, // 100::/64 RFC6666: Discard-Only Address Block - {"64:ff9b:1::"}, {"64:ff9b:1:ffff:ffff:ffff:ffff:ffff"}, // 64:ff9b:1::/48 RFC8215: IPv4-IPv6 Translation - {"2001:2::"}, {"2001:2:0:ffff:ffff:ffff:ffff:ffff"}, // 2001:2::/48 RFC5180,RFC Errata 1752: Benchmarking - {"2001:db8::"}, {"2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"}, // 2001:db8::/32 RFC3849: Documentation - {"2001::"}, {"2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff"}, // 2001::/23 RFC2928: IETF Protocol Assignments - {"5f00::"}, {"5f00:ffff:ffff:ffff:ffff:ffff:ffff:ffff"}, // 5f00::/16 RFC-ietf-6man-sids-06: Segment Routing (SRv6) - {"fe80::"}, {"febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"}, // fe80::/10 RFC4291: Link-Local Unicast - {"fc00::"}, {"fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"}, // fc00::/7 RFC4193, RFC8190: Unique Local - // some IPs in the middle of ranges - {"10.1.2.3"}, - {"100.64.3.2"}, - {"192.168.55.99"}, - {"2001:0DB8:0000:0000:face:b00c:0000:0000"}, - {"0100:0000:0000:0000:ffff:ffff:0000:0000"} - }; - } - - @Test - public void testIpAddressIpPrefix() - { - assertFunction("IP_PREFIX(IPADDRESS '1.2.3.4', 24)", IPPREFIX, "1.2.3.0/24"); - assertFunction("IP_PREFIX(IPADDRESS '1.2.3.4', 32)", IPPREFIX, "1.2.3.4/32"); - assertFunction("IP_PREFIX(IPADDRESS '1.2.3.4', 0)", IPPREFIX, "0.0.0.0/0"); - assertFunction("IP_PREFIX(IPADDRESS '::ffff:1.2.3.4', 24)", IPPREFIX, "1.2.3.0/24"); - assertFunction("IP_PREFIX(IPADDRESS '64:ff9b::17', 64)", IPPREFIX, "64:ff9b::/64"); - assertFunction("IP_PREFIX(IPADDRESS '64:ff9b::17', 127)", IPPREFIX, "64:ff9b::16/127"); - assertFunction("IP_PREFIX(IPADDRESS '64:ff9b::17', 128)", IPPREFIX, "64:ff9b::17/128"); - assertFunction("IP_PREFIX(IPADDRESS '64:ff9b::17', 0)", IPPREFIX, "::/0"); - assertInvalidFunction("IP_PREFIX(IPADDRESS '::ffff:1.2.3.4', -1)", "IPv4 subnet size must be in range [0, 32]"); - assertInvalidFunction("IP_PREFIX(IPADDRESS '::ffff:1.2.3.4', 33)", "IPv4 subnet size must be in range [0, 32]"); - assertInvalidFunction("IP_PREFIX(IPADDRESS '64:ff9b::10', -1)", "IPv6 subnet size must be in range [0, 128]"); - assertInvalidFunction("IP_PREFIX(IPADDRESS '64:ff9b::10', 129)", "IPv6 subnet size must be in range [0, 128]"); - } - - @Test - public void testStringIpPrefix() - { - assertFunction("IP_PREFIX('1.2.3.4', 24)", IPPREFIX, "1.2.3.0/24"); - assertFunction("IP_PREFIX('1.2.3.4', 32)", IPPREFIX, "1.2.3.4/32"); - assertFunction("IP_PREFIX('1.2.3.4', 0)", IPPREFIX, "0.0.0.0/0"); - assertFunction("IP_PREFIX('::ffff:1.2.3.4', 24)", IPPREFIX, "1.2.3.0/24"); - assertFunction("IP_PREFIX('64:ff9b::17', 64)", IPPREFIX, "64:ff9b::/64"); - assertFunction("IP_PREFIX('64:ff9b::17', 127)", IPPREFIX, "64:ff9b::16/127"); - assertFunction("IP_PREFIX('64:ff9b::17', 128)", IPPREFIX, "64:ff9b::17/128"); - assertFunction("IP_PREFIX('64:ff9b::17', 0)", IPPREFIX, "::/0"); - assertInvalidFunction("IP_PREFIX('::ffff:1.2.3.4', -1)", "IPv4 subnet size must be in range [0, 32]"); - assertInvalidFunction("IP_PREFIX('::ffff:1.2.3.4', 33)", "IPv4 subnet size must be in range [0, 32]"); - assertInvalidFunction("IP_PREFIX('64:ff9b::10', -1)", "IPv6 subnet size must be in range [0, 128]"); - assertInvalidFunction("IP_PREFIX('64:ff9b::10', 129)", "IPv6 subnet size must be in range [0, 128]"); - assertInvalidCast("IP_PREFIX('localhost', 24)", "Cannot cast value to IPADDRESS: localhost"); - assertInvalidCast("IP_PREFIX('64::ff9b::10', 24)", "Cannot cast value to IPADDRESS: 64::ff9b::10"); - assertInvalidCast("IP_PREFIX('64:face:book::10', 24)", "Cannot cast value to IPADDRESS: 64:face:book::10"); - assertInvalidCast("IP_PREFIX('123.456.789.012', 24)", "Cannot cast value to IPADDRESS: 123.456.789.012"); - } - - @Test - public void testIpSubnetMin() - { - assertFunction("IP_SUBNET_MIN(IPPREFIX '1.2.3.4/24')", IPADDRESS, "1.2.3.0"); - assertFunction("IP_SUBNET_MIN(IPPREFIX '1.2.3.4/32')", IPADDRESS, "1.2.3.4"); - assertFunction("IP_SUBNET_MIN(IPPREFIX '64:ff9b::17/64')", IPADDRESS, "64:ff9b::"); - assertFunction("IP_SUBNET_MIN(IPPREFIX '64:ff9b::17/127')", IPADDRESS, "64:ff9b::16"); - assertFunction("IP_SUBNET_MIN(IPPREFIX '64:ff9b::17/128')", IPADDRESS, "64:ff9b::17"); - assertFunction("IP_SUBNET_MIN(IPPREFIX '64:ff9b::17/0')", IPADDRESS, "::"); - } - - @Test - public void testIpSubnetMax() - { - assertFunction("IP_SUBNET_MAX(IPPREFIX '1.2.3.128/26')", IPADDRESS, "1.2.3.191"); - assertFunction("IP_SUBNET_MAX(IPPREFIX '192.168.128.4/32')", IPADDRESS, "192.168.128.4"); - assertFunction("IP_SUBNET_MAX(IPPREFIX '10.1.16.3/9')", IPADDRESS, "10.127.255.255"); - assertFunction("IP_SUBNET_MAX(IPPREFIX '2001:db8::16/127')", IPADDRESS, "2001:db8::17"); - assertFunction("IP_SUBNET_MAX(IPPREFIX '2001:db8::16/128')", IPADDRESS, "2001:db8::16"); - assertFunction("IP_SUBNET_MAX(IPPREFIX '64:ff9b::17/64')", IPADDRESS, "64:ff9b::ffff:ffff:ffff:ffff"); - assertFunction("IP_SUBNET_MAX(IPPREFIX '64:ff9b::17/72')", IPADDRESS, "64:ff9b::ff:ffff:ffff:ffff"); - assertFunction("IP_SUBNET_MAX(IPPREFIX '64:ff9b::17/0')", IPADDRESS, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); - } - - @Test - public void testIpSubnetRange() - { - assertFunction("IP_SUBNET_RANGE(IPPREFIX '1.2.3.160/24')", new ArrayType(IPADDRESS), ImmutableList.of("1.2.3.0", "1.2.3.255")); - assertFunction("IP_SUBNET_RANGE(IPPREFIX '1.2.3.128/31')", new ArrayType(IPADDRESS), ImmutableList.of("1.2.3.128", "1.2.3.129")); - assertFunction("IP_SUBNET_RANGE(IPPREFIX '10.1.6.46/32')", new ArrayType(IPADDRESS), ImmutableList.of("10.1.6.46", "10.1.6.46")); - assertFunction("IP_SUBNET_RANGE(IPPREFIX '10.1.6.46/0')", new ArrayType(IPADDRESS), ImmutableList.of("0.0.0.0", "255.255.255.255")); - assertFunction("IP_SUBNET_RANGE(IPPREFIX '64:ff9b::17/64')", new ArrayType(IPADDRESS), ImmutableList.of("64:ff9b::", "64:ff9b::ffff:ffff:ffff:ffff")); - assertFunction("IP_SUBNET_RANGE(IPPREFIX '64:ff9b::52f4/120')", new ArrayType(IPADDRESS), ImmutableList.of("64:ff9b::5200", "64:ff9b::52ff")); - assertFunction("IP_SUBNET_RANGE(IPPREFIX '64:ff9b::17/128')", new ArrayType(IPADDRESS), ImmutableList.of("64:ff9b::17", "64:ff9b::17")); - } - - @Test - public void testIsSubnetOf() - { - assertFunction("IS_SUBNET_OF(IPPREFIX '1.2.3.128/26', IPADDRESS '1.2.3.129')", BOOLEAN, true); - assertFunction("IS_SUBNET_OF(IPPREFIX '1.2.3.128/26', IPADDRESS '1.2.5.1')", BOOLEAN, false); - assertFunction("IS_SUBNET_OF(IPPREFIX '1.2.3.128/32', IPADDRESS '1.2.3.128')", BOOLEAN, true); - assertFunction("IS_SUBNET_OF(IPPREFIX '1.2.3.128/0', IPADDRESS '192.168.5.1')", BOOLEAN, true); - assertFunction("IS_SUBNET_OF(IPPREFIX '64:ff9b::17/64', IPADDRESS '64:ff9b::ffff:ff')", BOOLEAN, true); - assertFunction("IS_SUBNET_OF(IPPREFIX '64:ff9b::17/64', IPADDRESS '64:ffff::17')", BOOLEAN, false); - - assertFunction("IS_SUBNET_OF(IPPREFIX '192.168.3.131/26', IPPREFIX '192.168.3.144/30')", BOOLEAN, true); - assertFunction("IS_SUBNET_OF(IPPREFIX '1.2.3.128/26', IPPREFIX '1.2.5.1/30')", BOOLEAN, false); - assertFunction("IS_SUBNET_OF(IPPREFIX '1.2.3.128/26', IPPREFIX '1.2.3.128/26')", BOOLEAN, true); - assertFunction("IS_SUBNET_OF(IPPREFIX '64:ff9b::17/64', IPPREFIX '64:ff9b::ff:25/80')", BOOLEAN, true); - assertFunction("IS_SUBNET_OF(IPPREFIX '64:ff9b::17/64', IPPREFIX '64:ffff::17/64')", BOOLEAN, false); - assertFunction("IS_SUBNET_OF(IPPREFIX '2804:431:b000::/37', IPPREFIX '2804:431:b000::/38')", BOOLEAN, true); - assertFunction("IS_SUBNET_OF(IPPREFIX '2804:431:b000::/38', IPPREFIX '2804:431:b000::/37')", BOOLEAN, false); - assertFunction("IS_SUBNET_OF(IPPREFIX '170.0.52.0/22', IPPREFIX '170.0.52.0/24')", BOOLEAN, true); - assertFunction("IS_SUBNET_OF(IPPREFIX '170.0.52.0/24', IPPREFIX '170.0.52.0/22')", BOOLEAN, false); - } - - @Test - public void testIpv4PrefixCollapse() - { - // simple - assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.0/24', IPPREFIX '192.168.1.0/24'])", - new ArrayType(IPPREFIX), - ImmutableList.of("192.168.0.0/23")); - - // unsorted input, 1 adjacent prefix that cannot be aggregated, and one disjoint. - assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.1.0/24', IPPREFIX '192.168.0.0/24', IPPREFIX '192.168.2.0/24', IPPREFIX '192.168.9.0/24'])", - new ArrayType(IPPREFIX), - ImmutableList.of("192.168.0.0/23", "192.168.2.0/24", "192.168.9.0/24")); - } - - @Test - public void testIpv6PrefixCollapse() - { - // simple - assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '2620:10d:c090::/48', IPPREFIX '2620:10d:c091::/48'])", - new ArrayType(IPPREFIX), - ImmutableList.of("2620:10d:c090::/47")); - - // unsorted input, 1 adjacent prefix that cannot be aggregated, and one disjoint. - assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '2804:13c:4d6:e200::/56', IPPREFIX '2804:13c:4d6:dd00::/56', IPPREFIX '2804:13c:4d6:dc00::/56', IPPREFIX '2804:13c:4d6:de00::/56'])", - new ArrayType(IPPREFIX), - ImmutableList.of("2804:13c:4d6:dc00::/55", "2804:13c:4d6:de00::/56", "2804:13c:4d6:e200::/56")); - } - - @Test - public void testIpPrefixCollapseIpv4SingleIPs() - { - assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.1/32', IPPREFIX '192.168.33.1/32'])", - new ArrayType(IPPREFIX), - ImmutableList.of("192.168.0.1/32", "192.168.33.1/32")); - } - - @Test - public void testIpPrefixCollapseIpv6SingleIPs() - { - assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '2620:10d:c090:400::5:a869/128', IPPREFIX '2620:10d:c091:400::5:a869/128'])", - new ArrayType(IPPREFIX), - ImmutableList.of("2620:10d:c090:400::5:a869/128", "2620:10d:c091:400::5:a869/128")); - } - - @Test - public void testIpPrefixCollapseSinglePrefixReturnsSamePrefix() - { - assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.0/22'])", - new ArrayType(IPPREFIX), - ImmutableList.of("192.168.0.0/22")); - } - - @Test - public void testIpPrefixCollapseOverlappingPrefixes() - { - assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.0/22', IPPREFIX '192.168.0.0/24'])", - new ArrayType(IPPREFIX), - ImmutableList.of("192.168.0.0/22")); - assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.0/22', IPPREFIX '192.168.2.0/24'])", - new ArrayType(IPPREFIX), - ImmutableList.of("192.168.0.0/22")); - assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.0/22', IPPREFIX '192.168.3.0/24'])", - new ArrayType(IPPREFIX), - ImmutableList.of("192.168.0.0/22")); - assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '10.0.64.0/18', IPPREFIX '10.2.0.0/15', IPPREFIX '10.0.0.0/8', IPPREFIX '11.0.0.0/8', IPPREFIX '172.168.32.0/20', IPPREFIX '172.168.0.0/18'])", - new ArrayType(IPPREFIX), - ImmutableList.of("10.0.0.0/7", "172.168.0.0/18")); - assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '10.0.0.0/8', IPPREFIX '10.0.0.0/7'])", - new ArrayType(IPPREFIX), - ImmutableList.of("10.0.0.0/7")); - } - - @Test - public void testIpPrefixCollapseEmptyArrayInput() - { - assertFunction("IP_PREFIX_COLLAPSE(CAST(ARRAY[] AS ARRAY(IPPREFIX)))", new ArrayType(IPPREFIX), ImmutableList.of()); - } - - @Test - public void testIpPrefixCollapseNullInput() - { - assertFunction("IP_PREFIX_COLLAPSE(CAST(NULL AS ARRAY(IPPREFIX)))", new ArrayType(IPPREFIX), null); - } - - @Test - public void testIpPrefixCollapseNoNullPrefixesError() - { - assertInvalidFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.0/22', CAST(NULL AS IPPREFIX)])", - "ip_prefix_collapse does not support null elements"); - } - - @Test - public void testIpPrefixCollapseMixedIpVersionError() - { - assertInvalidFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.0/22', IPPREFIX '2409:4043:251a:d200::/56'])", - "All IPPREFIX elements must be the same IP version."); - } - - @Test (dataProvider = "private-ip-provider") - public void testIsPrivateTrue(String ipAddress) - { - assertFunction("IS_PRIVATE_IP(IPADDRESS '" + ipAddress + "')", BOOLEAN, true); - } - - @Test (dataProvider = "public-ip-provider") - public void testIsPrivateIpFalse(String ipAddress) - { - assertFunction("IS_PRIVATE_IP(IPADDRESS '" + ipAddress + "')", BOOLEAN, false); - } - - @Test - public void testIsPrivateIpNull() - { - assertFunction("IS_PRIVATE_IP(NULL)", BOOLEAN, null); - } - - @Test - public void testIpPrefixSubnets() - { - assertFunction("IP_PREFIX_SUBNETS(IPPREFIX '192.168.1.0/24', 25)", new ArrayType(IPPREFIX), ImmutableList.of("192.168.1.0/25", "192.168.1.128/25")); - assertFunction("IP_PREFIX_SUBNETS(IPPREFIX '192.168.0.0/24', 26)", new ArrayType(IPPREFIX), ImmutableList.of("192.168.0.0/26", "192.168.0.64/26", "192.168.0.128/26", "192.168.0.192/26")); - assertFunction("IP_PREFIX_SUBNETS(IPPREFIX '2A03:2880:C000::/34', 37)", - new ArrayType(IPPREFIX), - ImmutableList.of("2a03:2880:c000::/37", "2a03:2880:c800::/37", "2a03:2880:d000::/37", "2a03:2880:d800::/37", "2a03:2880:e000::/37", "2a03:2880:e800::/37", "2a03:2880:f000::/37", "2a03:2880:f800::/37")); - } - - @Test - public void testIpPrefixSubnetsReturnSelf() - { - assertFunction("IP_PREFIX_SUBNETS(IPPREFIX '192.168.1.0/24', 24)", new ArrayType(IPPREFIX), ImmutableList.of("192.168.1.0/24")); - assertFunction("IP_PREFIX_SUBNETS(IPPREFIX '2804:431:b000::/38', 38)", new ArrayType(IPPREFIX), ImmutableList.of("2804:431:b000::/38")); - } - - @Test - public void testIpPrefixSubnetsNewPrefixLengthLongerReturnsEmpty() - { - assertFunction("IP_PREFIX_SUBNETS(IPPREFIX '192.168.0.0/24', 23)", new ArrayType(IPPREFIX), ImmutableList.of()); - assertFunction("IP_PREFIX_SUBNETS(IPPREFIX '64:ff9b::17/64', 48)", new ArrayType(IPPREFIX), ImmutableList.of()); - } - - @Test - public void testIpPrefixSubnetsInvalidPrefixLengths() - { - assertInvalidFunction("IP_PREFIX_SUBNETS(IPPREFIX '192.168.0.0/24', -1)", "Invalid prefix length for IPv4: -1"); - assertInvalidFunction("IP_PREFIX_SUBNETS(IPPREFIX '192.168.0.0/24', 33)", "Invalid prefix length for IPv4: 33"); - assertInvalidFunction("IP_PREFIX_SUBNETS(IPPREFIX '64:ff9b::17/64', -1)", "Invalid prefix length for IPv6: -1"); - assertInvalidFunction("IP_PREFIX_SUBNETS(IPPREFIX '64:ff9b::17/64', 129)", "Invalid prefix length for IPv6: 129"); - } } diff --git a/presto-main-base/src/test/java/com/facebook/presto/sql/planner/sanity/TestCheckUnsupportedPrestissimoTypes.java b/presto-main-base/src/test/java/com/facebook/presto/sql/planner/sanity/TestCheckUnsupportedPrestissimoTypes.java index d19e911fc2f60..8ba19fb23f05f 100644 --- a/presto-main-base/src/test/java/com/facebook/presto/sql/planner/sanity/TestCheckUnsupportedPrestissimoTypes.java +++ b/presto-main-base/src/test/java/com/facebook/presto/sql/planner/sanity/TestCheckUnsupportedPrestissimoTypes.java @@ -31,11 +31,11 @@ import static com.facebook.presto.SessionTestUtils.TEST_SESSION; import static com.facebook.presto.common.type.BooleanType.BOOLEAN; +import static com.facebook.presto.common.type.IpAddressType.IPADDRESS; import static com.facebook.presto.common.type.TimestampWithTimeZoneType.TIMESTAMP_WITH_TIME_ZONE; import static com.facebook.presto.common.type.VarcharType.VARCHAR; import static com.facebook.presto.sql.planner.iterative.rule.test.PlanBuilder.assignment; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; -import static com.facebook.presto.type.IpAddressType.IPADDRESS; public class TestCheckUnsupportedPrestissimoTypes extends BasePlanTest diff --git a/presto-main-base/src/test/java/com/facebook/presto/type/TestIpAddressOperators.java b/presto-main-base/src/test/java/com/facebook/presto/type/TestIpAddressOperators.java index 7c1d54dbfad49..b83540d2c05c4 100644 --- a/presto-main-base/src/test/java/com/facebook/presto/type/TestIpAddressOperators.java +++ b/presto-main-base/src/test/java/com/facebook/presto/type/TestIpAddressOperators.java @@ -25,9 +25,9 @@ import static com.facebook.presto.common.function.OperatorType.INDETERMINATE; import static com.facebook.presto.common.type.BigintType.BIGINT; import static com.facebook.presto.common.type.BooleanType.BOOLEAN; +import static com.facebook.presto.common.type.IpAddressType.IPADDRESS; import static com.facebook.presto.common.type.VarbinaryType.VARBINARY; import static com.facebook.presto.common.type.VarcharType.VARCHAR; -import static com.facebook.presto.type.IpAddressType.IPADDRESS; import static com.google.common.io.BaseEncoding.base16; public class TestIpAddressOperators diff --git a/presto-main-base/src/test/java/com/facebook/presto/type/TestIpAddressType.java b/presto-main-base/src/test/java/com/facebook/presto/type/TestIpAddressType.java index cacd4b3ad252c..39a4ec375d253 100644 --- a/presto-main-base/src/test/java/com/facebook/presto/type/TestIpAddressType.java +++ b/presto-main-base/src/test/java/com/facebook/presto/type/TestIpAddressType.java @@ -20,7 +20,7 @@ import io.airlift.slice.Slices; import org.testng.annotations.Test; -import static com.facebook.presto.type.IpAddressType.IPADDRESS; +import static com.facebook.presto.common.type.IpAddressType.IPADDRESS; import static org.testng.Assert.assertEquals; public class TestIpAddressType diff --git a/presto-main-base/src/test/java/com/facebook/presto/type/TestIpPrefixOperators.java b/presto-main-base/src/test/java/com/facebook/presto/type/TestIpPrefixOperators.java index cbd9dff770e28..77f3280787539 100644 --- a/presto-main-base/src/test/java/com/facebook/presto/type/TestIpPrefixOperators.java +++ b/presto-main-base/src/test/java/com/facebook/presto/type/TestIpPrefixOperators.java @@ -24,9 +24,9 @@ import static com.facebook.presto.common.function.OperatorType.INDETERMINATE; import static com.facebook.presto.common.type.BigintType.BIGINT; import static com.facebook.presto.common.type.BooleanType.BOOLEAN; +import static com.facebook.presto.common.type.IpAddressType.IPADDRESS; +import static com.facebook.presto.common.type.IpPrefixType.IPPREFIX; import static com.facebook.presto.common.type.VarcharType.VARCHAR; -import static com.facebook.presto.type.IpAddressType.IPADDRESS; -import static com.facebook.presto.type.IpPrefixType.IPPREFIX; import static java.lang.System.arraycopy; public class TestIpPrefixOperators diff --git a/presto-main-base/src/test/java/com/facebook/presto/type/TestIpPrefixType.java b/presto-main-base/src/test/java/com/facebook/presto/type/TestIpPrefixType.java index 50b5eb672708a..7a3a6d94337d7 100644 --- a/presto-main-base/src/test/java/com/facebook/presto/type/TestIpPrefixType.java +++ b/presto-main-base/src/test/java/com/facebook/presto/type/TestIpPrefixType.java @@ -20,7 +20,7 @@ import io.airlift.slice.Slices; import org.testng.annotations.Test; -import static com.facebook.presto.type.IpPrefixType.IPPREFIX; +import static com.facebook.presto.common.type.IpPrefixType.IPPREFIX; import static com.google.common.base.Preconditions.checkState; import static java.lang.System.arraycopy; import static org.testng.Assert.assertEquals; diff --git a/presto-main-tests/src/main/java/com/facebook/presto/tests/operator/scalar/AbstractTestIpPrefix.java b/presto-main-tests/src/main/java/com/facebook/presto/tests/operator/scalar/AbstractTestIpPrefix.java new file mode 100644 index 0000000000000..b3ade52608e85 --- /dev/null +++ b/presto-main-tests/src/main/java/com/facebook/presto/tests/operator/scalar/AbstractTestIpPrefix.java @@ -0,0 +1,324 @@ + +/* + * Licensed 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 com.facebook.presto.tests.operator.scalar; + +import com.facebook.presto.common.type.ArrayType; +import com.google.common.collect.ImmutableList; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static com.facebook.presto.common.type.BooleanType.BOOLEAN; +import static com.facebook.presto.common.type.IpAddressType.IPADDRESS; +import static com.facebook.presto.common.type.IpPrefixType.IPPREFIX; + +public interface AbstractTestIpPrefix + extends TestFunctions +{ + @DataProvider(name = "public-ip-provider") + default Object[] publicIpProvider() + { + return new Object[] { + "6.7.8.9", + "157.240.200.99", + "8.8.8.8", + "128.1.2.8", + "2a03:2880:f031:12:face:b00c:0:2", + "2600:1406:6c00::173c:ad43", + "2607:f8b0:4007:818::2004" + }; + } + + @DataProvider(name = "private-ip-provider") + default Object[][] privateIpProvider() + { + return new Object[][] { + // The first and last IP address in each private range + {"0.0.0.0"}, {"0.255.255.255"}, // 0.0.0.0/8 RFC1122: "This host on this network" + {"10.0.0.0"}, {"10.255.255.255"}, // 10.0.0.0/8 RFC1918: Private-Use + {"100.64.0.0"}, {"100.127.255.255"}, // 100.64.0.0/10 RFC6598: Shared Address Space + {"127.0.0.0"}, {"127.255.255.255"}, // 127.0.0.0/8 RFC1122: Loopback + {"169.254.0.0"}, {"169.254.255.255"}, // 169.254.0.0/16 RFC3927: Link Local + {"172.16.0.0"}, {"172.31.255.255"}, // 172.16.0.0/12 RFC1918: Private-Use + {"192.0.0.0"}, {"192.0.0.255"}, // 192.0.0.0/24 RFC6890: IETF Protocol Assignments + {"192.0.2.0"}, {"192.0.2.255"}, // 192.0.2.0/24 RFC5737: Documentation (TEST-NET-1) + {"192.88.99.0"}, {"192.88.99.255"}, // 192.88.99.0/24 RFC3068: 6to4 Relay anycast + {"192.168.0.0"}, {"192.168.255.255"}, // 192.168.0.0/16 RFC1918: Private-Use + {"198.18.0.0"}, {"198.19.255.255"}, // 198.18.0.0/15 RFC2544: Benchmarking + {"198.51.100.0"}, {"198.51.100.255"}, // 198.51.100.0/24 RFC5737: Documentation (TEST-NET-2) + {"203.0.113.0"}, {"203.0.113.255"}, // 203.0.113.0/24 RFC5737: Documentation (TEST-NET-3) + {"240.0.0.0"}, {"255.255.255.255"}, // 240.0.0.0/4 RFC1112: Reserved + {"::"}, {"::"}, // ::/128 RFC4291: Unspecified address + {"::1"}, {"::1"}, // ::1/128 RFC4291: Loopback address + {"100::"}, {"100::ffff:ffff:ffff:ffff"}, // 100::/64 RFC6666: Discard-Only Address Block + {"64:ff9b:1::"}, {"64:ff9b:1:ffff:ffff:ffff:ffff:ffff"}, // 64:ff9b:1::/48 RFC8215: IPv4-IPv6 Translation + {"2001:2::"}, {"2001:2:0:ffff:ffff:ffff:ffff:ffff"}, // 2001:2::/48 RFC5180,RFC Errata 1752: Benchmarking + {"2001:db8::"}, {"2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"}, // 2001:db8::/32 RFC3849: Documentation + {"2001::"}, {"2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff"}, // 2001::/23 RFC2928: IETF Protocol Assignments + {"5f00::"}, {"5f00:ffff:ffff:ffff:ffff:ffff:ffff:ffff"}, // 5f00::/16 RFC-ietf-6man-sids-06: Segment Routing (SRv6) + {"fe80::"}, {"febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"}, // fe80::/10 RFC4291: Link-Local Unicast + {"fc00::"}, {"fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"}, // fc00::/7 RFC4193, RFC8190: Unique Local + // some IPs in the middle of ranges + {"10.1.2.3"}, + {"100.64.3.2"}, + {"192.168.55.99"}, + {"2001:0DB8:0000:0000:face:b00c:0000:0000"}, + {"0100:0000:0000:0000:ffff:ffff:0000:0000"} + }; + } + + @Test + default void testIpAddressIpPrefix() + { + assertFunction("IP_PREFIX(IPADDRESS '1.2.3.4', 24)", IPPREFIX, "1.2.3.0/24"); + assertFunction("IP_PREFIX(IPADDRESS '1.2.3.4', 32)", IPPREFIX, "1.2.3.4/32"); + assertFunction("IP_PREFIX(IPADDRESS '1.2.3.4', 0)", IPPREFIX, "0.0.0.0/0"); + assertFunction("IP_PREFIX(IPADDRESS '::ffff:1.2.3.4', 24)", IPPREFIX, "1.2.3.0/24"); + assertFunction("IP_PREFIX(IPADDRESS '64:ff9b::17', 64)", IPPREFIX, "64:ff9b::/64"); + assertFunction("IP_PREFIX(IPADDRESS '64:ff9b::17', 127)", IPPREFIX, "64:ff9b::16/127"); + assertFunction("IP_PREFIX(IPADDRESS '64:ff9b::17', 128)", IPPREFIX, "64:ff9b::17/128"); + assertFunction("IP_PREFIX(IPADDRESS '64:ff9b::17', 0)", IPPREFIX, "::/0"); + assertInvalidFunction("IP_PREFIX(IPADDRESS '::ffff:1.2.3.4', -1)", "IPv4 subnet size must be in range [0, 32]"); + assertInvalidFunction("IP_PREFIX(IPADDRESS '::ffff:1.2.3.4', 33)", "IPv4 subnet size must be in range [0, 32]"); + assertInvalidFunction("IP_PREFIX(IPADDRESS '64:ff9b::10', -1)", "IPv6 subnet size must be in range [0, 128]"); + assertInvalidFunction("IP_PREFIX(IPADDRESS '64:ff9b::10', 129)", "IPv6 subnet size must be in range [0, 128]"); + } + + @Test + default void testStringIpPrefix() + { + assertFunction("IP_PREFIX('1.2.3.4', 24)", IPPREFIX, "1.2.3.0/24"); + assertFunction("IP_PREFIX('1.2.3.4', 32)", IPPREFIX, "1.2.3.4/32"); + assertFunction("IP_PREFIX('1.2.3.4', 0)", IPPREFIX, "0.0.0.0/0"); + assertFunction("IP_PREFIX('::ffff:1.2.3.4', 24)", IPPREFIX, "1.2.3.0/24"); + assertFunction("IP_PREFIX('64:ff9b::17', 64)", IPPREFIX, "64:ff9b::/64"); + assertFunction("IP_PREFIX('64:ff9b::17', 127)", IPPREFIX, "64:ff9b::16/127"); + assertFunction("IP_PREFIX('64:ff9b::17', 128)", IPPREFIX, "64:ff9b::17/128"); + assertFunction("IP_PREFIX('64:ff9b::17', 0)", IPPREFIX, "::/0"); + assertInvalidFunction("IP_PREFIX('::ffff:1.2.3.4', -1)", "IPv4 subnet size must be in range [0, 32]"); + assertInvalidFunction("IP_PREFIX('::ffff:1.2.3.4', 33)", "IPv4 subnet size must be in range [0, 32]"); + assertInvalidFunction("IP_PREFIX('64:ff9b::10', -1)", "IPv6 subnet size must be in range [0, 128]"); + assertInvalidFunction("IP_PREFIX('64:ff9b::10', 129)", "IPv6 subnet size must be in range [0, 128]"); + assertInvalidCast("IP_PREFIX('localhost', 24)", "Cannot cast value to IPADDRESS: localhost"); + assertInvalidCast("IP_PREFIX('64::ff9b::10', 24)", "Cannot cast value to IPADDRESS: 64::ff9b::10"); + assertInvalidCast("IP_PREFIX('64:face:book::10', 24)", "Cannot cast value to IPADDRESS: 64:face:book::10"); + assertInvalidCast("IP_PREFIX('123.456.789.012', 24)", "Cannot cast value to IPADDRESS: 123.456.789.012"); + } + + @Test + default void testIpSubnetMin() + { + assertFunction("IP_SUBNET_MIN(IPPREFIX '1.2.3.4/24')", IPADDRESS, "1.2.3.0"); + assertFunction("IP_SUBNET_MIN(IPPREFIX '1.2.3.4/32')", IPADDRESS, "1.2.3.4"); + assertFunction("IP_SUBNET_MIN(IPPREFIX '64:ff9b::17/64')", IPADDRESS, "64:ff9b::"); + assertFunction("IP_SUBNET_MIN(IPPREFIX '64:ff9b::17/127')", IPADDRESS, "64:ff9b::16"); + assertFunction("IP_SUBNET_MIN(IPPREFIX '64:ff9b::17/128')", IPADDRESS, "64:ff9b::17"); + assertFunction("IP_SUBNET_MIN(IPPREFIX '64:ff9b::17/0')", IPADDRESS, "::"); + } + + @Test + default void testIpSubnetMax() + { + assertFunction("IP_SUBNET_MAX(IPPREFIX '1.2.3.128/26')", IPADDRESS, "1.2.3.191"); + assertFunction("IP_SUBNET_MAX(IPPREFIX '192.168.128.4/32')", IPADDRESS, "192.168.128.4"); + assertFunction("IP_SUBNET_MAX(IPPREFIX '10.1.16.3/9')", IPADDRESS, "10.127.255.255"); + assertFunction("IP_SUBNET_MAX(IPPREFIX '2001:db8::16/127')", IPADDRESS, "2001:db8::17"); + assertFunction("IP_SUBNET_MAX(IPPREFIX '2001:db8::16/128')", IPADDRESS, "2001:db8::16"); + assertFunction("IP_SUBNET_MAX(IPPREFIX '64:ff9b::17/64')", IPADDRESS, "64:ff9b::ffff:ffff:ffff:ffff"); + assertFunction("IP_SUBNET_MAX(IPPREFIX '64:ff9b::17/72')", IPADDRESS, "64:ff9b::ff:ffff:ffff:ffff"); + assertFunction("IP_SUBNET_MAX(IPPREFIX '64:ff9b::17/0')", IPADDRESS, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); + } + + @Test + default void testIpSubnetRange() + { + assertFunction("IP_SUBNET_RANGE(IPPREFIX '1.2.3.160/24')", new ArrayType(IPADDRESS), ImmutableList.of("1.2.3.0", "1.2.3.255")); + assertFunction("IP_SUBNET_RANGE(IPPREFIX '1.2.3.128/31')", new ArrayType(IPADDRESS), ImmutableList.of("1.2.3.128", "1.2.3.129")); + assertFunction("IP_SUBNET_RANGE(IPPREFIX '10.1.6.46/32')", new ArrayType(IPADDRESS), ImmutableList.of("10.1.6.46", "10.1.6.46")); + assertFunction("IP_SUBNET_RANGE(IPPREFIX '10.1.6.46/0')", new ArrayType(IPADDRESS), ImmutableList.of("0.0.0.0", "255.255.255.255")); + assertFunction("IP_SUBNET_RANGE(IPPREFIX '64:ff9b::17/64')", new ArrayType(IPADDRESS), ImmutableList.of("64:ff9b::", "64:ff9b::ffff:ffff:ffff:ffff")); + assertFunction("IP_SUBNET_RANGE(IPPREFIX '64:ff9b::52f4/120')", new ArrayType(IPADDRESS), ImmutableList.of("64:ff9b::5200", "64:ff9b::52ff")); + assertFunction("IP_SUBNET_RANGE(IPPREFIX '64:ff9b::17/128')", new ArrayType(IPADDRESS), ImmutableList.of("64:ff9b::17", "64:ff9b::17")); + } + + @Test + default void testIsSubnetOf() + { + assertFunction("IS_SUBNET_OF(IPPREFIX '1.2.3.128/26', IPADDRESS '1.2.3.129')", BOOLEAN, true); + assertFunction("IS_SUBNET_OF(IPPREFIX '1.2.3.128/26', IPADDRESS '1.2.5.1')", BOOLEAN, false); + assertFunction("IS_SUBNET_OF(IPPREFIX '1.2.3.128/32', IPADDRESS '1.2.3.128')", BOOLEAN, true); + assertFunction("IS_SUBNET_OF(IPPREFIX '1.2.3.128/0', IPADDRESS '192.168.5.1')", BOOLEAN, true); + assertFunction("IS_SUBNET_OF(IPPREFIX '64:ff9b::17/64', IPADDRESS '64:ff9b::ffff:ff')", BOOLEAN, true); + assertFunction("IS_SUBNET_OF(IPPREFIX '64:ff9b::17/64', IPADDRESS '64:ffff::17')", BOOLEAN, false); + + assertFunction("IS_SUBNET_OF(IPPREFIX '192.168.3.131/26', IPPREFIX '192.168.3.144/30')", BOOLEAN, true); + assertFunction("IS_SUBNET_OF(IPPREFIX '1.2.3.128/26', IPPREFIX '1.2.5.1/30')", BOOLEAN, false); + assertFunction("IS_SUBNET_OF(IPPREFIX '1.2.3.128/26', IPPREFIX '1.2.3.128/26')", BOOLEAN, true); + assertFunction("IS_SUBNET_OF(IPPREFIX '64:ff9b::17/64', IPPREFIX '64:ff9b::ff:25/80')", BOOLEAN, true); + assertFunction("IS_SUBNET_OF(IPPREFIX '64:ff9b::17/64', IPPREFIX '64:ffff::17/64')", BOOLEAN, false); + assertFunction("IS_SUBNET_OF(IPPREFIX '2804:431:b000::/37', IPPREFIX '2804:431:b000::/38')", BOOLEAN, true); + assertFunction("IS_SUBNET_OF(IPPREFIX '2804:431:b000::/38', IPPREFIX '2804:431:b000::/37')", BOOLEAN, false); + assertFunction("IS_SUBNET_OF(IPPREFIX '170.0.52.0/22', IPPREFIX '170.0.52.0/24')", BOOLEAN, true); + assertFunction("IS_SUBNET_OF(IPPREFIX '170.0.52.0/24', IPPREFIX '170.0.52.0/22')", BOOLEAN, false); + } + + @Test + default void testIpv4PrefixCollapse() + { + // simple + assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.0/24', IPPREFIX '192.168.1.0/24'])", + new ArrayType(IPPREFIX), + ImmutableList.of("192.168.0.0/23")); + + // unsorted input, 1 adjacent prefix that cannot be aggregated, and one disjoint. + assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.1.0/24', IPPREFIX '192.168.0.0/24', IPPREFIX '192.168.2.0/24', IPPREFIX '192.168.9.0/24'])", + new ArrayType(IPPREFIX), + ImmutableList.of("192.168.0.0/23", "192.168.2.0/24", "192.168.9.0/24")); + } + + @Test + default void testIpv6PrefixCollapse() + { + // simple + assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '2620:10d:c090::/48', IPPREFIX '2620:10d:c091::/48'])", + new ArrayType(IPPREFIX), + ImmutableList.of("2620:10d:c090::/47")); + + // unsorted input, 1 adjacent prefix that cannot be aggregated, and one disjoint. + assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '2804:13c:4d6:e200::/56', IPPREFIX '2804:13c:4d6:dd00::/56', IPPREFIX '2804:13c:4d6:dc00::/56', IPPREFIX '2804:13c:4d6:de00::/56'])", + new ArrayType(IPPREFIX), + ImmutableList.of("2804:13c:4d6:dc00::/55", "2804:13c:4d6:de00::/56", "2804:13c:4d6:e200::/56")); + } + + @Test + default void testIpPrefixCollapseIpv4SingleIPs() + { + assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.1/32', IPPREFIX '192.168.33.1/32'])", + new ArrayType(IPPREFIX), + ImmutableList.of("192.168.0.1/32", "192.168.33.1/32")); + } + + @Test + default void testIpPrefixCollapseIpv6SingleIPs() + { + assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '2620:10d:c090:400::5:a869/128', IPPREFIX '2620:10d:c091:400::5:a869/128'])", + new ArrayType(IPPREFIX), + ImmutableList.of("2620:10d:c090:400::5:a869/128", "2620:10d:c091:400::5:a869/128")); + } + + @Test + default void testIpPrefixCollapseSinglePrefixReturnsSamePrefix() + { + assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.0/22'])", + new ArrayType(IPPREFIX), + ImmutableList.of("192.168.0.0/22")); + } + + @Test + default void testIpPrefixCollapseOverlappingPrefixes() + { + assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.0/22', IPPREFIX '192.168.0.0/24'])", + new ArrayType(IPPREFIX), + ImmutableList.of("192.168.0.0/22")); + assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.0/22', IPPREFIX '192.168.2.0/24'])", + new ArrayType(IPPREFIX), + ImmutableList.of("192.168.0.0/22")); + assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.0/22', IPPREFIX '192.168.3.0/24'])", + new ArrayType(IPPREFIX), + ImmutableList.of("192.168.0.0/22")); + assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '10.0.64.0/18', IPPREFIX '10.2.0.0/15', IPPREFIX '10.0.0.0/8', IPPREFIX '11.0.0.0/8', IPPREFIX '172.168.32.0/20', IPPREFIX '172.168.0.0/18'])", + new ArrayType(IPPREFIX), + ImmutableList.of("10.0.0.0/7", "172.168.0.0/18")); + assertFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '10.0.0.0/8', IPPREFIX '10.0.0.0/7'])", + new ArrayType(IPPREFIX), + ImmutableList.of("10.0.0.0/7")); + } + + @Test + default void testIpPrefixCollapseEmptyArrayInput() + { + assertFunction("IP_PREFIX_COLLAPSE(CAST(ARRAY[] AS ARRAY(IPPREFIX)))", new ArrayType(IPPREFIX), ImmutableList.of()); + } + + @Test + default void testIpPrefixCollapseNullInput() + { + assertFunction("IP_PREFIX_COLLAPSE(CAST(NULL AS ARRAY(IPPREFIX)))", new ArrayType(IPPREFIX), null); + } + + @Test + default void testIpPrefixCollapseNoNullPrefixesError() + { + assertInvalidFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.0/22', CAST(NULL AS IPPREFIX)])", + "ip_prefix_collapse does not support null elements"); + } + + @Test + default void testIpPrefixCollapseMixedIpVersionError() + { + assertInvalidFunction("IP_PREFIX_COLLAPSE(ARRAY[IPPREFIX '192.168.0.0/22', IPPREFIX '2409:4043:251a:d200::/56'])", + "All IPPREFIX elements must be the same IP version."); + } + + @Test (dataProvider = "private-ip-provider") + default void testIsPrivateTrue(String ipAddress) + { + assertFunction("IS_PRIVATE_IP(IPADDRESS '" + ipAddress + "')", BOOLEAN, true); + } + + @Test (dataProvider = "public-ip-provider") + default void testIsPrivateIpFalse(String ipAddress) + { + assertFunction("IS_PRIVATE_IP(IPADDRESS '" + ipAddress + "')", BOOLEAN, false); + } + + @Test + default void testIsPrivateIpNull() + { + assertFunction("IS_PRIVATE_IP(NULL)", BOOLEAN, null); + } + + @Test + default void testIpPrefixSubnets() + { + assertFunction("IP_PREFIX_SUBNETS(IPPREFIX '192.168.1.0/24', 25)", new ArrayType(IPPREFIX), ImmutableList.of("192.168.1.0/25", "192.168.1.128/25")); + assertFunction("IP_PREFIX_SUBNETS(IPPREFIX '192.168.0.0/24', 26)", new ArrayType(IPPREFIX), ImmutableList.of("192.168.0.0/26", "192.168.0.64/26", "192.168.0.128/26", "192.168.0.192/26")); + assertFunction("IP_PREFIX_SUBNETS(IPPREFIX '2A03:2880:C000::/34', 37)", + new ArrayType(IPPREFIX), + ImmutableList.of("2a03:2880:c000::/37", "2a03:2880:c800::/37", "2a03:2880:d000::/37", "2a03:2880:d800::/37", "2a03:2880:e000::/37", "2a03:2880:e800::/37", "2a03:2880:f000::/37", "2a03:2880:f800::/37")); + } + + @Test + default void testIpPrefixSubnetsReturnSelf() + { + assertFunction("IP_PREFIX_SUBNETS(IPPREFIX '192.168.1.0/24', 24)", new ArrayType(IPPREFIX), ImmutableList.of("192.168.1.0/24")); + assertFunction("IP_PREFIX_SUBNETS(IPPREFIX '2804:431:b000::/38', 38)", new ArrayType(IPPREFIX), ImmutableList.of("2804:431:b000::/38")); + } + + @Test + default void testIpPrefixSubnetsNewPrefixLengthLongerReturnsEmpty() + { + assertFunction("IP_PREFIX_SUBNETS(IPPREFIX '192.168.0.0/24', 23)", new ArrayType(IPPREFIX), ImmutableList.of()); + assertFunction("IP_PREFIX_SUBNETS(IPPREFIX '64:ff9b::17/64', 48)", new ArrayType(IPPREFIX), ImmutableList.of()); + } + + @Test + default void testIpPrefixSubnetsInvalidPrefixLengths() + { + assertInvalidFunction("IP_PREFIX_SUBNETS(IPPREFIX '192.168.0.0/24', -1)", "Invalid prefix length for IPv4: -1"); + assertInvalidFunction("IP_PREFIX_SUBNETS(IPPREFIX '192.168.0.0/24', 33)", "Invalid prefix length for IPv4: 33"); + assertInvalidFunction("IP_PREFIX_SUBNETS(IPPREFIX '64:ff9b::17/64', -1)", "Invalid prefix length for IPv6: -1"); + assertInvalidFunction("IP_PREFIX_SUBNETS(IPPREFIX '64:ff9b::17/64', 129)", "Invalid prefix length for IPv6: 129"); + } +} diff --git a/presto-main-tests/src/main/java/com/facebook/presto/tests/operator/scalar/TestFunctions.java b/presto-main-tests/src/main/java/com/facebook/presto/tests/operator/scalar/TestFunctions.java index e72644201ef3b..01a008b62296b 100644 --- a/presto-main-tests/src/main/java/com/facebook/presto/tests/operator/scalar/TestFunctions.java +++ b/presto-main-tests/src/main/java/com/facebook/presto/tests/operator/scalar/TestFunctions.java @@ -27,4 +27,16 @@ public interface TestFunctions * Asserts that the projection is not supported and that it fails with the expected error message. */ void assertNotSupported(String projection, String message); + + /** + * Asserts that the projection contains an invalid function call and fails + * with the specified functional error message. + */ + void assertInvalidFunction(String projection, String message); + + /** + * Asserts that the projection contains an invalid type conversion (cast) + * and fails with the expected casting error message. + */ + void assertInvalidCast(String projection, String message); } diff --git a/presto-native-tests/src/test/java/com/facebook/presto/nativetests/operator/scalar/AbstractTestNativeFunctions.java b/presto-native-tests/src/test/java/com/facebook/presto/nativetests/operator/scalar/AbstractTestNativeFunctions.java index 6c99067f3f399..3952d8e82ef1a 100644 --- a/presto-native-tests/src/test/java/com/facebook/presto/nativetests/operator/scalar/AbstractTestNativeFunctions.java +++ b/presto-native-tests/src/test/java/com/facebook/presto/nativetests/operator/scalar/AbstractTestNativeFunctions.java @@ -80,6 +80,34 @@ public void assertNotSupported(String projection, @Language("RegExp") String mes } } + @Override + public void assertInvalidFunction(String projection, @Language("RegExp") String message) + { + String query = format("SELECT %s", projection); + @Language("SQL") String rewritten = rewrite(query); + try { + computeActual(rewritten); + fail("expected exception"); + } + catch (RuntimeException ex) { + assertExceptionMessage(rewritten, ex, Pattern.quote(message), true, false); + } + } + + @Override + public void assertInvalidCast(String projection, @Language("RegExp") String message) + { + String query = format("SELECT %s", projection); + @Language("SQL") String rewritten = rewrite(query); + try { + computeActual(rewritten); + fail("expected exception"); + } + catch (RuntimeException ex) { + assertExceptionMessage(rewritten, ex, message, true, false); + } + } + /** * Rewrite SQL of the form 'select cast(arg as type)' to 'select cast(a as type) from (values (arg)) t(a)', and * SQL of the form 'select function(arg1, arg2, ...)' to diff --git a/presto-native-tests/src/test/java/com/facebook/presto/nativetests/operator/scalar/TestIpPrefixFunctions.java b/presto-native-tests/src/test/java/com/facebook/presto/nativetests/operator/scalar/TestIpPrefixFunctions.java new file mode 100644 index 0000000000000..ea181eee17544 --- /dev/null +++ b/presto-native-tests/src/test/java/com/facebook/presto/nativetests/operator/scalar/TestIpPrefixFunctions.java @@ -0,0 +1,22 @@ +/* + * Licensed 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 com.facebook.presto.nativetests.operator.scalar; + +import com.facebook.presto.tests.operator.scalar.AbstractTestIpPrefix; + +public class TestIpPrefixFunctions + extends AbstractTestNativeFunctions + implements AbstractTestIpPrefix +{ +} diff --git a/presto-spi/pom.xml b/presto-spi/pom.xml index c703a051062d4..a84c67a450657 100644 --- a/presto-spi/pom.xml +++ b/presto-spi/pom.xml @@ -22,6 +22,12 @@ com.facebook.presto presto-common + + + com.google.guava + guava + + @@ -73,6 +79,12 @@ presto-common test-jar test + + + com.google.guava + guava + + diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/TestingPrestoClient.java b/presto-tests/src/main/java/com/facebook/presto/tests/TestingPrestoClient.java index 904c61831f0fa..db2cafbda26d4 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/TestingPrestoClient.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/TestingPrestoClient.java @@ -70,6 +70,8 @@ import static com.facebook.presto.common.type.DateType.DATE; import static com.facebook.presto.common.type.DoubleType.DOUBLE; import static com.facebook.presto.common.type.IntegerType.INTEGER; +import static com.facebook.presto.common.type.IpAddressType.IPADDRESS; +import static com.facebook.presto.common.type.IpPrefixType.IPPREFIX; import static com.facebook.presto.common.type.JsonType.JSON; import static com.facebook.presto.common.type.RealType.REAL; import static com.facebook.presto.common.type.SmallintType.SMALLINT; @@ -82,7 +84,6 @@ import static com.facebook.presto.testing.MaterializedResult.DEFAULT_PRECISION; import static com.facebook.presto.type.IntervalDayTimeType.INTERVAL_DAY_TIME; import static com.facebook.presto.type.IntervalYearMonthType.INTERVAL_YEAR_MONTH; -import static com.facebook.presto.type.IpAddressType.IPADDRESS; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Iterables.transform; @@ -246,6 +247,9 @@ else if (INTERVAL_YEAR_MONTH.equals(type)) { else if (IPADDRESS.equals(type)) { return value; } + else if (IPPREFIX.equals(type)) { + return value; + } else if (type instanceof ArrayType) { return ((List) value).stream() .map(element -> convertToRowValue(((ArrayType) type).getElementType(), element)) From 93def8b1e31da26374eb8f472ad4ed39ca331209 Mon Sep 17 00:00:00 2001 From: Anant Aneja <1797669+aaneja@users.noreply.github.com> Date: Tue, 3 Feb 2026 19:34:59 -0800 Subject: [PATCH 3/4] fix(planner): Fix filter stats estimation corner cases (#26812) ## Description Fix for https://github.com/prestodb/presto/issues/26685 Fix for https://github.com/prestodb/presto/issues/26808 ## Motivation and Context ## Impact ## Test Plan ## Contributor checklist - [ ] Please make sure your submission complies with our [contributing guide](https://github.com/prestodb/presto/blob/master/CONTRIBUTING.md), in particular [code style](https://github.com/prestodb/presto/blob/master/CONTRIBUTING.md#code-style) and [commit standards](https://github.com/prestodb/presto/blob/master/CONTRIBUTING.md#commit-standards). - [ ] PR description addresses the issue accurately and concisely. If the change is non-trivial, a GitHub Issue is referenced. - [ ] Documented new properties (with its default value), SQL syntax, functions, or other functionality. - [ ] If release notes are required, they follow the [release notes guidelines](https://github.com/prestodb/presto/wiki/Release-Notes-Guidelines). - [ ] Adequate tests were added if applicable. - [ ] CI passed. - [ ] If adding new dependencies, verified they have an [OpenSSF Scorecard](https://securityscorecards.dev/#the-checks) score of 5.0 or higher (or obtained explicit TSC approval for lower scores). ## Release Notes Please follow [release notes guidelines](https://github.com/prestodb/presto/wiki/Release-Notes-Guidelines) and fill in the release notes below. ``` == RELEASE NOTES == General Changes * ... * ... Hive Connector Changes * ... * ... ``` If release note is NOT required, use: ``` == NO RELEASE NOTE == ``` --- .../cost/ComparisonStatsCalculator.java | 16 ++- .../presto/cost/FilterStatsCalculator.java | 16 ++- .../AbstractTestFilterStatsCalculator.java | 107 +++++++++++++++++- 3 files changed, 134 insertions(+), 5 deletions(-) diff --git a/presto-main-base/src/main/java/com/facebook/presto/cost/ComparisonStatsCalculator.java b/presto-main-base/src/main/java/com/facebook/presto/cost/ComparisonStatsCalculator.java index cba986d84bcff..fc04b944aaebc 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/cost/ComparisonStatsCalculator.java +++ b/presto-main-base/src/main/java/com/facebook/presto/cost/ComparisonStatsCalculator.java @@ -105,14 +105,26 @@ private PlanNodeStatsEstimate estimateExpressionNotEqualToLiteral( else { filterRange = new StatisticRange(NEGATIVE_INFINITY, true, POSITIVE_INFINITY, true, 1); } - double filterFactor = 1 - calculateFilterFactor(expressionStatistics, filterRange); + + double filterFactor; + double expressionNDV = expressionStatistics.getDistinctValuesCount(); + if (Double.compare(expressionNDV, 1D) == 0) { + // It's hard to make a meaningful estimate when we have only one distinct value + filterFactor = UNKNOWN_FILTER_COEFFICIENT; + } + else { + filterFactor = 1 - calculateFilterFactor(expressionStatistics, filterRange); + } PlanNodeStatsEstimate.Builder estimate = PlanNodeStatsEstimate.buildFrom(inputStatistics); estimate.setOutputRowCount(filterFactor * (1 - expressionStatistics.getNullsFraction()) * inputStatistics.getOutputRowCount()); if (expressionVariable.isPresent()) { + // If the original NDV was 1, we do not make any changes to the new estimate, since we're not sure if we eliminated the only distinct value + // Otherwise, we reduce the NDV by 1 (unless it was already 0) + double newNDV = Double.compare(expressionNDV, 1D) == 0 ? 1 : max(expressionNDV - 1, 0); VariableStatsEstimate symbolNewEstimate = buildFrom(expressionStatistics) .setNullsFraction(0.0) - .setDistinctValuesCount(max(expressionStatistics.getDistinctValuesCount() - 1, 0)) + .setDistinctValuesCount(newNDV) .build(); estimate = estimate.addVariableStatistics(expressionVariable.get(), symbolNewEstimate); } diff --git a/presto-main-base/src/main/java/com/facebook/presto/cost/FilterStatsCalculator.java b/presto-main-base/src/main/java/com/facebook/presto/cost/FilterStatsCalculator.java index d01c5b6ba68f2..37fb1abba807f 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/cost/FilterStatsCalculator.java +++ b/presto-main-base/src/main/java/com/facebook/presto/cost/FilterStatsCalculator.java @@ -91,6 +91,7 @@ import static java.lang.Double.NaN; import static java.lang.Double.isInfinite; import static java.lang.Double.isNaN; +import static java.lang.Double.max; import static java.lang.Double.min; import static java.lang.String.format; import static java.util.Collections.emptyMap; @@ -98,6 +99,13 @@ public class FilterStatsCalculator { + /** + * + * This value applies a filter factor to upper-bound the size of the variable range selected for an IN predicate + * Since the estimator sums up the individual estimates, we don't want to go beyond 1.0 + * This also impacts NOT IN similarly, we never apply a filter factor of 0.0 for a NOT IN clause + */ + static final double CEIL_IN_PREDICATE_UPPER_BOUND_COEFFICIENT = 0.8; static final double UNKNOWN_FILTER_COEFFICIENT = 0.9; private final Metadata metadata; @@ -403,9 +411,11 @@ protected PlanNodeStatsEstimate visitInPredicate(InPredicate node, Void context) } double notNullValuesBeforeIn = input.getOutputRowCount() * (1 - valueStats.getNullsFraction()); + double ceiledInEstimated = max(notNullValuesBeforeIn * CEIL_IN_PREDICATE_UPPER_BOUND_COEFFICIENT, 1.0); + double inEstimateRowCount = min(inEstimate.getOutputRowCount(), ceiledInEstimated); PlanNodeStatsEstimate.Builder result = PlanNodeStatsEstimate.buildFrom(input); - result.setOutputRowCount(min(inEstimate.getOutputRowCount(), notNullValuesBeforeIn)); + result.setOutputRowCount(inEstimateRowCount); if (node.getValue() instanceof SymbolReference) { VariableReferenceExpression valueVariable = toVariable(node.getValue()); @@ -774,9 +784,11 @@ private PlanNodeStatsEstimate estimateIn(RowExpression value, List 'bar'", 90D}, // 100 * UNKNOWN_FILTER_COEFFICIENT + {"name <> 'name' AND name <> 'bar'", 81D}, // 100 * UNKNOWN_FILTER_COEFFICIENT * UNKNOWN_FILTER_COEFFICIENT + {"name <> 'foo' OR name is NULL", 90D}, // 100 * UNKNOWN_FILTER_COEFFICIENT + {"name is NULL OR name <> 'foo'", 90D}, // 100 * UNKNOWN_FILTER_COEFFICIENT + }; + } + + @DataProvider + public static Object[][] inList() + { + return new Object[][] { + {"'one'"}, + {"'one','two'"}, + {"'one','two','three'"}, + {"'one','two','three','four'"}, + {"'one','two','three','four','five'"}, + {"'one','two','three','four','five','six'"}, + {"'one','two','three','four','five','six', 'seven'"} + }; + } + @BeforeClass public void setUp() throws Exception @@ -500,6 +532,50 @@ public void testSymbolEqualsSameSymbolFilter() .build()); } + @Test(dataProvider = "inList") + public void testInPredicateWithoutNDV(String inList) + { + Expression exp = expression("status IN (" + inList + ")"); + TypeProvider customTypes = TypeProvider.fromVariables(ImmutableList.builder() + .add(new VariableReferenceExpression(Optional.empty(), "status", MEDIUM_VARCHAR_TYPE)) + .build()); + + RowExpression rowExpression = translator.translateAndOptimize(exp, customTypes); + + VariableStatsEstimate nameStats = VariableStatsEstimate.builder() + // Nulls fraction is known, but NDV is not. Stats propagation should work + .setNullsFraction(0.0D) + .build(); + + PlanNodeStatsEstimate inputStats = PlanNodeStatsEstimate.builder() + .addVariableStatistics(new VariableReferenceExpression(Optional.empty(), "status", MEDIUM_VARCHAR_TYPE), nameStats) + .setOutputRowCount(100D) + .build(); + + PlanNodeStatsEstimate rowExpressionStatsEstimate = statsCalculator.filterStats(inputStats, rowExpression, session); + + // The IN filter should always apply a filter factor between (0,1) (never NaN/0/1) + int inListLength = inList.split(",").length; + if (inListLength == 1) { + // A single entry IN list is equivalent to an infinite range intersect; we use StatisticRange#INFINITE_TO_INFINITE_RANGE_INTERSECT_OVERLAP_HEURISTIC_FACTOR (0.5) + // as our filter factor, resulting in : non-null-inputRowCount * 0.5 = 50 + assertEquals(rowExpressionStatsEstimate.getOutputRowCount(), 50D); + } + else { + // Multiple values in the IN list - We sum up the estimates, but cap it to non-null-inputRowCount * CEIL_IN_PREDICATE_UPPER_BOUND_COEFFICIENT = 80 in this case + assertEquals(rowExpressionStatsEstimate.getOutputRowCount(), 80D); + } + } + + @Test(dataProvider = "inList") + public void testNotInPredicateEstimateIsNeverZero(String inList) + { + RowExpression rowExpression = translator.translateAndOptimize(expression("mediumVarchar NOT IN (" + inList + ")"), standardTypes); + PlanNodeStatsEstimate rowExpressionStatsEstimate = statsCalculator.filterStats(standardInputStatistics, rowExpression, session); + + assertNotEquals(rowExpressionStatsEstimate.getOutputRowCount(), 0D, 0.0001D); + } + @Test public void testInPredicateFilter() { @@ -588,7 +664,8 @@ public void testInPredicateFilter() // More values in range than distinct values assertExpression("z IN (DOUBLE '-1', 3.14e0, 0e0, 1e0, 2e0, 3e0, 4e0, 5e0, 6e0, 7e0, 8e0, DOUBLE '-2')") - .outputRowsCount(900.0) + // Range estimate is never the full-range, it's non-null count * CEIL_IN_PREDICATE_UPPER_BOUND_COEFFICIENT + .outputRowsCount(720.0) .variableStats(new VariableReferenceExpression(Optional.empty(), "z", DOUBLE), variableStats -> variableStats.distinctValuesCount(5.0) .lowValue(-2.0) @@ -605,6 +682,34 @@ public void testInPredicateFilter() .nullsFraction(0.0)); } + @Test(dataProvider = "ndv1Expressions") + public void testNotEqualsOnVariablesWithNDV1(String expressionStr, double expectedOutputRowsCount) + { + Expression exp = expression(expressionStr); + + VariableReferenceExpression name = new VariableReferenceExpression(Optional.empty(), "name", MEDIUM_VARCHAR_TYPE); + TypeProvider customTypes = TypeProvider.fromVariables(ImmutableList.builder() + .add(name) + .build()); + + RowExpression rowExpression = translator.translateAndOptimize(exp, customTypes); + + VariableStatsEstimate nameStats = VariableStatsEstimate.builder() + .setNullsFraction(0D) + .setDistinctValuesCount(1D) + .build(); + + PlanNodeStatsEstimate rowExpressionStatsEstimate = statsCalculator.filterStats(PlanNodeStatsEstimate.builder() + .addVariableStatistics(name, nameStats) + .setOutputRowCount(100D) + .build(), rowExpression, session); + + PlanNodeStatsAssertion.assertThat(rowExpressionStatsEstimate) + .outputRowsCount(expectedOutputRowsCount) + // Variable Stats remains unchanged + .variableStats(name, variableStats -> variableStats.distinctValuesCount(1D).nullsFraction(0D)); + } + protected PlanNodeStatsAssertion assertExpression(String expression) { return assertExpression(expression(expression)); From e1006483a1d6f5470a8d4229b7805cf08301d92e Mon Sep 17 00:00:00 2001 From: Ke <50636602+kewang1024@users.noreply.github.com> Date: Tue, 3 Feb 2026 20:18:08 -0800 Subject: [PATCH 4/4] feat(native): Add config for asyc cache numShards (#27073) ``` == NO RELEASE NOTE == ``` ## Summary by Sourcery Add configurable shard count for the async data cache and wire it through server initialization. New Features: - Introduce a new system config option to control the number of async cache shards with a default value. - Expose the async cache shard count to the async data cache options during server initialization. Tests: - Add unit tests covering default and custom values for the async cache shard count system config. --- .../presto_cpp/main/PrestoServer.cpp | 3 ++- .../presto_cpp/main/common/Configs.cpp | 5 +++++ .../presto_cpp/main/common/Configs.h | 8 ++++++++ .../presto_cpp/main/common/tests/ConfigTest.cpp | 11 +++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/presto-native-execution/presto_cpp/main/PrestoServer.cpp b/presto-native-execution/presto_cpp/main/PrestoServer.cpp index 9c06fa12d4bdd..ba0e9f01ed016 100644 --- a/presto-native-execution/presto_cpp/main/PrestoServer.cpp +++ b/presto-native-execution/presto_cpp/main/PrestoServer.cpp @@ -1073,7 +1073,8 @@ void PrestoServer::initializeVeloxMemory() { velox::cache::AsyncDataCache::Options cacheOptions{ systemConfig->asyncCacheMaxSsdWriteRatio(), systemConfig->asyncCacheSsdSavableRatio(), - systemConfig->asyncCacheMinSsdSavableBytes()}; + systemConfig->asyncCacheMinSsdSavableBytes(), + systemConfig->asyncCacheNumShards()}; cache_ = velox::cache::AsyncDataCache::create( velox::memory::memoryManager()->allocator(), std::move(ssd), diff --git a/presto-native-execution/presto_cpp/main/common/Configs.cpp b/presto-native-execution/presto_cpp/main/common/Configs.cpp index 89f14085da3e2..e44937470a186 100644 --- a/presto-native-execution/presto_cpp/main/common/Configs.cpp +++ b/presto-native-execution/presto_cpp/main/common/Configs.cpp @@ -209,6 +209,7 @@ SystemConfig::SystemConfig() { NUM_PROP(kAsyncCacheMaxSsdWriteRatio, 0.7), NUM_PROP(kAsyncCacheSsdSavableRatio, 0.125), NUM_PROP(kAsyncCacheMinSsdSavableBytes, 1 << 24 /*16MB*/), + NUM_PROP(kAsyncCacheNumShards, 4), STR_PROP(kAsyncCachePersistenceInterval, "0s"), BOOL_PROP(kAsyncCacheSsdDisableFileCow, false), BOOL_PROP(kSsdCacheChecksumEnabled, false), @@ -695,6 +696,10 @@ int32_t SystemConfig::asyncCacheMinSsdSavableBytes() const { return optionalProperty(kAsyncCacheMinSsdSavableBytes).value(); } +int32_t SystemConfig::asyncCacheNumShards() const { + return optionalProperty(kAsyncCacheNumShards).value(); +} + std::chrono::duration SystemConfig::asyncCachePersistenceInterval() const { return velox::config::toDuration( diff --git a/presto-native-execution/presto_cpp/main/common/Configs.h b/presto-native-execution/presto_cpp/main/common/Configs.h index 2c44c3e845427..b92fa33c255b5 100644 --- a/presto-native-execution/presto_cpp/main/common/Configs.h +++ b/presto-native-execution/presto_cpp/main/common/Configs.h @@ -429,6 +429,12 @@ class SystemConfig : public ConfigBase { static constexpr std::string_view kAsyncCacheMinSsdSavableBytes{ "async-cache-min-ssd-savable-bytes"}; + /// The number of shards for the async data cache. The cache is divided into + /// shards to decrease contention on the mutex for the key to entry mapping + /// and other housekeeping. Must be a power of 2. + static constexpr std::string_view kAsyncCacheNumShards{ + "async-cache-num-shards"}; + /// The interval for persisting in-memory cache to SSD. Setting this config /// to a non-zero value will activate periodic cache persistence. static constexpr std::string_view kAsyncCachePersistenceInterval{ @@ -1066,6 +1072,8 @@ class SystemConfig : public ConfigBase { int32_t asyncCacheMinSsdSavableBytes() const; + int32_t asyncCacheNumShards() const; + std::chrono::duration asyncCachePersistenceInterval() const; bool asyncCacheSsdDisableFileCow() const; diff --git a/presto-native-execution/presto_cpp/main/common/tests/ConfigTest.cpp b/presto-native-execution/presto_cpp/main/common/tests/ConfigTest.cpp index 76096dad1145a..694ce3d14b251 100644 --- a/presto-native-execution/presto_cpp/main/common/tests/ConfigTest.cpp +++ b/presto-native-execution/presto_cpp/main/common/tests/ConfigTest.cpp @@ -230,6 +230,17 @@ TEST_F(ConfigTest, optionalSystemConfigsWithDefault) { ASSERT_EQ(config.maxDriversPerTask(), 1024); } +TEST_F(ConfigTest, asyncCacheNumShards) { + SystemConfig config; + init(config, {}); + // Test default value is 4 + ASSERT_EQ(config.asyncCacheNumShards(), 4); + + // Test custom value + init(config, {{std::string(SystemConfig::kAsyncCacheNumShards), "8"}}); + ASSERT_EQ(config.asyncCacheNumShards(), 8); +} + TEST_F(ConfigTest, remoteFunctionServer) { SystemConfig config; init(config, {});