From 171afbd9de60ecd3819cfd62acd55a1ec78b8425 Mon Sep 17 00:00:00 2001 From: Pramod Satya Date: Mon, 11 Nov 2024 07:46:50 +0530 Subject: [PATCH] Add presto-native-tests module Co-authored-by: Manoj Negi --- .circleci/continue_config.yml | 52 + .../prestocpp-linux-build-and-unit-test.yml | 82 ++ .github/workflows/test-other-modules.yml | 1 + pom.xml | 1 + .../PrestoNativeQueryRunnerUtils.java | 37 +- presto-native-tests/README.md | 31 + presto-native-tests/pom.xml | 122 ++ .../AbstractTestAggregationsNative.java | 227 ++++ .../AbstractTestQueriesNative.java | 1208 +++++++++++++++++ .../TestAggregations.java | 43 + .../TestDistributedEngineOnlyQueries.java | 122 ++ ...estDistributedQueriesNoHashGeneration.java | 47 + .../TestNonIterativeDistributedQueries.java | 49 + ...TestOptimizeMixedDistinctAggregations.java | 55 + .../TestOrderByQueries.java | 54 + .../TestRepartitionQueries.java | 82 ++ .../TestRepartitionQueriesWithSmallPages.java | 86 ++ .../TestTpchDistributedQueries.java | 98 ++ .../TestWindowQueries.java | 654 +++++++++ .../tests/AbstractTestEngineOnlyQueries.java | 0 .../tests/AbstractTestOrderByQueries.java | 13 +- .../presto/tests/AbstractTestQueries.java | 135 +- .../tests/AbstractTestQueryFramework.java | 10 + .../tests/AbstractTestWindowQueries.java | 20 +- .../presto/tests/QueryAssertions.java | 25 + 25 files changed, 3194 insertions(+), 60 deletions(-) create mode 100644 presto-native-tests/README.md create mode 100644 presto-native-tests/pom.xml create mode 100644 presto-native-tests/src/test/java/com.facebook.presto.nativetests/AbstractTestAggregationsNative.java create mode 100644 presto-native-tests/src/test/java/com.facebook.presto.nativetests/AbstractTestQueriesNative.java create mode 100644 presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestAggregations.java create mode 100644 presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestDistributedEngineOnlyQueries.java create mode 100644 presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestDistributedQueriesNoHashGeneration.java create mode 100644 presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestNonIterativeDistributedQueries.java create mode 100644 presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestOptimizeMixedDistinctAggregations.java create mode 100644 presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestOrderByQueries.java create mode 100644 presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestRepartitionQueries.java create mode 100644 presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestRepartitionQueriesWithSmallPages.java create mode 100644 presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestTpchDistributedQueries.java create mode 100644 presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestWindowQueries.java rename presto-tests/src/{test => main}/java/com/facebook/presto/tests/AbstractTestEngineOnlyQueries.java (100%) diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 41e649b64cfd..29b966d88c9e 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -29,6 +29,10 @@ workflows: run_linux_tests: << pipeline.parameters.run_linux_tests >> requires: - linux-build-and-unit-test + - linux-presto-native-tests: + run_linux_tests: << pipeline.parameters.run_linux_tests >> + requires: + - linux-build-and-unit-test - linux-spark-e2e-tests: run_linux_tests: << pipeline.parameters.run_linux_tests >> requires: @@ -198,6 +202,54 @@ jobs: - store_artifacts: path: '/tmp/PrestoNativeQueryRunnerUtils' + linux-presto-native-tests: + executor: build + parameters: + run_linux_tests: + type: boolean + default: false + parallelism: 5 + steps: + - run: echo "Run Linux tests is << parameters.run_linux_tests >>" + - when: + condition: << parameters.run_linux_tests >> + steps: + - checkout + - attach_workspace: + at: presto-native-execution + - maven_install: + maven_install_opts: ${MAVEN_INSTALL_OPTS} + maven_fast_install: ${MAVEN_FAST_INSTALL} + - run: + name: 'Run Presto native tests' + command: | + export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/usr/local/lib:/usr/local/lib64" + export PRESTO_SERVER_PATH="${HOME}/project/presto-native-execution/_build/debug/presto_cpp/main/presto_server" + export TEMP_PATH="/tmp" + TESTFILES=$(circleci tests glob "presto-native-tests/src/test/**/Test*.java" | circleci tests split --split-by=timings) + # Convert file paths to comma separated class names + export TESTCLASSES= + for test_file in $TESTFILES + do + tmp=${test_file##*/} + test_class=${tmp%%\.*} + export TESTCLASSES="${TESTCLASSES},$test_class" + done + export TESTCLASSES=${TESTCLASSES#,} + + if [ ! -z $TESTCLASSES ]; then + mvn test \ + ${MAVEN_TEST} \ + -pl 'presto-native-tests' \ + -Dtest="${TESTCLASSES}" \ + -DPRESTO_SERVER=${PRESTO_SERVER_PATH} \ + -DDATA_DIR=${TEMP_PATH} \ + -Duser.timezone=America/Bahia_Banderas \ + -T1C + fi + - store_artifacts: + path: '/tmp/PrestoNativeQueryRunnerUtils' + linux-presto-native-sidecar-tests: executor: build parameters: diff --git a/.github/workflows/prestocpp-linux-build-and-unit-test.yml b/.github/workflows/prestocpp-linux-build-and-unit-test.yml index a3770fb1a8df..73cb2124ff8e 100644 --- a/.github/workflows/prestocpp-linux-build-and-unit-test.yml +++ b/.github/workflows/prestocpp-linux-build-and-unit-test.yml @@ -169,6 +169,88 @@ jobs: -Duser.timezone=America/Bahia_Banderas \ -T1C + + prestocpp-linux-presto-native-tests: + needs: prestocpp-linux-build-for-test + runs-on: ubuntu-22.04 + container: + image: prestodb/presto-native-dependency:0.290-20241014120930-e1fc090 + env: + MAVEN_OPTS: "-Xmx4G -XX:+ExitOnOutOfMemoryError" + MAVEN_FAST_INSTALL: "-B -V --quiet -T 1C -DskipTests -Dair.check.skip-all -Dmaven.javadoc.skip=true" + MAVEN_TEST: "-B -Dair.check.skip-all -Dmaven.javadoc.skip=true -DLogTestDurationListener.enabled=true --fail-at-end" + steps: + - uses: actions/checkout@v4 + + - name: Fix git permissions + # Usually actions/checkout does this but as we run in a container + # it doesn't work + run: git config --global --add safe.directory ${GITHUB_WORKSPACE} + + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: presto-native-build + path: presto-native-execution/_build/release + + # Permissions are lost when uploading. Details here: https://github.com/actions/upload-artifact/issues/38 + - name: Restore execute permissions and library path + run: | + chmod +x ${GITHUB_WORKSPACE}/presto-native-execution/_build/release/presto_cpp/main/presto_server + chmod +x ${GITHUB_WORKSPACE}/presto-native-execution/_build/release/velox/velox/functions/remote/server/velox_functions_remote_server_main + # Ensure transitive dependency libboost-iostreams is found. + ldconfig /usr/local/lib + + - name: Install OpenJDK8 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '8' + + - name: Cache local Maven repository + id: cache-maven + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-2-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven-2- + + - name: Populate maven cache + if: steps.cache-maven.outputs.cache-hit != 'true' + run: ./mvnw de.qaware.maven:go-offline-maven-plugin:resolve-dependencies + + - name: Maven install + env: + # Use different Maven options to install. + MAVEN_OPTS: "-Xmx2G -XX:+ExitOnOutOfMemoryError" + run: | + for i in $(seq 1 3); do ./mvnw clean install $MAVEN_FAST_INSTALL -pl 'presto-native-tests' -am && s=0 && break || s=$? && sleep 10; done; (exit $s) + + - name: Run presto-native tests + run: | + export PRESTO_SERVER_PATH="${GITHUB_WORKSPACE}/presto-native-execution/_build/release/presto_cpp/main/presto_server" + export TESTFILES=`find ./presto-native-tests/src/test -type f -name 'Test*.java'` + # Convert file paths to comma separated class names + export TESTCLASSES= + for test_file in $TESTFILES + do + tmp=${test_file##*/} + test_class=${tmp%%\.*} + export TESTCLASSES="${TESTCLASSES},$test_class" + done + export TESTCLASSES=${TESTCLASSES#,} + echo "TESTCLASSES = $TESTCLASSES" + + mvn test \ + ${MAVEN_TEST} \ + -pl 'presto-native-tests' \ + -Dtest="${TESTCLASSES}" \ + -DPRESTO_SERVER=${PRESTO_SERVER_PATH} \ + -DDATA_DIR=${RUNNER_TEMP} \ + -Duser.timezone=America/Bahia_Banderas \ + -T1C + prestocpp-linux-spark-e2e-tests: needs: prestocpp-linux-build-for-test runs-on: ubuntu-22.04 diff --git a/.github/workflows/test-other-modules.yml b/.github/workflows/test-other-modules.yml index 3c3f84817a0d..b2fec9efd4cb 100644 --- a/.github/workflows/test-other-modules.yml +++ b/.github/workflows/test-other-modules.yml @@ -64,6 +64,7 @@ jobs: run: | ./mvnw test -T 1 ${MAVEN_TEST} -pl ' !presto-tests, + !presto-native-tests, !presto-accumulo, !presto-cassandra, !presto-hive, diff --git a/pom.xml b/pom.xml index ee4920c08462..43e64c51259e 100644 --- a/pom.xml +++ b/pom.xml @@ -197,6 +197,7 @@ presto-test-coverage presto-hudi presto-native-execution + presto-native-tests presto-router presto-open-telemetry redis-hbo-provider diff --git a/presto-native-execution/src/test/java/com/facebook/presto/nativeworker/PrestoNativeQueryRunnerUtils.java b/presto-native-execution/src/test/java/com/facebook/presto/nativeworker/PrestoNativeQueryRunnerUtils.java index 58431a64f7ca..1cd8a73102a2 100644 --- a/presto-native-execution/src/test/java/com/facebook/presto/nativeworker/PrestoNativeQueryRunnerUtils.java +++ b/presto-native-execution/src/test/java/com/facebook/presto/nativeworker/PrestoNativeQueryRunnerUtils.java @@ -43,7 +43,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.OptionalInt; import java.util.UUID; @@ -121,7 +123,7 @@ public static QueryRunner createQueryRunner( defaultQueryRunner.close(); - return createNativeQueryRunner(dataDirectory.get().toString(), prestoServerPath.get(), workerCount, cacheMaxSize, true, Optional.empty(), storageFormat, addStorageFormatToPath, false, isCoordinatorSidecarEnabled, false); + return createNativeQueryRunner(dataDirectory.get().toString(), prestoServerPath.get(), workerCount, cacheMaxSize, true, Optional.empty(), storageFormat, addStorageFormatToPath, false, isCoordinatorSidecarEnabled, false, Collections.emptyMap(), Collections.emptyMap()); } public static QueryRunner createJavaQueryRunner() @@ -326,7 +328,9 @@ public static QueryRunner createNativeQueryRunner( boolean addStorageFormatToPath, Boolean failOnNestedLoopJoin, boolean isCoordinatorSidecarEnabled, - boolean singleNodeExecutionEnabled) + boolean singleNodeExecutionEnabled, + Map extraProperties, + Map extraCoordinatorProperties) throws Exception { // The property "hive.allow-drop-table" needs to be set to true because security is always "legacy" in NativeQueryRunner. @@ -350,8 +354,9 @@ public static QueryRunner createNativeQueryRunner( .put("experimental.internal-communication.thrift-transport-enabled", String.valueOf(useThrift)) .putAll(getNativeWorkerSystemProperties()) .putAll(isCoordinatorSidecarEnabled ? getNativeSidecarProperties() : ImmutableMap.of()) + .putAll(extraProperties) .build(), - coordinatorProperties.build(), + coordinatorProperties.putAll(extraCoordinatorProperties).build(), "legacy", hiveProperties, workerCount, @@ -408,6 +413,28 @@ public static QueryRunner createNativeQueryRunner(String remoteFunctionServerUds return createNativeQueryRunner(false, DEFAULT_STORAGE_FORMAT, Optional.ofNullable(remoteFunctionServerUds), false, false, false); } + public static QueryRunner createNativeQueryRunner(Map extraProperties, Map extraCoordinatorProperties, + String storageFormat) + throws Exception + { + int cacheMaxSize = 0; + NativeQueryRunnerParameters nativeQueryRunnerParameters = getNativeQueryRunnerParameters(); + return createNativeQueryRunner( + nativeQueryRunnerParameters.dataDirectory.toString(), + nativeQueryRunnerParameters.serverBinary.toString(), + nativeQueryRunnerParameters.workerCount, + cacheMaxSize, + true, + Optional.empty(), + storageFormat, + true, + false, + false, + false, + extraProperties, + extraCoordinatorProperties); + } + public static QueryRunner createNativeQueryRunner(boolean useThrift) throws Exception { @@ -448,7 +475,9 @@ public static QueryRunner createNativeQueryRunner( true, failOnNestedLoopJoin, isCoordinatorSidecarEnabled, - singleNodeExecutionEnabled); + singleNodeExecutionEnabled, + Collections.emptyMap(), + Collections.emptyMap()); } // Start the remote function server. Return the UDS path used to communicate with it. diff --git a/presto-native-tests/README.md b/presto-native-tests/README.md new file mode 100644 index 000000000000..a882b2a33b46 --- /dev/null +++ b/presto-native-tests/README.md @@ -0,0 +1,31 @@ +# Presto Native Tests + +This module contains end-to-end tests that run queries from test classes in +the `presto-tests` module with Presto C++ workers. Please build the module +`presto-native-execution` first. + +The following command can be used to run all tests in this module: +``` +mvn test + -pl 'presto-native-tests' + -Dtest="com.facebook.presto.nativetests.Test*" + -Duser.timezone=America/Bahia_Banderas + -DPRESTO_SERVER=${PRESTO_HOME}/presto-native-execution/cmake-build-debug/presto_cpp/main/presto_server + -DWORKER_COUNT=${WORKER_COUNT} -T1C +``` +Please update JVM argument `PRESTO_SERVER` to point to the Presto C++ worker +binary `presto_server`. + +## Adding new tests + +Presto C++ currently does not have the same behavior as Presto for certain +queries. This could be because of missing types, missing function signatures, +among other reasons. Tests with these unsupported queries are therefore +expected to fail and the test asserts the error message is as expected. + +Issues should also be created for the failing queries, so they are documented +and fixed. Please add the tag `presto-native-tests` for these issues. +Once all the failures in a testcase are fixed, the overriden test in this +module should be removed and the testcase in the corresponding base class in +`presto-tests` would be the single source of truth for Presto SQL coverage +tests. diff --git a/presto-native-tests/pom.xml b/presto-native-tests/pom.xml new file mode 100644 index 000000000000..73120202d957 --- /dev/null +++ b/presto-native-tests/pom.xml @@ -0,0 +1,122 @@ + + + 4.0.0 + + + com.facebook.presto + presto-root + 0.291-SNAPSHOT + + + presto-native-tests + presto-native-tests + Presto Native Tests + + + ${project.parent.basedir} + + + + + org.testng + testng + + + + com.facebook.presto + presto-native-execution + ${project.version} + test-jar + test + + + + com.facebook.presto + presto-common + + + + + com.facebook.presto + presto-spi + + + + com.facebook.presto + presto-main + + + + com.facebook.presto + presto-tests + ${project.version} + + + + com.google.guava + guava + + + + com.facebook.presto + presto-tpcds + test + + + + org.jetbrains + annotations + test + + + + + + + + pl.project13.maven + git-commit-id-plugin + + true + + + + org.basepom.maven + duplicate-finder-maven-plugin + + + parquet.thrift + about.html + mozilla/public-suffix-list.txt + iceberg-build.properties + org.apache.avro.data/Json.avsc + + + com.esotericsoftware.kryo.* + com.esotericsoftware.minlog.Log + com.esotericsoftware.reflectasm.* + module-info + META-INF.versions.9.module-info + org.apache.avro.* + com.github.benmanes.caffeine.* + org.roaringbitmap.* + + + + + org.apache.maven.plugins + maven-surefire-plugin + + -Xms4g -Xmx4g + 1 + false + remote-function,textfile_reader + + /root/project/build/debug/presto_cpp/main/presto_server + + + + + + diff --git a/presto-native-tests/src/test/java/com.facebook.presto.nativetests/AbstractTestAggregationsNative.java b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/AbstractTestAggregationsNative.java new file mode 100644 index 000000000000..4580af2f8cc7 --- /dev/null +++ b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/AbstractTestAggregationsNative.java @@ -0,0 +1,227 @@ +/* + * 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; + +import com.facebook.presto.tests.AbstractTestAggregations; +import org.testng.annotations.Test; + +import static java.lang.String.format; + +public abstract class AbstractTestAggregationsNative + extends AbstractTestAggregations +{ + private static final String approxDistributionTypesUnsupportedError = ".*Failed to parse type.*digest.*"; + private static final String aggregateFunctionNotRegisteredError = ".*Aggregate function not registered: .*digest_agg.*"; + + @Override + @Test + public void testApproximateCountDistinct() + { + String timeTypeUnsupportedError = "Failed to parse type.*time"; + String charTypeUnsupportedError = "Failed to parse type.*char"; + String signatureUnsupportedError = ".*Aggregate function signature is not supported.*"; + + // test NULL + assertQuery("SELECT approx_distinct(NULL)", "SELECT 0"); + assertQuery("SELECT approx_distinct(NULL, 0.023)", "SELECT 0"); + + // test date + assertQuery("SELECT approx_distinct(orderdate) FROM orders", "SELECT 2372"); + assertQuery("SELECT approx_distinct(orderdate, 0.023) FROM orders", "SELECT 2372"); + + // test timestamp + assertQuery("SELECT approx_distinct(CAST(orderdate AS TIMESTAMP)) FROM orders", "SELECT 2347"); + assertQuery("SELECT approx_distinct(CAST(orderdate AS TIMESTAMP), 0.023) FROM orders", "SELECT 2347"); + + // test timestamp with time zone + assertQueryFails("SELECT approx_distinct(CAST(orderdate AS TIMESTAMP WITH TIME ZONE)) FROM orders", + signatureUnsupportedError, true); + assertQueryFails("SELECT approx_distinct(CAST(orderdate AS TIMESTAMP WITH TIME ZONE), 0.023) FROM orders", + signatureUnsupportedError, true); + + // test time + assertQueryFails("SELECT approx_distinct(CAST(from_unixtime(custkey) AS TIME)) FROM orders", timeTypeUnsupportedError, true); + assertQueryFails("SELECT approx_distinct(CAST(from_unixtime(custkey) AS TIME), 0.023) FROM orders", timeTypeUnsupportedError, true); + + // test time with time zone + assertQueryFails("SELECT approx_distinct(CAST(from_unixtime(custkey) AS TIME WITH TIME ZONE)) FROM orders", timeTypeUnsupportedError, true); + assertQueryFails("SELECT approx_distinct(CAST(from_unixtime(custkey) AS TIME WITH TIME ZONE), 0.023) FROM orders", timeTypeUnsupportedError, true); + + // test short decimal + assertQuery("SELECT approx_distinct(CAST(custkey AS DECIMAL(18, 0))) FROM orders", "SELECT 990"); + assertQuery("SELECT approx_distinct(CAST(custkey AS DECIMAL(18, 0)), 0.023) FROM orders", "SELECT 990"); + + // test long decimal + assertQuery("SELECT approx_distinct(CAST(custkey AS DECIMAL(25, 20))) FROM orders", "SELECT 1013"); + assertQuery("SELECT approx_distinct(CAST(custkey AS DECIMAL(25, 20)), 0.023) FROM orders", "SELECT 1013"); + + // test real + assertQuery("SELECT approx_distinct(CAST(custkey AS REAL)) FROM orders", "SELECT 982"); + assertQuery("SELECT approx_distinct(CAST(custkey AS REAL), 0.023) FROM orders", "SELECT 982"); + + // test bigint + assertQuery("SELECT approx_distinct(custkey) FROM orders", "SELECT 990"); + assertQuery("SELECT approx_distinct(custkey, 0.023) FROM orders", "SELECT 990"); + + // test integer + assertQuery("SELECT approx_distinct(CAST(custkey AS INTEGER)) FROM orders", "SELECT 1028"); + assertQuery("SELECT approx_distinct(CAST(custkey AS INTEGER), 0.023) FROM orders", "SELECT 1028"); + + // test smallint + assertQuery("SELECT approx_distinct(CAST(custkey AS SMALLINT)) FROM orders", "SELECT 1023"); + assertQuery("SELECT approx_distinct(CAST(custkey AS SMALLINT), 0.023) FROM orders", "SELECT 1023"); + + // test tinyint + assertQuery("SELECT approx_distinct(CAST((custkey % 128) AS TINYINT)) FROM orders", "SELECT 128"); + assertQuery("SELECT approx_distinct(CAST((custkey % 128) AS TINYINT), 0.023) FROM orders", "SELECT 128"); + + // test double + assertQuery("SELECT approx_distinct(CAST(custkey AS DOUBLE)) FROM orders", "SELECT 1014"); + assertQuery("SELECT approx_distinct(CAST(custkey AS DOUBLE), 0.023) FROM orders", "SELECT 1014"); + + // test varchar + assertQuery("SELECT approx_distinct(CAST(custkey AS VARCHAR)) FROM orders", "SELECT 1036"); + assertQuery("SELECT approx_distinct(CAST(custkey AS VARCHAR), 0.023) FROM orders", "SELECT 1036"); + + // test char + assertQueryFails("SELECT approx_distinct(CAST(CAST(custkey AS VARCHAR) AS CHAR(20))) FROM orders", charTypeUnsupportedError, true); + assertQueryFails("SELECT approx_distinct(CAST(CAST(custkey AS VARCHAR) AS CHAR(20)), 0.023) FROM orders", charTypeUnsupportedError, true); + + // test varbinary + assertQuery("SELECT approx_distinct(to_utf8(CAST(custkey AS VARCHAR))) FROM orders", "SELECT 1036"); + assertQuery("SELECT approx_distinct(to_utf8(CAST(custkey AS VARCHAR)), 0.023) FROM orders", "SELECT 1036"); + } + + @Override + @Test(dataProvider = "getType") + public void testStatisticalDigest(String type) + { + assertQueryFails(format("SELECT value_at_quantile(%s_agg(CAST(orderkey AS DOUBLE)), 0.5E0) > 0 FROM lineitem", type), aggregateFunctionNotRegisteredError, true); + assertQueryFails(format("SELECT value_at_quantile(%s_agg(CAST(quantity AS DOUBLE)), 0.5E0) > 0 FROM lineitem", type), aggregateFunctionNotRegisteredError, true); + assertQueryFails(format("SELECT value_at_quantile(%s_agg(CAST(quantity AS DOUBLE)), 0.5E0) > 0 FROM lineitem", type), aggregateFunctionNotRegisteredError, true); + assertQueryFails(format("SELECT value_at_quantile(%s_agg(CAST(orderkey AS DOUBLE), 2), 0.5E0) > 0 FROM lineitem", type), aggregateFunctionNotRegisteredError, true); + assertQueryFails(format("SELECT value_at_quantile(%s_agg(CAST(quantity AS DOUBLE), 3), 0.5E0) > 0 FROM lineitem", type), aggregateFunctionNotRegisteredError, true); + assertQueryFails(format("SELECT value_at_quantile(%s_agg(CAST(quantity AS DOUBLE), 4), 0.5E0) > 0 FROM lineitem", type), aggregateFunctionNotRegisteredError, true); + assertQueryFails(format("SELECT value_at_quantile(%s_agg(CAST(orderkey AS DOUBLE), 2, 0.0001E0), 0.5E0) > 0 FROM lineitem", type), aggregateFunctionNotRegisteredError, true); + assertQueryFails(format("SELECT value_at_quantile(%s_agg(CAST(quantity AS DOUBLE), 3, 0.0001E0), 0.5E0) > 0 FROM lineitem", type), aggregateFunctionNotRegisteredError, true); + assertQueryFails(format("SELECT value_at_quantile(%s_agg(CAST(quantity AS DOUBLE), 4, 0.0001E0), 0.5E0) > 0 FROM lineitem", type), aggregateFunctionNotRegisteredError, true); + } + + /** + * Comprehensive correctness testing is done in the TestQuantileDigestAggregationFunction and TestTDigestAggregationFunction + */ + // TODO: This test is flaky, to be enabled after fixing it. + @Override + @Test(enabled = false, dataProvider = "getType") + public void testStatisticalDigestGroupBy(String type) + { + assertQueryFails(format("SELECT partkey, value_at_quantile(%s_agg(CAST(orderkey AS DOUBLE)), 0.5E0) > 0 FROM lineitem GROUP BY partkey", type), + approxDistributionTypesUnsupportedError, true); + assertQueryFails(format("SELECT partkey, value_at_quantile(%s_agg(CAST(quantity AS DOUBLE)), 0.5E0) > 0 FROM lineitem GROUP BY partkey", type), + approxDistributionTypesUnsupportedError, true); + assertQueryFails(format("SELECT partkey, value_at_quantile(%s_agg(CAST(quantity AS DOUBLE)), 0.5E0) > 0 FROM lineitem GROUP BY partkey", type), + approxDistributionTypesUnsupportedError, true); + assertQueryFails(format("SELECT partkey, value_at_quantile(%s_agg(CAST(orderkey AS DOUBLE), 2), 0.5E0) > 0 FROM lineitem GROUP BY partkey", type), + approxDistributionTypesUnsupportedError, true); + assertQueryFails(format("SELECT partkey, value_at_quantile(%s_agg(CAST(quantity AS DOUBLE), 3), 0.5E0) > 0 FROM lineitem GROUP BY partkey", type), + approxDistributionTypesUnsupportedError, true); + assertQueryFails(format("SELECT partkey, value_at_quantile(%s_agg(CAST(quantity AS DOUBLE), 4), 0.5E0) > 0 FROM lineitem GROUP BY partkey", type), + approxDistributionTypesUnsupportedError, true); + assertQueryFails(format("SELECT partkey, value_at_quantile(%s_agg(CAST(orderkey AS DOUBLE), 2, 0.0001E0), 0.5E0) > 0 FROM lineitem GROUP BY partkey", type), + approxDistributionTypesUnsupportedError, true); + assertQueryFails(format("SELECT partkey, value_at_quantile(%s_agg(CAST(quantity AS DOUBLE), 3, 0.0001E0), 0.5E0) > 0 FROM lineitem GROUP BY partkey", type), + approxDistributionTypesUnsupportedError, true); + assertQueryFails(format("SELECT partkey, value_at_quantile(%s_agg(CAST(quantity AS DOUBLE), 4, 0.0001E0), 0.5E0) > 0 FROM lineitem GROUP BY partkey", type), + approxDistributionTypesUnsupportedError, true); + } + + /** + * Comprehensive correctness testing is done in the TestMergeQuantileDigestFunction and TestMergeTDigestFunction + */ + @Override + @Test(dataProvider = "getType") + public void testStatisticalDigestMerge(String type) + { + assertQueryFails(format("SELECT value_at_quantile(merge(%s), 0.5E0) > 0 FROM (SELECT partkey, %s_agg(CAST(orderkey AS DOUBLE)) as %s FROM lineitem GROUP BY partkey)", + type, + type, + type), + aggregateFunctionNotRegisteredError, true); + } + + /** + * Comprehensive correctness testing is done in the TestMergeQuantileDigestFunction and TestMergeTDigestFunction + */ + // TODO: This test is flaky, to be enabled after fixing it. + @Override + @Test(enabled = false, dataProvider = "getType") + public void testStatisticalDigestMergeGroupBy(String type) + { + assertQueryFails(format("SELECT partkey, value_at_quantile(merge(%s), 0.5E0) > 0 " + + "FROM (SELECT partkey, suppkey, %s_agg(CAST(orderkey AS DOUBLE)) as %s FROM lineitem GROUP BY partkey, suppkey)" + + "GROUP BY partkey", + type, + type, + type), + aggregateFunctionNotRegisteredError, true); + } + + /// See issue for details: https://github.com/prestodb/presto/issues/20909 + @Override + @Test + public void testSumDataSizeForStats() + { + // varchar + assertQuery("SELECT \"sum_data_size_for_stats\"(comment) FROM orders", "SELECT 787364"); + + // char + // Presto removes trailing whitespaces when casting to CHAR. + // Hard code the expected data size since there is no easy to way to compute it in H2. + assertQueryFails("SELECT \"sum_data_size_for_stats\"(CAST(comment AS CHAR(1000))) FROM orders", + "Failed to parse type \\[char\\(1000\\)]", true); + + // varbinary + assertQuery("SELECT \"sum_data_size_for_stats\"(CAST(comment AS VARBINARY)) FROM orders", "SELECT 787364"); + + // array + assertQuery("SELECT \"sum_data_size_for_stats\"(ARRAY[comment]) FROM orders", "SELECT 847364"); + assertQuery("SELECT \"sum_data_size_for_stats\"(ARRAY[comment, comment]) FROM orders", "SELECT 1634728"); + + // map + assertQuery("SELECT \"sum_data_size_for_stats\"(map(ARRAY[1], ARRAY[comment])) FROM orders", "SELECT 907364"); + assertQuery("SELECT \"sum_data_size_for_stats\"(map(ARRAY[1, 2], ARRAY[comment, comment])) FROM orders", "SELECT 1754728"); + + // row + assertQuery("SELECT \"sum_data_size_for_stats\"(ROW(comment)) FROM orders", "SELECT 847364"); + assertQuery("SELECT \"sum_data_size_for_stats\"(ROW(comment, comment)) FROM orders", "SELECT 1634728"); + } + + /// See issue for details: https://github.com/prestodb/presto/issues/20909 + @Override + @Test + public void testMaxDataSizeForStats() + { + // varchar + assertQuery("SELECT \"max_data_size_for_stats\"(comment) FROM orders", "select 82"); + + // char + assertQueryFails("SELECT \"max_data_size_for_stats\"(CAST(comment AS CHAR(1000))) FROM orders", + "Failed to parse type \\[char\\(1000\\)]", true); + + // varbinary + assertQuery("SELECT \"max_data_size_for_stats\"(CAST(comment AS VARBINARY)) FROM orders", "select 82"); + + // max_data_size_for_stats is not needed for array, map and row + } +} diff --git a/presto-native-tests/src/test/java/com.facebook.presto.nativetests/AbstractTestQueriesNative.java b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/AbstractTestQueriesNative.java new file mode 100644 index 000000000000..370b2188bb1b --- /dev/null +++ b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/AbstractTestQueriesNative.java @@ -0,0 +1,1208 @@ +package com.facebook.presto.nativetests; + +/* + * 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. + */ + +import com.facebook.presto.Session; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.testing.MaterializedResult; +import com.facebook.presto.testing.MaterializedRow; +import com.facebook.presto.tests.AbstractTestQueries; +import org.intellij.lang.annotations.Language; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.List; +import java.util.Set; + +import static com.facebook.presto.SystemSessionProperties.FIELD_NAMES_IN_JSON_CAST_ENABLED; +import static com.facebook.presto.SystemSessionProperties.JOIN_PREFILTER_BUILD_SIDE; +import static com.facebook.presto.SystemSessionProperties.KEY_BASED_SAMPLING_ENABLED; +import static com.facebook.presto.SystemSessionProperties.KEY_BASED_SAMPLING_PERCENTAGE; +import static com.facebook.presto.SystemSessionProperties.MERGE_AGGREGATIONS_WITH_AND_WITHOUT_FILTER; +import static com.facebook.presto.SystemSessionProperties.PREFILTER_FOR_GROUPBY_LIMIT; +import static com.facebook.presto.SystemSessionProperties.PREFILTER_FOR_GROUPBY_LIMIT_TIMEOUT_MS; +import static com.facebook.presto.SystemSessionProperties.REMOVE_MAP_CAST; +import static com.facebook.presto.SystemSessionProperties.REMOVE_REDUNDANT_CAST_TO_VARCHAR_IN_JOIN; +import static com.facebook.presto.common.type.BigintType.BIGINT; +import static com.facebook.presto.testing.MaterializedResult.resultBuilder; +import static com.facebook.presto.testing.assertions.Assert.assertEquals; +import static com.facebook.presto.tests.QueryAssertions.assertEqualsIgnoreOrder; +import static com.google.common.collect.ImmutableSet.toImmutableSet; +import static org.testng.Assert.assertNotEquals; + +public abstract class AbstractTestQueriesNative + extends AbstractTestQueries +{ + private static final String UNSUPPORTED_CORRELATED_SUBQUERY_ERROR_MSG = "line .*: Given correlated subquery is not supported"; + private static final String P4_HLL_TIMESTAMP_TZ_TYPE_UNSUPPORTED_ERROR = ".*Failed to parse type \\[P4HyperLogLog]. Type not registered.*"; + private static final String KHLL_TIMESTAMP_TZ_TYPE_UNSUPPORTED_ERROR = ".*inferredType Failed to parse type \\[KHyperLogLog]. Type not registered.*"; + private static final String CHAR_TYPE_UNSUPPORTED_ERROR = ".*Failed to parse type \\[char\\(.*\\)]. syntax error, unexpected LPAREN, expecting WORD.*"; + private static final String HASH_GENERATION_UNSUPPORTED_ERROR = ".*Scalar function name not registered: presto.default.\\$operator\\$.*hash.*"; + private static final String ARRAY_COMPARISON_UNSUPPORTED_ERROR = ".*ARRAY comparison not supported for values that contain nulls.*"; + private static final String CREATE_HLL_FUNCTION_NOT_REGISTERED = ".*Scalar function name not registered: presto.default.create_hll, called with arguments.*"; + + @DataProvider(name = "use_default_literal_coalesce") + public static Object[][] useDefaultLiteralCoalesce() + { + return new Object[][] {{true}}; + } + + // These tests are disabled because they use the following functions that will not be supported in Presto C++: + // custom_add, custom_sum, custom_rank, apply. + @Override + @Test(enabled = false) + public void testCustomAdd() {} + + @Override + @Test(enabled = false) + public void testCustomSum() {} + + @Override + @Test(enabled = false) + public void testCustomRank() {} + + @Override + @Test(enabled = false) + public void testApplyLambdaRepeated() {} + + @Override + @Test(enabled = false) + public void testLambdaCapture() {} + + @Override + @Test(enabled = false) + public void testLambdaInAggregationContext() {} + + @Override + @Test(enabled = false) + public void testLambdaInSubqueryContext() {} + + @Override + @Test(enabled = false) + public void testNonDeterministicInLambda() {} + + @Override + @Test(enabled = false) + public void testRowSubscriptInLambda() {} + + @Override + @Test(enabled = false) + public void testTryWithLambda() {} + + // The following tests are disabled because Velox does not support optimized hash generation. + @Override + @Test(enabled = false) + public void testDoubleDistinctPositiveAndNegativeZero(String optimizeHashGeneration) {} + + @Override + @Test(enabled = false) + public void testRealDistinctPositiveAndNegativeZero(String optimizeHashGeneration) {} + + @Override + @Test(enabled = false) + public void testRealJoinPositiveAndNegativeZero(String optimizeHashGeneration) {} + + @Override + @Test(enabled = false) + public void testDoubleJoinPositiveAndNegativeZero(String optimizeHashGeneration) {} + + // reduce_agg returns different results in Presto C++. See presto-docs/src/main/sphinx/presto_cpp/limitations.rst. + @Override + @Test + public void testReduceAgg() + { + assertQuery( + "SELECT x, reduce_agg(y, 1, (a, b) -> a * b, (a, b) -> a * b) " + + "FROM (VALUES (1, 5), (1, 6), (1, 7), (2, 8), (2, 9), (3, 10)) AS t(x, y) " + + "GROUP BY x", + "VALUES (1, 5 * 6 * 7), (2, 8 * 9), (3, 10)"); + assertQuery( + "SELECT x, reduce_agg(y, 0, (a, b) -> a + b, (a, b) -> a + b) " + + "FROM (VALUES (1, 5), (1, 6), (1, 7), (2, 8), (2, 9), (3, 10)) AS t(x, y) " + + "GROUP BY x", + "VALUES (1, 5 + 6 + 7), (2, 8 + 9), (3, 10)"); + + assertQuery( + "SELECT x, reduce_agg(y, 1, (a, b) -> a * b, (a, b) -> a * b) " + + "FROM (VALUES (1, CAST(5 AS DOUBLE)), (1, 6), (1, 7), (2, 8), (2, 9), (3, 10)) AS t(x, y) " + + "GROUP BY x", + "VALUES (1, CAST(5 AS DOUBLE) * 6 * 7), (2, 8 * 9), (3, 10)"); + assertQuery( + "SELECT x, reduce_agg(y, 0, (a, b) -> a + b, (a, b) -> a + b) " + + "FROM (VALUES (1, CAST(5 AS DOUBLE)), (1, 6), (1, 7), (2, 8), (2, 9), (3, 10)) AS t(x, y) " + + "GROUP BY x", + "VALUES (1, CAST(5 AS DOUBLE) + 6 + 7), (2, 8 + 9), (3, 10)"); + + assertQuery( + "SELECT " + + "x, " + + "array_join(" + + " array_sort(" + + " split(reduce_agg(y, '', (a, b) -> a || b, (a, b) -> a || b), '')" + + " ), " + + " ''" + + ") " + + "FROM (VALUES (1, 'a'), (1, 'b'), (1, 'c'), (2, 'd'), (2, 'e'), (3, 'f')) AS t(x, y) " + + "GROUP BY x", + "VALUES (1, 'abc'), (2, 'de'), (3, 'f')"); + + assertQuery( + "SELECT " + + "x, " + + "array_join(" + + " array_sort(" + + " reduce_agg(y, ARRAY['x'], (a, b) -> a || b, (a, b) -> a || b)" + + " ), " + + " ''" + + ") " + + "FROM (VALUES (1, ARRAY['a']), (1, ARRAY['b']), (1, ARRAY['c']), (2, ARRAY['d']), (2, ARRAY['e']), (3, ARRAY['f'])) AS t(x, y) " + + "GROUP BY x", + "VALUES (1, 'abcxxx'), (2, 'dexx'), (3, 'fx')"); + + assertQuery("SELECT REDUCE_AGG((x,y), (0,0), (x, y)->(x[1],y[1]), (x,y)->(x[1],y[1]))[1] from (select 1 x, 2 y)", "select 0"); + } + + @Override + @Test + public void testReduceAggWithNulls() + { + assertQueryFails("select reduce_agg(x, null, (x,y)->try(x+y), (x,y)->try(x+y)) from (select 1 union all select 10) T(x)", ".*REDUCE_AGG only supports non-NULL literal as the initial value.*"); + assertQueryFails("select reduce_agg(x, cast(null as bigint), (x,y)->coalesce(x, 0)+coalesce(y, 0), (x,y)->coalesce(x, 0)+coalesce(y, 0)) from (values cast(10 as bigint),10)T(x)", ".*REDUCE_AGG only supports non-NULL literal as the initial value.*"); + + // here some reduce_aggs coalesce overflow/zero-divide errors to null in the input/combine functions + assertQueryFails("select reduce_agg(x, 0, (x,y)->try(1/x+1/y), (x,y)->try(1/x+1/y)) from ((select 0) union all select 10.) T(x)", "!states->isNullAt\\(i\\) Lambda expressions in reduce_agg should not return null for non-null inputs", true); + assertQueryFails("select reduce_agg(x, 0, (x, y)->try(x+y), (x, y)->try(x+y)) from (values 2817, 9223372036854775807) AS T(x)", "!states->isNullAt\\(i\\) Lambda expressions in reduce_agg should not return null for non-null inputs", true); + assertQuery("select reduce_agg(x, array[], (x, y)->array[element_at(x, 2)], (x, y)->array[element_at(x, 2)]) from (select array[array[1]]) T(x)", "select array[null]"); + } + + // The following approx_set function tests are overriden since approx_set returns different results in Presto C++. + @Override + @Test + public void testApproxSetBigint() + { + MaterializedResult actual = computeActual("SELECT cardinality(approx_set(custkey)) FROM orders"); + + MaterializedResult expected = resultBuilder(getSession(), BIGINT) + .row(1005L) + .build(); + + assertEquals(actual.getMaterializedRows(), expected.getMaterializedRows()); + } + + @Override + @Test + public void testApproxSetBigintGroupBy() + { + MaterializedResult actual = computeActual("" + + "SELECT orderstatus, cardinality(approx_set(custkey)) " + + "FROM orders " + + "GROUP BY orderstatus"); + + MaterializedResult expected = resultBuilder(getSession(), actual.getTypes()) + .row("O", 1003L) + .row("F", 1001L) + .row("P", 304L) + .build(); + + assertEqualsIgnoreOrder(actual.getMaterializedRows(), expected.getMaterializedRows()); + } + + @Override + @Test + public void testApproxSetDouble() + { + MaterializedResult actual = computeActual("SELECT cardinality(approx_set(CAST(custkey AS DOUBLE))) FROM orders"); + + MaterializedResult expected = resultBuilder(getSession(), BIGINT) + .row(1002L) + .build(); + + assertEquals(actual.getMaterializedRows(), expected.getMaterializedRows()); + } + + @Override + @Test + public void testApproxSetDoubleGroupBy() + { + MaterializedResult actual = computeActual("" + + "SELECT orderstatus, cardinality(approx_set(CAST(custkey AS DOUBLE))) " + + "FROM orders " + + "GROUP BY orderstatus"); + + MaterializedResult expected = resultBuilder(getSession(), actual.getTypes()) + .row("O", 1002L) + .row("F", 998L) + .row("P", 304L) + .build(); + + assertEqualsIgnoreOrder(actual.getMaterializedRows(), expected.getMaterializedRows()); + } + + @Override + @Test + public void testApproxSetGroupByWithNulls() + { + MaterializedResult actual = computeActual("" + + "SELECT orderstatus, cardinality(approx_set(IF(custkey % 2 <> 0, custkey))) " + + "FROM orders " + + "GROUP BY orderstatus"); + + MaterializedResult expected = resultBuilder(getSession(), actual.getTypes()) + .row("O", 499L) + .row("F", 496L) + .row("P", 153L) + .build(); + + assertEqualsIgnoreOrder(actual.getMaterializedRows(), expected.getMaterializedRows()); + } + + @Override + @Test + public void testApproxSetVarchar() + { + MaterializedResult actual = computeActual("SELECT cardinality(approx_set(CAST(custkey AS VARCHAR))) FROM orders"); + + MaterializedResult expected = resultBuilder(getSession(), BIGINT) + .row(1015L) + .build(); + + assertEquals(actual.getMaterializedRows(), expected.getMaterializedRows()); + } + + @Override + @Test + public void testApproxSetVarcharGroupBy() + { + MaterializedResult actual = computeActual("" + + "SELECT orderstatus, cardinality(approx_set(CAST(custkey AS VARCHAR))) " + + "FROM orders " + + "GROUP BY orderstatus"); + + MaterializedResult expected = resultBuilder(getSession(), actual.getTypes()) + .row("O", 1012L) + .row("F", 1011L) + .row("P", 304L) + .build(); + + assertEqualsIgnoreOrder(actual.getMaterializedRows(), expected.getMaterializedRows()); + } + + @Override + @Test + public void testApproxSetWithNulls() + { + MaterializedResult actual = computeActual("SELECT cardinality(approx_set(IF(orderstatus = 'O', custkey))) FROM orders"); + + MaterializedResult expected = resultBuilder(getSession(), actual.getTypes()) + .row(1003L) + .build(); + + assertEquals(actual.getMaterializedRows(), expected.getMaterializedRows()); + } + + @Override + @Test + public void testApproxSetGroupByWithOnlyNullsInOneGroup() + { + MaterializedResult actual = computeActual("" + + "SELECT orderstatus, cardinality(approx_set(IF(orderstatus != 'O', custkey))) " + + "FROM orders " + + "GROUP BY orderstatus"); + + MaterializedResult expected = resultBuilder(getSession(), actual.getTypes()) + .row("O", null) + .row("F", 1001L) + .row("P", 304L) + .build(); + + assertEqualsIgnoreOrder(actual.getMaterializedRows(), expected.getMaterializedRows()); + } + + @Override + @Test + public void testArrayCumSumDecimals() + { + String functionNotRegisteredError = ".*Scalar function presto.default.array_cum_sum not registered with arguments.*"; + String sql = "select array_cum_sum(k) from (values (array[cast(5.1 as decimal(38, 1)), 6, 0]), (ARRAY[]), (CAST(NULL AS array(decimal)))) t(k)"; + assertQueryFails(sql, functionNotRegisteredError, true); + + sql = "select array_cum_sum(k) from (values (array[cast(5.1 as decimal(38, 1)), 6, null, 3]), (array[cast(null as decimal(38, 1)), 6, null, 3])) t(k)"; + assertQueryFails(sql, functionNotRegisteredError, true); + } + + @Override + @Test + public void testArrayCumSumVarchar() + { + String sql = "select array_cum_sum(k) from (values (array[cast('5.1' as varchar), '6', '0']), (ARRAY[]), (CAST(NULL AS array(varchar)))) t(k)"; + assertQueryFails(sql, ".*Scalar function presto.default.array_cum_sum not registered with arguments.*", true); + + sql = "select array_cum_sum(k) from (values (array[cast(null as varchar), '6', '0'])) t(k)"; + assertQueryFails(sql, ".*Scalar function presto.default.array_cum_sum not registered with arguments: \\(ARRAY.*", true); + } + + @Override + @Test + public void testGroupByLimit() + { + Session prefilter = Session.builder(getSession()) + .setSystemProperty(PREFILTER_FOR_GROUPBY_LIMIT, "true") + .build(); + MaterializedResult result1 = computeActual(prefilter, "select count(shipdate), orderkey from lineitem group by orderkey limit 100000"); + MaterializedResult result2 = computeActual("select count(shipdate), orderkey from lineitem group by orderkey limit 100000"); + assertEqualsIgnoreOrder(result1, result2, "Prefilter and without prefilter don't give matching results"); + + assertQueryFails(prefilter, "select count(custkey), orderkey from orders where orderstatus='F' and orderkey < 50 group by orderkey limit 100", HASH_GENERATION_UNSUPPORTED_ERROR, true); + assertQuery(prefilter, "select count(1) from (select count(custkey), orderkey from orders where orderstatus='F' and orderkey < 50 group by orderkey limit 4)", "select 4"); + assertQuery(prefilter, "select count(comment), orderstatus from (select upper(comment) comment, upper(orderstatus) orderstatus from orders where orderkey < 50) group by orderstatus limit 100", "values (5, 'F'), (10, 'O')"); + + assertQuery(prefilter, "select count(comment), orderstatus from (select upper(comment) comment, upper(orderstatus) orderstatus from orders where orderkey < 50) group by orderstatus having count(1) > 1 limit 100", "values (5, 'F'), (10, 'O')"); + + prefilter = Session.builder(getSession()) + .setSystemProperty(PREFILTER_FOR_GROUPBY_LIMIT, "true") + .setSystemProperty(PREFILTER_FOR_GROUPBY_LIMIT_TIMEOUT_MS, "1") + .build(); + + result1 = computeActual(prefilter, "select count(shipdate), orderkey from lineitem group by orderkey limit 100000"); + result2 = computeActual("select count(shipdate), orderkey from lineitem group by orderkey limit 100000"); + assertEqualsIgnoreOrder(result1, result2, "Prefilter and without prefilter don't give matching results"); + + assertQuery(prefilter, "select count(1) from (select count(custkey), orderkey from orders group by orderkey limit 100000)", "values 15000"); + assertQuery(prefilter, "select count(1) from (select count(custkey), orderkey from orders group by orderkey limit 4)", "select 4"); + assertQuery(prefilter, "select count(1) from (select count(comment), orderstatus from (select upper(comment) comment, upper(orderstatus) orderstatus from orders) group by orderstatus limit 100000)", "values 3"); + } + + @Override + @Test + public void testJoinPrefilter() + { + { + // Orig + String testQuery = "SELECT 1 from region join nation using(regionkey)"; + MaterializedResult result = computeActual("explain(type distributed) " + testQuery); + assertEquals(((String) result.getMaterializedRows().get(0).getField(0)).indexOf("SemiJoin"), -1); + result = computeActual(testQuery); + assertEquals(result.getRowCount(), 25); + + // With feature + Session session = Session.builder(getSession()) + .setSystemProperty(JOIN_PREFILTER_BUILD_SIDE, String.valueOf(true)) + .build(); + result = computeActual(session, "explain(type distributed) " + testQuery); + assertNotEquals(((String) result.getMaterializedRows().get(0).getField(0)).indexOf("SemiJoin"), -1); + result = computeActual(session, testQuery); + assertEquals(result.getRowCount(), 25); + } + + { + // Orig + @Language("SQL") String testQuery = "SELECT 1 from region r join nation n on cast(r.regionkey as varchar) = cast(n.regionkey as varchar)"; + MaterializedResult result = computeActual("explain(type distributed) " + testQuery); + assertEquals(((String) result.getMaterializedRows().get(0).getField(0)).indexOf("SemiJoin"), -1); + result = computeActual(testQuery); + assertEquals(result.getRowCount(), 25); + + // With feature + Session session = Session.builder(getSession()) + .setSystemProperty(JOIN_PREFILTER_BUILD_SIDE, String.valueOf(true)) + .setSystemProperty(REMOVE_REDUNDANT_CAST_TO_VARCHAR_IN_JOIN, String.valueOf(false)) + .build(); + result = computeActual(session, "explain(type distributed) " + testQuery); + assertNotEquals(((String) result.getMaterializedRows().get(0).getField(0)).indexOf("SemiJoin"), -1); + assertNotEquals(((String) result.getMaterializedRows().get(0).getField(0)).indexOf("XX_HASH_64"), -1); + assertQueryFails(session, testQuery, HASH_GENERATION_UNSUPPORTED_ERROR, true); + } + + { + // Orig + String testQuery = "SELECT 1 from lineitem l join orders o on l.orderkey = o.orderkey and l.suppkey = o.custkey"; + MaterializedResult result = computeActual("explain(type distributed) " + testQuery); + assertEquals(((String) result.getMaterializedRows().get(0).getField(0)).indexOf("SemiJoin"), -1); + result = computeActual(testQuery); + assertEquals(result.getRowCount(), 37); + + // With feature + Session session = Session.builder(getSession()) + .setSystemProperty(JOIN_PREFILTER_BUILD_SIDE, String.valueOf(true)) + .build(); + result = computeActual(session, "explain(type distributed) " + testQuery); + assertNotEquals(((String) result.getMaterializedRows().get(0).getField(0)).indexOf("SemiJoin"), -1); + assertNotEquals(((String) result.getMaterializedRows().get(0).getField(0)).indexOf("XX_HASH_64"), -1); + assertQueryFails(session, testQuery, HASH_GENERATION_UNSUPPORTED_ERROR, true); + } + } + + @Override + @Test(expectedExceptions = {RuntimeException.class, PrestoException.class}, expectedExceptionsMessageRegExp = CREATE_HLL_FUNCTION_NOT_REGISTERED) + public void testMergeEmptyNonEmptyApproxSetWithDifferentMaxError() + { + computeActual("SELECT cardinality(merge(c)) FROM (SELECT create_hll(custkey, 0.1) c FROM orders UNION ALL SELECT empty_approx_set(0.2))"); + } + + @Override + @Test + public void testMergeHyperLogLog() + { + assertQueryFails("SELECT cardinality(merge(create_hll(custkey))) FROM orders", CREATE_HLL_FUNCTION_NOT_REGISTERED, true); + } + + @Override + @Test + public void testMergeHyperLogLogGroupBy() + { + assertQueryFails( + "SELECT orderstatus, cardinality(merge(create_hll(custkey))) " + + "FROM orders " + + "GROUP BY orderstatus", CREATE_HLL_FUNCTION_NOT_REGISTERED, true); + } + + @Override + @Test + public void testMergeHyperLogLogWithNulls() + { + assertQueryFails("SELECT cardinality(merge(create_hll(IF(orderstatus = 'O', custkey)))) FROM orders", + CREATE_HLL_FUNCTION_NOT_REGISTERED, true); + } + + @Override + @Test + public void testMergeHyperLogLogGroupByWithNulls() + { + assertQueryFails( + "SELECT orderstatus, cardinality(merge(create_hll(IF(orderstatus != 'O', custkey)))) " + + "FROM orders " + + "GROUP BY orderstatus", + CREATE_HLL_FUNCTION_NOT_REGISTERED, true); + } + + @Override + @Test + public void testKeyBasedSampling() + { + String[] queries = new String[]{ + "select count(1) from orders join lineitem using(orderkey)", + "select count(1) from (select custkey, max(orderkey) from orders group by custkey)", + "select count_if(m >= 1) from (select max(orderkey) over(partition by custkey) m from orders)", + "select cast(m as bigint) from (select sum(totalprice) over(partition by custkey order by comment) m from orders order by 1 desc limit 1)", + "select count(1) from lineitem where orderkey in (select orderkey from orders where length(comment) > 7)", + "select count(1) from lineitem where orderkey not in (select orderkey from orders where length(comment) > 27)", + "select count(1) from (select distinct orderkey, custkey from orders)", + }; + int[] unsampledResults = new int[]{60175, 1000, 15000, 5408941, 60175, 9256, 15000}; + for (int i = 0; i < queries.length; i++) { + assertQuery(queries[i], "select " + unsampledResults[i]); + } + + Session sessionWithKeyBasedSampling = Session.builder(getSession()) + .setSystemProperty(KEY_BASED_SAMPLING_ENABLED, "true") + .setSystemProperty(KEY_BASED_SAMPLING_PERCENTAGE, "0.2") + .build(); + for (int i = 0; i < queries.length; i++) { + assertQueryFails(sessionWithKeyBasedSampling, queries[i], "Scalar function name not registered: presto.default.key_sampling_percent, called with arguments", true); + } + + sessionWithKeyBasedSampling = Session.builder(getSession()) + .setSystemProperty(KEY_BASED_SAMPLING_ENABLED, "true") + .setSystemProperty(KEY_BASED_SAMPLING_PERCENTAGE, "0.1") + .build(); + for (int i = 0; i < queries.length; i++) { + assertQueryFails(sessionWithKeyBasedSampling, queries[i], "Scalar function name not registered: presto.default.key_sampling_percent, called with arguments", true); + } + } + + @Override + @Test + public void testP4ApproxSetBigint() + { + assertQueryFails("SELECT cardinality(cast(approx_set(custkey) AS P4HYPERLOGLOG)) FROM orders", + P4_HLL_TIMESTAMP_TZ_TYPE_UNSUPPORTED_ERROR, true); + } + + @Override + @Test + public void testP4ApproxSetVarchar() + { + assertQueryFails("SELECT cardinality(cast(approx_set(CAST(custkey AS VARCHAR)) AS P4HYPERLOGLOG)) FROM orders", + P4_HLL_TIMESTAMP_TZ_TYPE_UNSUPPORTED_ERROR, true); + } + + @Override + @Test + public void testP4ApproxSetBigintGroupBy() + { + assertQueryFails( + "SELECT orderstatus, cardinality(cast(approx_set(custkey) AS P4HYPERLOGLOG)) " + + "FROM orders " + + "GROUP BY orderstatus", P4_HLL_TIMESTAMP_TZ_TYPE_UNSUPPORTED_ERROR, true); + } + + @Override + @Test + public void testP4ApproxSetDouble() + { + assertQueryFails("SELECT cardinality(cast(approx_set(CAST(custkey AS DOUBLE)) AS P4HYPERLOGLOG)) FROM orders", + P4_HLL_TIMESTAMP_TZ_TYPE_UNSUPPORTED_ERROR, true); + } + + @Override + @Test + public void testP4ApproxSetDoubleGroupBy() + { + assertQueryFails( + "SELECT orderstatus, cardinality(cast(approx_set(CAST(custkey AS DOUBLE)) AS P4HYPERLOGLOG)) " + + "FROM orders " + + "GROUP BY orderstatus", P4_HLL_TIMESTAMP_TZ_TYPE_UNSUPPORTED_ERROR, true); + } + + @Override + @Test + public void testP4ApproxSetGroupByWithNulls() + { + assertQueryFails( + "SELECT orderstatus, cardinality(cast(approx_set(IF(custkey % 2 <> 0, custkey)) AS P4HYPERLOGLOG)) " + + "FROM orders " + + "GROUP BY orderstatus", P4_HLL_TIMESTAMP_TZ_TYPE_UNSUPPORTED_ERROR, true); + } + + @Override + @Test + public void testP4ApproxSetGroupByWithOnlyNullsInOneGroup() + { + assertQueryFails( + "SELECT orderstatus, cardinality(cast(approx_set(IF(orderstatus != 'O', custkey)) AS P4HYPERLOGLOG)) " + + "FROM orders " + + "GROUP BY orderstatus", P4_HLL_TIMESTAMP_TZ_TYPE_UNSUPPORTED_ERROR, true); + } + + @Override + @Test + public void testP4ApproxSetOnlyNulls() + { + assertQueryFails("SELECT cardinality(cast(approx_set(null) AS P4HYPERLOGLOG)) FROM orders", + P4_HLL_TIMESTAMP_TZ_TYPE_UNSUPPORTED_ERROR, true); + } + + @Override + @Test + public void testP4ApproxSetVarcharGroupBy() + { + assertQueryFails( + "SELECT orderstatus, cardinality(cast(approx_set(CAST(custkey AS VARCHAR)) AS P4HYPERLOGLOG)) " + + "FROM orders " + + "GROUP BY orderstatus", P4_HLL_TIMESTAMP_TZ_TYPE_UNSUPPORTED_ERROR, true); + } + + @Override + @Test + public void testP4ApproxSetWithNulls() + { + assertQueryFails("SELECT cardinality(cast(approx_set(IF(orderstatus = 'O', custkey)) AS P4HYPERLOGLOG)) FROM orders", + P4_HLL_TIMESTAMP_TZ_TYPE_UNSUPPORTED_ERROR, true); + } + + @Override + @Test + public void testLargeBytecode() + { + StringBuilder stringBuilder = new StringBuilder("SELECT x FROM (SELECT orderkey x, custkey y from orders limit 10) WHERE CASE true "); + // Generate 100 cases. + for (int i = 0; i < 100; i++) { + stringBuilder.append(" when x in ("); + for (int j = 0; j < 20; j++) { + stringBuilder.append("random(" + (i * 100 + j) + "), "); + } + + stringBuilder.append("random(" + i + ")) then x = random()"); + } + + stringBuilder.append("else x = random() end"); + assertQueryFails(stringBuilder.toString(), + "input > 0 \\(0 vs. 0\\) bound must be positive presto.default.random\\(0:INTEGER\\)", true); + } + + @Override + @Test + public void testArraySplitIntoChunks() + { + String functionNotRegistered = "Scalar function name not registered: presto.default.array_split_into_chunks"; + + String sql = "select array_split_into_chunks(array[1, 2, 3, 4, 5, 6], 2)"; + assertQueryFails(sql, functionNotRegistered, true); + + sql = "select array_split_into_chunks(array[1, 2, 3, 4, 5], 3)"; + assertQueryFails(sql, functionNotRegistered, true); + + sql = "select array_split_into_chunks(array[1, 2, 3], 5)"; + assertQueryFails(sql, functionNotRegistered, true); + + sql = "select array_split_into_chunks(null, 2)"; + assertQuery(sql, "values null"); + + sql = "select array_split_into_chunks(array[1, 2, 3], 0)"; + assertQueryFails(sql, functionNotRegistered, true); + + sql = "select array_split_into_chunks(array[1, 2, 3], -1)"; + assertQueryFails(sql, functionNotRegistered, true); + + sql = "select array_split_into_chunks(array[1, null, 3, null, 5], 2)"; + assertQueryFails(sql, functionNotRegistered, true); + + sql = "select array_split_into_chunks(array['a', 'b', 'c', 'd'], 2)"; + assertQueryFails(sql, functionNotRegistered, true); + + sql = "select array_split_into_chunks(array[1.1, 2.2, 3.3, 4.4, 5.5], 2)"; + assertQueryFails(sql, functionNotRegistered, true); + + sql = "select array_split_into_chunks(array[null, null, null], 0)"; + assertQueryFails(sql, functionNotRegistered, true); + + sql = "select array_split_into_chunks(array[null, null, null], 2)"; + assertQueryFails(sql, functionNotRegistered, true); + + sql = "select array_split_into_chunks(array[null, 1, 2], 5)"; + assertQueryFails(sql, functionNotRegistered, true); + + sql = "select array_split_into_chunks(array[], 0)"; + assertQueryFails(sql, functionNotRegistered, true); + } + + @Override + @Test + public void testMapUnionSumOverflow() + { + assertQueryFails( + "select y, map_union_sum(x) from (select 1 y, map(array['x', 'z', 'y'], cast(array[null,30,100] as array)) x " + + "union all select 1 y, map(array['x', 'y'], cast(array[1,100] as array))x) group by y", "Value 200 exceeds 127", true); + assertQueryFails( + "select y, map_union_sum(x) from (select 1 y, map(array['x', 'z', 'y'], cast(array[null,30, 32760] as array)) x " + + "union all select 1 y, map(array['x', 'y'], cast(array[1,100] as array))x) group by y", "Value 32860 exceeds 32767", true); + } + + @Override + @Test + public void testRows() + { + // Using JSON_FORMAT(CAST(_ AS JSON)) because H2 does not support ROW type + Session session = Session.builder(getSession()).setSystemProperty(FIELD_NAMES_IN_JSON_CAST_ENABLED, "true").build(); + assertQuery(session, "SELECT JSON_FORMAT(CAST(ROW(1 + 2, CONCAT('a', 'b')) AS JSON))", "SELECT '{\"\":3,\"\":\"ab\"}'"); + assertQuery(session, "SELECT JSON_FORMAT(CAST(ROW(a + b) AS JSON)) FROM (VALUES (1, 2)) AS t(a, b)", "SELECT '[3]'"); + assertQuery(session, "SELECT JSON_FORMAT(CAST(ROW(1, ROW(9, a, ARRAY[], NULL), ROW(1, 2)) AS JSON)) FROM (VALUES ('a')) t(a)", + "SELECT '[1,[9,\"a\",[],null],[1,2]]'"); + assertQuery(session, "SELECT JSON_FORMAT(CAST(ROW(ROW(ROW(ROW(ROW(a, b), c), d), e), f) AS JSON)) FROM (VALUES (ROW(0, 1), 2, '3', NULL, ARRAY[5], ARRAY[])) t(a, b, c, d, e, f)", + "SELECT '[[[[[[0,1],2],\"3\"],null],[5]],[]]'"); + assertQuery(session, "SELECT JSON_FORMAT(CAST(ARRAY_AGG(ROW(a, b)) AS JSON)) FROM (VALUES (1, 2), (3, 4), (5, 6)) t(a, b)", + "SELECT '[[1,2],[3,4],[5,6]]'"); + assertQuery(session, "SELECT CONTAINS(ARRAY_AGG(ROW(a, b)), ROW(1, 2)) FROM (VALUES (1, 2), (3, 4), (5, 6)) t(a, b)", "SELECT TRUE"); + assertQuery(session, "SELECT JSON_FORMAT(CAST(ARRAY_AGG(ROW(c, d)) AS JSON)) FROM (VALUES (ARRAY[1, 3, 5], ARRAY[2, 4, 6])) AS t(a, b) CROSS JOIN UNNEST(a, b) AS u(c, d)", + "SELECT '[[1,2],[3,4],[5,6]]'"); + assertQuery(session, "SELECT JSON_FORMAT(CAST(ROW(x, y, z) AS JSON)) FROM (VALUES ROW(1, NULL, '3')) t(x,y,z)", "SELECT '[1,null,\"3\"]'"); + assertQuery(session, "SELECT JSON_FORMAT(CAST(ROW(x, y, z) AS JSON)) FROM (VALUES ROW(1, CAST(NULL AS INTEGER), '3')) t(x,y,z)", "SELECT '[1,null,\"3\"]'"); + } + + @Override + @Test + public void testDuplicateUnnestRows() + { + assertQueryFails("SELECT * from (select * FROM (values 1) as t(k)) CROSS JOIN unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)]) AS r(r1, r2, r3, r4)", + "Field not found: field_.*. Available fields are: field, field_.*", true); + assertQueryFails("SELECT * from (select * FROM (values 1) as t(k)) CROSS JOIN unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)], ARRAY[row(10, 13, 15), row(23, 25, 20)]) AS r(r1, r2, r3, r4, r5, r6, r7)", + "Field not found: field_.*. Available fields are: field, field_.*", true); + assertQueryFails("SELECT * from (select * FROM (values 1) as t(k)) CROSS JOIN unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)]) WITH ORDINALITY AS r(r1, r2, r3, r4, ord)", + "Field not found: field_.*. Available fields are: field, field_.*", true); + assertQueryFails("SELECT * from (select * FROM (values 1) as t(k)) CROSS JOIN unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)], ARRAY[row(10, 13, 15), row(23, 25, 20)]) WITH ORDINALITY AS r(r1, r2, r3, r4, r5, r6, r7, ord)", + "Field not found: field_.*. Available fields are: field, field_.*", true); + + assertQueryFails("SELECT * from unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)]) AS r(r1, r2, r3, r4)", + "Field not found: field_.*. Available fields are: field, field_.*", true); + assertQueryFails("SELECT * from unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)]) WITH ORDINALITY AS r(r1, r2, r3, r4, ord)", + "Field not found: field_.*. Available fields are: field, field_.*", true); + } + + @Override + @Test + public void testMergeEmptyNonEmptyApproxSet() + { + assertQueryFails("SELECT cardinality(merge(c)) FROM (SELECT create_hll(custkey) c FROM orders UNION ALL SELECT empty_approx_set())", + CREATE_HLL_FUNCTION_NOT_REGISTERED, true); + } + + @Override + @Test + public void testSetUnionWithNulls() + { + // all nulls should return empty array to match behavior of array_distinct(flatten(array_agg(x))) + assertQueryFails( + "select set_union(x) from (values null, null, null) as t(x)", + "Unexpected type UNKNOWN", true); + // nulls inside arrays should be captured while pure nulls should be ignored + assertQueryFails( + "select set_union(x) from (values null, array[null], null) as t(x)", + "Unexpected type UNKNOWN", true); + // return null for empty rows + assertQueryFails( + "select set_union(x) from (values null, array[null], null) as t(x) where x != null", + "Unexpected type UNKNOWN", true); + } + + @Override + @Test + public void testSamplingJoinChain() + { + Session sessionWithKeyBasedSampling = Session.builder(getSession()) + .setSystemProperty(KEY_BASED_SAMPLING_ENABLED, "true") + .build(); + String query = "select count(1) FROM lineitem l left JOIN orders o ON l.orderkey = o.orderkey JOIN customer c ON o.custkey = c.custkey"; + + assertQuery(query, "select 60175"); + assertQueryFails(sessionWithKeyBasedSampling, query, "Scalar function name not registered: presto.default.key_sampling_percent, called with arguments", true); + } + + @Override + @Test + public void testMergeEmptyNonEmptyApproxSetWithSameMaxError() + { + assertQueryFails("SELECT cardinality(merge(c)) FROM (SELECT create_hll(custkey, 0.1) c FROM orders UNION ALL SELECT empty_approx_set(0.1))", + CREATE_HLL_FUNCTION_NOT_REGISTERED, true); + } + + @Override + @Test + public void testCorrelatedNonAggregationScalarSubqueries() + { + String subqueryReturnedTooManyRows = ".*Scalar sub-query has returned multiple rows.*"; + + assertQuery("SELECT (SELECT 1 WHERE a = 2) FROM (VALUES 1) t(a)", "SELECT null"); + assertQuery("SELECT (SELECT 2 WHERE a = 1) FROM (VALUES 1) t(a)", "SELECT 2"); + assertQueryFails( + "SELECT (SELECT 2 FROM (VALUES 3, 4) WHERE a = 1) FROM (VALUES 1) t(a)", + subqueryReturnedTooManyRows); + + // multiple subquery output projections + // TODO: Check why native query runner doesn't throw an error for below queries. + computeActual("SELECT name FROM nation n WHERE 'AFRICA' = (SELECT 'bleh' FROM region WHERE regionkey > n.regionkey)"); + computeActual("SELECT name FROM nation n WHERE 'AFRICA' = (SELECT name FROM region WHERE regionkey > n.regionkey)"); + assertQueryFails( + "SELECT name FROM nation n WHERE 1 = (SELECT 1 FROM region WHERE regionkey > n.regionkey)", + subqueryReturnedTooManyRows); + + // correlation used in subquery output + assertQueryFails( + "SELECT name FROM nation n WHERE 'AFRICA' = (SELECT n.name FROM region WHERE regionkey > n.regionkey)", + UNSUPPORTED_CORRELATED_SUBQUERY_ERROR_MSG); + + assertQuery( + "SELECT (SELECT 2 WHERE o.orderkey = 1) FROM orders o ORDER BY orderkey LIMIT 5", + "VALUES 2, null, null, null, null"); + // outputs plain correlated orderkey symbol which causes ambiguity with outer query orderkey symbol + assertQueryFails( + "SELECT (SELECT o.orderkey WHERE o.orderkey = 1) FROM orders o ORDER BY orderkey LIMIT 5", + UNSUPPORTED_CORRELATED_SUBQUERY_ERROR_MSG); + assertQueryFails( + "SELECT (SELECT o.orderkey * 2 WHERE o.orderkey = 1) FROM orders o ORDER BY orderkey LIMIT 5", + UNSUPPORTED_CORRELATED_SUBQUERY_ERROR_MSG); + // correlation used outside the subquery + assertQueryFails( + "SELECT o.orderkey, (SELECT o.orderkey * 2 WHERE o.orderkey = 1) FROM orders o ORDER BY orderkey LIMIT 5", + UNSUPPORTED_CORRELATED_SUBQUERY_ERROR_MSG); + + // aggregation with having +// TODO: uncomment below test once #8456 is fixed +// assertQuery("SELECT (SELECT avg(totalprice) FROM orders GROUP BY custkey, orderdate HAVING avg(totalprice) < a) FROM (VALUES 900) t(a)"); + + // correlation in predicate + assertQuery("SELECT name FROM nation n WHERE 'AFRICA' = (SELECT name FROM region WHERE regionkey = n.regionkey)"); + + // same correlation in predicate and projection + assertQueryFails( + "SELECT nationkey FROM nation n WHERE " + + "(SELECT n.regionkey * 2 FROM region r WHERE n.regionkey = r.regionkey) > 6", + UNSUPPORTED_CORRELATED_SUBQUERY_ERROR_MSG); + + // different correlation in predicate and projection + assertQueryFails( + "SELECT nationkey FROM nation n WHERE " + + "(SELECT n.nationkey * 2 FROM region r WHERE n.regionkey = r.regionkey) > 6", + UNSUPPORTED_CORRELATED_SUBQUERY_ERROR_MSG); + + // correlation used in subrelation + assertQuery( + "SELECT nationkey FROM nation n WHERE " + + "(SELECT regionkey * 2 FROM (SELECT regionkey FROM region r WHERE n.regionkey = r.regionkey)) > 6 " + + "ORDER BY 1 LIMIT 3", + "VALUES 4, 10, 11"); // h2 didn't make it + + // with duplicated rows + assertQuery( + "SELECT (SELECT name FROM nation WHERE nationkey = a) FROM (VALUES 1, 1, 2, 3) t(a)", + "VALUES 'ARGENTINA', 'ARGENTINA', 'BRAZIL', 'CANADA'"); // h2 didn't make it + + // returning null when nothing matched + assertQuery( + "SELECT (SELECT name FROM nation WHERE nationkey = a) FROM (VALUES 31) t(a)", + "VALUES null"); + + assertQuery( + "SELECT (SELECT r.name FROM nation n, region r WHERE r.regionkey = n.regionkey AND n.nationkey = a) FROM (VALUES 1) t(a)", + "VALUES 'AMERICA'"); + } + + @Override + @Test + public void testScalarSubquery() + { + // nested + assertQuery("SELECT (SELECT (SELECT (SELECT 1)))"); + + // TODO: Investigate error seen with disabled queries: 'expected types count (22) does not + // match actual column count (16)' + // aggregation + computeActual("SELECT * FROM lineitem WHERE orderkey = \n" + + "(SELECT max(orderkey) FROM orders)"); + + // no output + assertQuery("SELECT * FROM lineitem WHERE orderkey = \n" + + "(SELECT orderkey FROM orders WHERE 0=1)"); + + // no output matching with null test + computeActual("SELECT * FROM lineitem WHERE \n" + + "(SELECT orderkey FROM orders WHERE 0=1) " + + "is null"); + assertQuery("SELECT * FROM lineitem WHERE \n" + + "(SELECT orderkey FROM orders WHERE 0=1) " + + "is not null"); + + // subquery results and in in-predicate + assertQuery("SELECT (SELECT 1) IN (1, 2, 3)"); + assertQuery("SELECT (SELECT 1) IN ( 2, 3)"); + + // multiple subqueries + assertQuery("SELECT (SELECT 1) = (SELECT 3)"); + assertQuery("SELECT (SELECT 1) < (SELECT 3)"); + assertQuery("SELECT COUNT(*) FROM lineitem WHERE " + + "(SELECT min(orderkey) FROM orders)" + + "<" + + "(SELECT max(orderkey) FROM orders)"); + assertQuery("SELECT (SELECT 1), (SELECT 2), (SELECT 3)"); + + // distinct + assertQuery("SELECT DISTINCT orderkey FROM lineitem " + + "WHERE orderkey BETWEEN" + + " (SELECT avg(orderkey) FROM orders) - 10 " + + " AND" + + " (SELECT avg(orderkey) FROM orders) + 10"); + + // subqueries with joins + assertQuery("SELECT o1.orderkey, COUNT(*) " + + "FROM orders o1 " + + "INNER JOIN (SELECT * FROM orders ORDER BY orderkey LIMIT 10) o2 " + + "ON o1.orderkey " + + "BETWEEN (SELECT avg(orderkey) FROM orders) - 10 AND (SELECT avg(orderkey) FROM orders) + 10 " + + "GROUP BY o1.orderkey"); + assertQuery("SELECT o1.orderkey, COUNT(*) " + + "FROM (SELECT * FROM orders ORDER BY orderkey LIMIT 5) o1 " + + "LEFT JOIN (SELECT * FROM orders ORDER BY orderkey LIMIT 10) o2 " + + "ON o1.orderkey " + + "BETWEEN (SELECT avg(orderkey) FROM orders) - 10 AND (SELECT avg(orderkey) FROM orders) + 10 " + + "GROUP BY o1.orderkey"); + assertQuery("SELECT o1.orderkey, COUNT(*) " + + "FROM orders o1 RIGHT JOIN (SELECT * FROM orders ORDER BY orderkey LIMIT 10) o2 " + + "ON o1.orderkey " + + "BETWEEN (SELECT avg(orderkey) FROM orders) - 10 AND (SELECT avg(orderkey) FROM orders) + 10 " + + "GROUP BY o1.orderkey"); + assertQuery("SELECT DISTINCT COUNT(*) " + + "FROM (SELECT * FROM orders ORDER BY orderkey LIMIT 5) o1 " + + "FULL JOIN (SELECT * FROM orders ORDER BY orderkey LIMIT 10) o2 " + + "ON o1.orderkey " + + "BETWEEN (SELECT avg(orderkey) FROM orders) - 10 AND (SELECT avg(orderkey) FROM orders) + 10 " + + "GROUP BY o1.orderkey", + "VALUES 1, 10"); + + // subqueries with ORDER BY + assertQuery("SELECT orderkey, totalprice FROM orders ORDER BY (SELECT 2)"); + + // subquery returns multiple rows + String multipleRowsErrorMsg = ".*Expected single row of input.*"; + assertQueryFails("SELECT * FROM lineitem WHERE orderkey = (\n" + + "SELECT orderkey FROM orders ORDER BY totalprice)", + multipleRowsErrorMsg); + assertQueryFails("SELECT orderkey, totalprice FROM orders ORDER BY (VALUES 1, 2)", + multipleRowsErrorMsg); + + // exposes a bug in optimize hash generation because EnforceSingleNode does not + // support more than one column from the underlying query + assertQuery("SELECT custkey, (SELECT DISTINCT custkey FROM orders ORDER BY custkey LIMIT 1) FROM orders"); + + // cast scalar sub-query + assertQuery("SELECT 1.0/(SELECT 1), CAST(1.0 AS REAL)/(SELECT 1), 1/(SELECT 1)"); + assertQuery("SELECT 1.0 = (SELECT 1) AND 1 = (SELECT 1), 2.0 = (SELECT 1) WHERE 1.0 = (SELECT 1) AND 1 = (SELECT 1)"); + assertQuery("SELECT 1.0 = (SELECT 1), 2.0 = (SELECT 1), CAST(2.0 AS REAL) = (SELECT 1) WHERE 1.0 = (SELECT 1)"); + + // coerce correlated symbols + assertQuery("SELECT * FROM (VALUES 1) t(a) WHERE 1=(SELECT count(*) WHERE 1.0 = a)", "SELECT 1"); + assertQuery("SELECT * FROM (VALUES 1.0) t(a) WHERE 1=(SELECT count(*) WHERE 1 = a)", "SELECT 1.0"); + } + + @Override + @Test + public void testCorrelatedScalarSubqueries() + { + assertQuery("SELECT (SELECT n.nationkey + n.NATIONKEY) FROM nation n"); + assertQuery("SELECT (SELECT 2 * n.nationkey) FROM nation n"); + assertQuery("SELECT nationkey FROM nation n WHERE 2 = (SELECT 2 * n.nationkey)"); + assertQuery("SELECT nationkey FROM nation n ORDER BY (SELECT 2 * n.nationkey)"); + + // group by + assertQuery("SELECT max(n.regionkey), 2 * n.nationkey, (SELECT n.nationkey) FROM nation n GROUP BY n.nationkey"); + assertQuery( + "SELECT max(l.quantity), 2 * l.orderkey FROM lineitem l GROUP BY l.orderkey HAVING max(l.quantity) < (SELECT l.orderkey)"); + assertQuery("SELECT max(l.quantity), 2 * l.orderkey FROM lineitem l GROUP BY l.orderkey, (SELECT l.orderkey)"); + + // join + assertQuery("SELECT * FROM nation n1 JOIN nation n2 ON n1.nationkey = (SELECT n2.nationkey)"); + assertQueryFails( + "SELECT (SELECT l3.* FROM lineitem l2 CROSS JOIN (SELECT l1.orderkey) l3 LIMIT 1) FROM lineitem l1", + UNSUPPORTED_CORRELATED_SUBQUERY_ERROR_MSG); + + // subrelation + assertQuery( + "SELECT 1 FROM nation n WHERE 2 * nationkey - 1 = (SELECT * FROM (SELECT n.nationkey))", + "SELECT 1"); // h2 fails to parse this query + + // two level of nesting + assertQuery("SELECT * FROM nation n WHERE 2 = (SELECT (SELECT 2 * n.nationkey))"); + + // explicit LIMIT in subquery + assertQueryFails( + "SELECT (SELECT count(*) FROM (VALUES (7,1)) t(orderkey, value) WHERE orderkey = corr_key LIMIT 1) FROM (values 7) t(corr_key)", + UNSUPPORTED_CORRELATED_SUBQUERY_ERROR_MSG); + + assertQuery( + "SELECT (SELECT count(*) FROM (VALUES (7,1)) t(orderkey, value) WHERE orderkey = corr_key GROUP BY value LIMIT 2) FROM (values 7) t(corr_key)"); + + // Limit(1) and non-constant output symbol of the subquery (count) + assertQueryFails("SELECT (SELECT count(*) FROM (VALUES (7,1), (7,2)) t(orderkey, value) WHERE orderkey = corr_key GROUP BY value LIMIT 1) FROM (values 7) t(corr_key)", + UNSUPPORTED_CORRELATED_SUBQUERY_ERROR_MSG); + } + + @Override + @Test + public void testLikePrefixAndSuffixWithChars() + { + assertQueryFails("select x like 'abc%' from (values CAST ('abc' AS CHAR(3)), CAST ('def' AS CHAR(3)), CAST ('bcd' AS CHAR(3))) T(x)", CHAR_TYPE_UNSUPPORTED_ERROR, true); + assertQueryFails("select x like '%abc%' from (values CAST ('xabcy' AS CHAR(5)), CAST ('abxabcdef' AS CHAR(9)), CAST ('bcd' AS CHAR(3)), CAST ('xabcyabcz' AS CHAR(9))) T(x)", CHAR_TYPE_UNSUPPORTED_ERROR, true); + assertQueryFails( + "select x like '%abc' from (values CAST('xa bc' AS CHAR(5)), CAST ('xabcy' AS CHAR(5)), CAST ('abcd' AS CHAR(4)), CAST ('xabc' AS CHAR(4)), CAST (' xabc' AS CHAR(5))) T(x)", CHAR_TYPE_UNSUPPORTED_ERROR, true); + assertQueryFails("select x like '%ab_c' from (values CAST('xa bc' AS CHAR(5)), CAST ('xabcy' AS CHAR(5)), CAST ('abcd' AS CHAR(4))) T(x)", CHAR_TYPE_UNSUPPORTED_ERROR, true); + assertQueryFails("select x like '%' from (values CAST('xa bc' AS CHAR(5)), CAST ('xabcy' AS CHAR(5)), CAST ('abcd' AS CHAR(4))) T(x)", CHAR_TYPE_UNSUPPORTED_ERROR, true); + assertQueryFails("select x like '%_%' from (values CAST('xa bc' AS CHAR(5)), CAST ('xabcy' AS CHAR(5)), CAST ('abcd' AS CHAR(4))) T(x)", CHAR_TYPE_UNSUPPORTED_ERROR, true); + assertQueryFails("select x like '%a%' from (values CAST('xa bc' AS CHAR(5)), CAST ('xabcy' AS CHAR(5)), CAST ('abcd' AS CHAR(4))) T(x)", CHAR_TYPE_UNSUPPORTED_ERROR, true); + assertQueryFails("select x like '%acd%xy%' from (values CAST('xa bc' AS CHAR(5)), CAST ('xabcy' AS CHAR(5)), CAST ('abcd' AS CHAR(4))) T(x)", CHAR_TYPE_UNSUPPORTED_ERROR, true); + } + + @Override + public void testMapBlockBug() + { + assertQueryFails(" VALUES(MAP_AGG(12345,123))", "Scalar function name not registered: presto.default.map_agg", true); + } + + @Override + public void testMergeKHyperLogLog() + { + assertQueryFails("select k1, cardinality(merge(khll)), uniqueness_distribution(merge(khll)) from (select k1, k2, khyperloglog_agg(v1, v2) khll from (values (1, 1, 2, 3), (1, 1, 4, 0), (1, 2, 90, 20), (1, 2, 87, 1), " + + "(2, 1, 11, 30), (2, 1, 11, 11), (2, 2, 9, 1), (2, 2, 87, 2)) t(k1, k2, v1, v2) group by k1, k2) group by k1", KHLL_TIMESTAMP_TZ_TYPE_UNSUPPORTED_ERROR, true); + + assertQueryFails("select cardinality(merge(khll)), uniqueness_distribution(merge(khll)) from (select k1, k2, khyperloglog_agg(v1, v2) khll from (values (1, 1, 2, 3), (1, 1, 4, 0), (1, 2, 90, 20), (1, 2, 87, 1), " + + "(2, 1, 11, 30), (2, 1, 11, 11), (2, 2, 9, 1), (2, 2, 87, 2)) t(k1, k2, v1, v2) group by k1, k2)", KHLL_TIMESTAMP_TZ_TYPE_UNSUPPORTED_ERROR, true); + } + + @Override + @Test + public void testRemoveMapCastFailure() + { + Session enableOptimization = Session.builder(getSession()) + .setSystemProperty(REMOVE_MAP_CAST, "true") + .build(); + assertQueryFails(enableOptimization, "select feature[key] from (values (map(array[cast(1 as integer), 2, 3, 4], array[0.3, 0.5, 0.9, 0.1]), cast(2 as bigint)), (map(array[cast(1 as integer), 2, 3, 4], array[0.3, 0.5, 0.9, 0.1]), 400000000000)) t(feature, key)", + "Cannot cast BIGINT.*to INTEGER. Overflow during arithmetic conversion", true); + } + + @Override + @Test + public void testRemoveRedundantCastToVarcharInJoinClause() + { + Session session = Session.builder(getSession()) + .setSystemProperty(REMOVE_REDUNDANT_CAST_TO_VARCHAR_IN_JOIN, "true") + .build(); + // TODO: Check why results do not match for disabled queries. + // Trigger optimization + computeActual("select * from orders o join customer c on cast(o.custkey as varchar) = cast(c.custkey as varchar)"); + assertQuery(session, "select o.orderkey, c.name from orders o join customer c on cast(o.custkey as varchar) = cast(c.custkey as varchar)"); + computeActual(session, "select *, cast(o.custkey as varchar), cast(c.custkey as varchar) from orders o join customer c on cast(o.custkey as varchar) = cast(c.custkey as varchar)"); + assertQuery(session, "select r.custkey, r.orderkey, r.name, n.nationkey from (select o.custkey, o.orderkey, c.name from orders o join customer c on cast(o.custkey as varchar) = cast(c.custkey as varchar)) r, nation n"); + // Do not trigger optimization + assertQuery(session, "select * from customer c join orders o on cast(acctbal as varchar) = cast(totalprice as varchar)"); + } + + @Override + @Test + public void testSameAggregationWithAndWithoutFilter() + { + Session enableOptimization = Session.builder(getSession()) + .setSystemProperty(MERGE_AGGREGATIONS_WITH_AND_WITHOUT_FILTER, "true") + .build(); + Session disableOptimization = Session.builder(getSession()) + .setSystemProperty(MERGE_AGGREGATIONS_WITH_AND_WITHOUT_FILTER, "false") + .build(); + + // TODO: Check why results do not match with MERGE_AGGREGATIONS_WITH_AND_WITHOUT_FILTER enabled. + // Disabled tests to be enabled after fixing this issue. + computeActual(getQueryRunner(), enableOptimization, "select regionkey, count(name) filter (where name like '%N%') n_nations, count(name) all_nations from nation group by regionkey"); + assertQuery(enableOptimization, "select count(name) filter (where name like '%N%') n_nations, count(name) all_nations from nation", "values (15,25)"); + assertQuery(enableOptimization, "select count(1), count(1) filter (where k > 5) from (values 1, null, 3, 5, null, 8, 10) t(k)", "values (7, 2)"); + + String sql = "select regionkey, count(name) filter (where name like '%N%') n_nations, count(name) all_nations from nation group by regionkey"; + // MaterializedResult resultWithOptimization = computeActual(enableOptimization, sql); + // MaterializedResult resultWithoutOptimization = computeActual(disableOptimization, sql); + // assertEqualsIgnoreOrder(resultWithOptimization, resultWithoutOptimization); + + sql = "select count(name) filter (where name like '%N%') n_nations, count(name) all_nations from nation"; + // resultWithOptimization = computeActual(enableOptimization, sql); + // resultWithoutOptimization = computeActual(disableOptimization, sql); + // assertEqualsIgnoreOrder(resultWithOptimization, resultWithoutOptimization); + + sql = "select partkey, sum(quantity), sum(quantity) filter (where discount > 0.1) from lineitem group by grouping sets((), (partkey))"; + MaterializedResult resultWithOptimization = computeActual(enableOptimization, sql); + MaterializedResult resultWithoutOptimization = computeActual(disableOptimization, sql); + assertEqualsIgnoreOrder(resultWithOptimization, resultWithoutOptimization); + + // multiple aggregations in query + sql = "select partkey, sum(quantity), sum(quantity) filter (where discount < 0.05), sum(linenumber), sum(linenumber) filter (where discount < 0.05) from lineitem group by partkey"; + resultWithOptimization = computeActual(enableOptimization, sql); + resultWithoutOptimization = computeActual(disableOptimization, sql); + assertEqualsIgnoreOrder(resultWithOptimization, resultWithoutOptimization); + // aggregations in multiple levels + sql = "select partkey, avg(sum), avg(sum) filter (where tax < 0.05), avg(filtersum) from (select partkey, suppkey, sum(quantity) sum, sum(quantity) filter (where discount > 0.05) filtersum, max(tax) tax from lineitem where partkey=1598 group by partkey, suppkey) t group by partkey"; + resultWithOptimization = computeActual(enableOptimization, sql); + resultWithoutOptimization = computeActual(disableOptimization, sql); + assertEqualsIgnoreOrder(resultWithOptimization, resultWithoutOptimization); + // global aggregation + sql = "select sum(quantity), sum(quantity) filter (where discount < 0.05) from lineitem"; + resultWithOptimization = computeActual(enableOptimization, sql); + resultWithoutOptimization = computeActual(disableOptimization, sql); + assertEqualsIgnoreOrder(resultWithOptimization, resultWithoutOptimization); + // order by + sql = "select partkey, array_agg(suppkey order by suppkey), array_agg(suppkey order by suppkey) filter (where discount > 0.05) from lineitem group by partkey"; + resultWithOptimization = computeActual(enableOptimization, sql); + resultWithoutOptimization = computeActual(disableOptimization, sql); + assertEqualsIgnoreOrder(resultWithOptimization, resultWithoutOptimization); + // grouping sets + sql = "SELECT partkey, suppkey, sum(quantity), sum(quantity) filter (where discount > 0.05) from lineitem group by grouping sets((), (partkey), (partkey, suppkey))"; + resultWithOptimization = computeActual(enableOptimization, sql); + resultWithoutOptimization = computeActual(disableOptimization, sql); + assertEqualsIgnoreOrder(resultWithOptimization, resultWithoutOptimization); + // aggregation over union + sql = "SELECT partkey, sum(quantity), sum(quantity) filter (where orderkey > 0) from (select quantity, orderkey, partkey from lineitem union all select totalprice as quantity, orderkey, custkey as partkey from orders) group by partkey"; + resultWithOptimization = computeActual(enableOptimization, sql); + resultWithoutOptimization = computeActual(disableOptimization, sql); + assertEqualsIgnoreOrder(resultWithOptimization, resultWithoutOptimization); + // aggregation over join + sql = "select custkey, sum(quantity), sum(quantity) filter (where tax < 0.05) from lineitem l join orders o on l.orderkey=o.orderkey group by custkey"; + resultWithOptimization = computeActual(enableOptimization, sql); + resultWithoutOptimization = computeActual(disableOptimization, sql); + assertEqualsIgnoreOrder(resultWithOptimization, resultWithoutOptimization); + } + + @Override + @Test + public void testSetAggIndeterminateArrays() + { + // union all is to force usage of the serialized state + assertQueryFails("SELECT unnested from (SELECT set_agg(x) as agg_result from (" + + "SELECT ARRAY[ARRAY[null, 2]] x " + + "UNION ALL " + + "SELECT ARRAY[null, ARRAY[1, null]] " + + "UNION ALL " + + "SELECT ARRAY[ARRAY[null, 2]])) " + + "CROSS JOIN unnest(agg_result) as r(unnested)", + ARRAY_COMPARISON_UNSUPPORTED_ERROR, true); + } + + @Override + @Test + public void testSetAggIndeterminateRows() + { + // union all is to force usage of the serialized state + assertQueryFails("SELECT unnested from (SELECT set_agg(x) as agg_result from (" + + "SELECT ARRAY[CAST(row(null, 2) AS ROW(INTEGER, INTEGER))] x " + + "UNION ALL " + + "SELECT ARRAY[null, CAST(row(1, null) AS ROW(INTEGER, INTEGER))] " + + "UNION ALL " + + "SELECT ARRAY[CAST(row(null, 2) AS ROW(INTEGER, INTEGER))])) " + + "CROSS JOIN unnest(agg_result) as r(unnested)", + ARRAY_COMPARISON_UNSUPPORTED_ERROR, true); + } + + @Override + @Test + public void testSetUnionIndeterminateRows() + { + // union all is to force usage of the serialized state + assertQueryFails("SELECT c1, c2 from (SELECT set_union(x) as agg_result from (" + + "SELECT ARRAY[CAST(row(null, 2) AS ROW(INTEGER, INTEGER))] x " + + "UNION ALL " + + "SELECT ARRAY[null, CAST(row(1, null) AS ROW(INTEGER, INTEGER))] " + + "UNION ALL " + + "SELECT ARRAY[CAST(row(null, 2) AS ROW(INTEGER, INTEGER))])) " + + "CROSS JOIN unnest(agg_result) as r(c1, c2)", + ".*Field not found.*", true); + } + + @Override + @Test + public void testLambdaInAggregation() + { + assertQuery("SELECT id, reduce_agg(value, 0, (a, b) -> a + b+0, (a, b) -> a + b) FROM ( VALUES (1, 2), (1, 3), (1, 4), (2, 20), (2, 30), (2, 40) ) AS t(id, value) GROUP BY id", "values (1, 9), (2, 90)"); + assertQuery("SELECT id, 's' || reduce_agg(value, '', (a, b) -> concat(a, b, 's'), (a, b) -> concat(a, b, 's')) FROM ( VALUES (1, '2'), (1, '3'), (1, '4'), (2, '20'), (2, '30'), (2, '40') ) AS t(id, value) GROUP BY id", + "values (1, 's2s3ss4ss'), (2, 's20s30ss40ss')"); + assertQueryFails("SELECT id, reduce_agg(value, array[id, value], (a, b) -> a || b, (a, b) -> a || b) FROM ( VALUES (1, 2), (1, 3), (1, 4), (2, 20), (2, 30), (2, 40) ) AS t(id, value) GROUP BY id", + ".*REDUCE_AGG only supports non-NULL literal as the initial value.*"); + } + + @Override + @Test + public void testPreserveAssignmentsInJoin() + { + // The following two timestamps represent the same point in time but with different time zones + String timestampLosAngeles = "2001-08-22 03:04:05.321 America/Los_Angeles"; + String timestampNewYork = "2001-08-22 06:04:05.321 America/New_York"; + Set> rows = computeActual("WITH source AS (" + + "SELECT * FROM (" + + " VALUES" + + " (TIMESTAMP '" + timestampLosAngeles + "')," + + " (TIMESTAMP '" + timestampNewYork + "')" + + ") AS tbl (tstz)" + + ")" + + "SELECT * FROM source a JOIN source b ON a.tstz = b.tstz").getMaterializedRows().stream() + .map(MaterializedRow::getFields) + .collect(toImmutableSet()); + // TODO: Enable assertion after fixing result mismatch + /* Assert.assertEquals(rows, + ImmutableSet.of( + ImmutableList.of(zonedDateTime(timestampLosAngeles), zonedDateTime(timestampLosAngeles)), + ImmutableList.of(zonedDateTime(timestampLosAngeles), zonedDateTime(timestampNewYork)), + ImmutableList.of(zonedDateTime(timestampNewYork), zonedDateTime(timestampLosAngeles)), + ImmutableList.of(zonedDateTime(timestampNewYork), zonedDateTime(timestampNewYork)))); */ + } +} diff --git a/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestAggregations.java b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestAggregations.java new file mode 100644 index 000000000000..53ee6e496354 --- /dev/null +++ b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestAggregations.java @@ -0,0 +1,43 @@ +/* + * 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; + +import com.facebook.presto.nativeworker.NativeQueryRunnerUtils; +import com.facebook.presto.nativeworker.PrestoNativeQueryRunnerUtils; +import com.facebook.presto.testing.QueryRunner; + +public class TestAggregations + extends AbstractTestAggregationsNative +{ + private static final String storageFormat = "PARQUET"; + + @Override + protected QueryRunner createQueryRunner() throws Exception + { + return PrestoNativeQueryRunnerUtils.createNativeQueryRunner(true, storageFormat); + } + + @Override + protected void createTables() + { + try { + QueryRunner javaQueryRunner = PrestoNativeQueryRunnerUtils.createJavaQueryRunner(storageFormat); + NativeQueryRunnerUtils.createAllTables(javaQueryRunner, false); + javaQueryRunner.close(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestDistributedEngineOnlyQueries.java b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestDistributedEngineOnlyQueries.java new file mode 100644 index 000000000000..fccfeaad5c39 --- /dev/null +++ b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestDistributedEngineOnlyQueries.java @@ -0,0 +1,122 @@ +/* + * 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; + +import com.facebook.presto.Session; +import com.facebook.presto.common.type.TimeZoneKey; +import com.facebook.presto.nativeworker.NativeQueryRunnerUtils; +import com.facebook.presto.nativeworker.PrestoNativeQueryRunnerUtils; +import com.facebook.presto.testing.QueryRunner; +import com.facebook.presto.tests.AbstractTestEngineOnlyQueries; +import org.intellij.lang.annotations.Language; +import org.testng.annotations.Test; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Objects; + +import static com.google.common.base.Preconditions.checkState; +import static org.testng.Assert.assertEquals; + +public class TestDistributedEngineOnlyQueries + extends AbstractTestEngineOnlyQueries +{ + private static final String timeTypeUnsupportedError = ".*Failed to parse type \\[time.*"; + + private static final String storageFormat = "PARQUET"; + + @Override + protected QueryRunner createQueryRunner() throws Exception + { + return PrestoNativeQueryRunnerUtils.createNativeQueryRunner(true, storageFormat); + } + + @Override + protected void createTables() + { + try { + QueryRunner javaQueryRunner = PrestoNativeQueryRunnerUtils.createJavaQueryRunner(storageFormat); + NativeQueryRunnerUtils.createAllTables(javaQueryRunner, false); + javaQueryRunner.close(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + @Test + public void testTimeLiterals() + { + Session chicago = Session.builder(getSession()).setTimeZoneKey(TimeZoneKey.getTimeZoneKey("America/Chicago")).build(); + Session kathmandu = Session.builder(getSession()).setTimeZoneKey(TimeZoneKey.getTimeZoneKey("Asia/Kathmandu")).build(); + + assertEquals(computeScalar("SELECT DATE '2013-03-22'"), LocalDate.of(2013, 3, 22)); + assertQuery("SELECT DATE '2013-03-22'"); + assertQuery(chicago, "SELECT DATE '2013-03-22'"); + assertQuery(kathmandu, "SELECT DATE '2013-03-22'"); + + assertQueryFails("SELECT TIME '3:04:05'", timeTypeUnsupportedError, true); + assertQueryFails("SELECT TIME '3:04:05.123'", timeTypeUnsupportedError, true); + assertQueryFails("SELECT TIME '3:04:05'", timeTypeUnsupportedError, true); + assertQueryFails("SELECT TIME '0:04:05'", timeTypeUnsupportedError, true); + assertQueryFails(chicago, "SELECT TIME '3:04:05'", timeTypeUnsupportedError, true); + assertQueryFails(kathmandu, "SELECT TIME '3:04:05'", timeTypeUnsupportedError, true); + + assertQueryFails("SELECT TIME '01:02:03.400 Z'", timeTypeUnsupportedError, true); + assertQueryFails("SELECT TIME '01:02:03.400 UTC'", timeTypeUnsupportedError, true); + assertQueryFails("SELECT TIME '3:04:05 +06:00'", timeTypeUnsupportedError, true); + assertQueryFails("SELECT TIME '3:04:05 +0507'", timeTypeUnsupportedError, true); + assertQueryFails("SELECT TIME '3:04:05 +03'", timeTypeUnsupportedError, true); + + assertEquals(computeScalar("SELECT TIMESTAMP '1960-01-22 3:04:05'"), LocalDateTime.of(1960, 1, 22, 3, 4, 5)); + assertEquals(computeScalar("SELECT TIMESTAMP '1960-01-22 3:04:05.123'"), LocalDateTime.of(1960, 1, 22, 3, 4, 5, 123_000_000)); + assertQuery("SELECT TIMESTAMP '1960-01-22 3:04:05'"); + assertQuery("SELECT TIMESTAMP '1960-01-22 3:04:05.123'"); + // TODO #7122 assertQuery(chicago, "SELECT TIMESTAMP '1960-01-22 3:04:05.123'"); + // TODO #7122 assertQuery(kathmandu, "SELECT TIMESTAMP '1960-01-22 3:04:05.123'"); + + assertEquals(computeScalar("SELECT TIMESTAMP '1960-01-22 3:04:05 +06:00'"), ZonedDateTime.of(1960, 1, 22, 3, 4, 5, 0, ZoneOffset.ofHoursMinutes(6, 0))); + } + + @Override + @Test + public void testLocallyUnrepresentableTimeLiterals() + { + LocalDateTime localTimeThatDidNotExist = LocalDateTime.of(2017, 4, 2, 2, 10); + checkState(ZoneId.systemDefault().getRules().getValidOffsets(localTimeThatDidNotExist).isEmpty(), "This test assumes certain JVM time zone"); + // This tests that both Presto runner and H2 can return TIMESTAMP value that never happened in JVM's zone (e.g. is not representable using java.sql.Timestamp) + @Language("SQL") String sql = DateTimeFormatter.ofPattern("'SELECT TIMESTAMP '''uuuu-MM-dd HH:mm:ss''").format(localTimeThatDidNotExist); + assertEquals(computeScalar(sql), localTimeThatDidNotExist); // this tests Presto and the QueryRunner + assertQuery(sql); // this tests H2QueryRunner + + LocalDate localDateThatDidNotHaveMidnight = LocalDate.of(1970, 1, 1); + checkState(ZoneId.systemDefault().getRules().getValidOffsets(localDateThatDidNotHaveMidnight.atStartOfDay()).isEmpty(), "This test assumes certain JVM time zone"); + // This tests that both Presto runner and H2 can return DATE value for a day which midnight never happened in JVM's zone (e.g. is not exactly representable using java.sql.Date) + sql = DateTimeFormatter.ofPattern("'SELECT DATE '''uuuu-MM-dd''").format(localDateThatDidNotHaveMidnight); + assertEquals(computeScalar(sql), localDateThatDidNotHaveMidnight); // this tests Presto and the QueryRunner + assertQuery(sql); // this tests H2QueryRunner + + LocalTime localTimeThatDidNotOccurOn19700101 = LocalTime.of(0, 10); + checkState(ZoneId.systemDefault().getRules().getValidOffsets(localTimeThatDidNotOccurOn19700101.atDate(LocalDate.ofEpochDay(0))).isEmpty(), "This test assumes certain JVM time zone"); + checkState(!Objects.equals(java.sql.Time.valueOf(localTimeThatDidNotOccurOn19700101).toLocalTime(), localTimeThatDidNotOccurOn19700101), "This test assumes certain JVM time zone"); + sql = DateTimeFormatter.ofPattern("'SELECT TIME '''HH:mm:ss''").format(localTimeThatDidNotOccurOn19700101); + assertQueryFails(sql, timeTypeUnsupportedError, true); + } +} diff --git a/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestDistributedQueriesNoHashGeneration.java b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestDistributedQueriesNoHashGeneration.java new file mode 100644 index 000000000000..ee57b560ed60 --- /dev/null +++ b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestDistributedQueriesNoHashGeneration.java @@ -0,0 +1,47 @@ +/* + * 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; + +import com.facebook.presto.nativeworker.NativeQueryRunnerUtils; +import com.facebook.presto.nativeworker.PrestoNativeQueryRunnerUtils; +import com.facebook.presto.testing.QueryRunner; +import com.google.common.collect.ImmutableMap; + +public class TestDistributedQueriesNoHashGeneration + extends AbstractTestQueriesNative +{ + private static final String storageFormat = "PARQUET"; + + @Override + protected QueryRunner createQueryRunner() + throws Exception + { + return PrestoNativeQueryRunnerUtils.createNativeQueryRunner( + ImmutableMap.of(), + ImmutableMap.of("optimizer.optimize-hash-generation", "false"), storageFormat); + } + + @Override + protected void createTables() + { + try { + QueryRunner javaQueryRunner = PrestoNativeQueryRunnerUtils.createJavaQueryRunner(storageFormat); + NativeQueryRunnerUtils.createAllTables(javaQueryRunner, false); + javaQueryRunner.close(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestNonIterativeDistributedQueries.java b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestNonIterativeDistributedQueries.java new file mode 100644 index 000000000000..214914e2628e --- /dev/null +++ b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestNonIterativeDistributedQueries.java @@ -0,0 +1,49 @@ +/* + * 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; + +import com.facebook.presto.nativeworker.NativeQueryRunnerUtils; +import com.facebook.presto.nativeworker.PrestoNativeQueryRunnerUtils; +import com.facebook.presto.testing.QueryRunner; +import com.google.common.collect.ImmutableMap; + +/** + * Test that Presto works with {@link com.facebook.presto.sql.planner.iterative.IterativeOptimizer} disabled. + */ +public class TestNonIterativeDistributedQueries + extends AbstractTestQueriesNative +{ + private static final String storageFormat = "PARQUET"; + + @Override + protected QueryRunner createQueryRunner() throws Exception + { + return PrestoNativeQueryRunnerUtils.createNativeQueryRunner( + ImmutableMap.of("experimental.iterative-optimizer-enabled", "false"), + ImmutableMap.of(), storageFormat); + } + + @Override + protected void createTables() + { + try { + QueryRunner javaQueryRunner = PrestoNativeQueryRunnerUtils.createJavaQueryRunner(storageFormat); + NativeQueryRunnerUtils.createAllTables(javaQueryRunner, false); + javaQueryRunner.close(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestOptimizeMixedDistinctAggregations.java b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestOptimizeMixedDistinctAggregations.java new file mode 100644 index 000000000000..8ee9f35f9f60 --- /dev/null +++ b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestOptimizeMixedDistinctAggregations.java @@ -0,0 +1,55 @@ + +/* + * 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; + +import com.facebook.presto.nativeworker.NativeQueryRunnerUtils; +import com.facebook.presto.nativeworker.PrestoNativeQueryRunnerUtils; +import com.facebook.presto.testing.QueryRunner; +import com.google.common.collect.ImmutableMap; + +public class TestOptimizeMixedDistinctAggregations + extends AbstractTestAggregationsNative +{ + private static final String storageFormat = "PARQUET"; + + @Override + protected QueryRunner createQueryRunner() + throws Exception + { + return PrestoNativeQueryRunnerUtils.createNativeQueryRunner( + ImmutableMap.of(), + ImmutableMap.of("optimizer.optimize-mixed-distinct-aggregations", "true"), storageFormat); + } + + @Override + protected void createTables() + { + try { + QueryRunner javaQueryRunner = PrestoNativeQueryRunnerUtils.createJavaQueryRunner(storageFormat); + NativeQueryRunnerUtils.createAllTables(javaQueryRunner, false); + javaQueryRunner.close(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void testCountDistinct() + { + assertQuery("SELECT COUNT(DISTINCT custkey + 1) FROM orders", "SELECT COUNT(*) FROM (SELECT DISTINCT custkey + 1 FROM orders) t"); + assertQuery("SELECT COUNT(DISTINCT linenumber), COUNT(*) from lineitem where linenumber < 0"); + } +} diff --git a/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestOrderByQueries.java b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestOrderByQueries.java new file mode 100644 index 000000000000..836f0816498d --- /dev/null +++ b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestOrderByQueries.java @@ -0,0 +1,54 @@ +/* + * 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; + +import com.facebook.presto.nativeworker.NativeQueryRunnerUtils; +import com.facebook.presto.nativeworker.PrestoNativeQueryRunnerUtils; +import com.facebook.presto.testing.QueryRunner; +import com.facebook.presto.tests.AbstractTestOrderByQueries; +import org.testng.annotations.Test; + +public class TestOrderByQueries + extends AbstractTestOrderByQueries +{ + private static final String storageFormat = "PARQUET"; + + @Override + protected QueryRunner createQueryRunner() throws Exception + { + return PrestoNativeQueryRunnerUtils.createNativeQueryRunner(true, storageFormat); + } + + @Override + protected void createTables() + { + try { + QueryRunner javaQueryRunner = PrestoNativeQueryRunnerUtils.createJavaQueryRunner(storageFormat); + NativeQueryRunnerUtils.createAllTables(javaQueryRunner, false); + javaQueryRunner.close(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + @Test + public void testOrderByWithOutputColumnReferenceInLambdas() + { + assertQueryFails("SELECT x AS y FROM (values (1,2), (2,3)) t(x, y) GROUP BY x ORDER BY apply(x, x -> -x) + 2*x", ".*Scalar function name not registered: presto.default.apply.*"); + assertQueryFails("SELECT -y AS x FROM (values (1,2), (2,3)) t(x, y) GROUP BY y ORDER BY apply(x, x -> -x)", ".*Scalar function name not registered: presto.default.apply.*"); + assertQueryFails("SELECT -y AS x FROM (values (1,2), (2,3)) t(x, y) GROUP BY y ORDER BY sum(apply(-y, x -> x * 1.0))", ".*Scalar function name not registered: presto.default.apply.*"); + } +} diff --git a/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestRepartitionQueries.java b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestRepartitionQueries.java new file mode 100644 index 000000000000..fa5d2738d3cc --- /dev/null +++ b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestRepartitionQueries.java @@ -0,0 +1,82 @@ +/* + * 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; + +import com.facebook.presto.nativeworker.NativeQueryRunnerUtils; +import com.facebook.presto.nativeworker.PrestoNativeQueryRunnerUtils; +import com.facebook.presto.testing.QueryRunner; +import com.facebook.presto.tests.AbstractTestRepartitionQueries; +import org.testng.annotations.Test; + +public class TestRepartitionQueries + extends AbstractTestRepartitionQueries +{ + private static final String storageFormat = "PARQUET"; + + @Override + protected QueryRunner createQueryRunner() throws Exception + { + return PrestoNativeQueryRunnerUtils.createNativeQueryRunner(true, storageFormat); + } + + @Override + protected void createTables() + { + try { + QueryRunner javaQueryRunner = PrestoNativeQueryRunnerUtils.createJavaQueryRunner(storageFormat); + NativeQueryRunnerUtils.createAllTables(javaQueryRunner, false); + javaQueryRunner.close(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + @Test + public void testIpAddress() + { + // TODO: Enable this query after result mismatch is fixed + computeActual("WITH lineitem_ex AS \n" + + "(\n" + + "SELECT\n" + + " partkey,\n" + + " CAST(\n" + + " CONCAT(\n" + + " CONCAT(\n" + + " CONCAT(\n" + + " CONCAT(\n" + + " CONCAT(\n" + + " CONCAT(CAST((orderkey % 255) AS VARCHAR), '.'),\n" + + " CAST((partkey % 255) AS VARCHAR)\n" + + " ),\n" + + " '.'\n" + + " ),\n" + + " CAST(suppkey AS VARCHAR)\n" + + " ),\n" + + " '.'\n" + + " ),\n" + + " CAST(linenumber AS VARCHAR)\n" + + " ) AS ipaddress\n" + + " ) AS ip\n" + + " FROM lineitem\n" + + " )\n" + + "SELECT\n" + + " CHECKSUM(l.ip) \n" + + "FROM lineitem_ex l,\n" + + " partsupp p\n" + + "WHERE\n" + + " l.partkey = p.partkey"); + } +} diff --git a/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestRepartitionQueriesWithSmallPages.java b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestRepartitionQueriesWithSmallPages.java new file mode 100644 index 000000000000..a7ff3540655d --- /dev/null +++ b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestRepartitionQueriesWithSmallPages.java @@ -0,0 +1,86 @@ +/* + * 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; + +import com.facebook.presto.nativeworker.NativeQueryRunnerUtils; +import com.facebook.presto.nativeworker.PrestoNativeQueryRunnerUtils; +import com.facebook.presto.testing.QueryRunner; +import com.facebook.presto.tests.AbstractTestRepartitionQueries; +import com.google.common.collect.ImmutableMap; +import org.testng.annotations.Test; + +public class TestRepartitionQueriesWithSmallPages + extends AbstractTestRepartitionQueries +{ + private static final String storageFormat = "PARQUET"; + + @Override + protected QueryRunner createQueryRunner() throws Exception + { + return PrestoNativeQueryRunnerUtils.createNativeQueryRunner( + ImmutableMap.of("experimental.optimized-repartitioning", "true", + // Use small SerializedPages to force flushing + "driver.max-page-partitioning-buffer-size", "200B"), ImmutableMap.of(), storageFormat); + } + + @Override + protected void createTables() + { + try { + QueryRunner javaQueryRunner = PrestoNativeQueryRunnerUtils.createJavaQueryRunner(storageFormat); + NativeQueryRunnerUtils.createAllTables(javaQueryRunner, false); + javaQueryRunner.close(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + @Test + public void testIpAddress() + { + // TODO: Enable this query after result mismatch is fixed + computeActual("WITH lineitem_ex AS \n" + + "(\n" + + "SELECT\n" + + " partkey,\n" + + " CAST(\n" + + " CONCAT(\n" + + " CONCAT(\n" + + " CONCAT(\n" + + " CONCAT(\n" + + " CONCAT(\n" + + " CONCAT(CAST((orderkey % 255) AS VARCHAR), '.'),\n" + + " CAST((partkey % 255) AS VARCHAR)\n" + + " ),\n" + + " '.'\n" + + " ),\n" + + " CAST(suppkey AS VARCHAR)\n" + + " ),\n" + + " '.'\n" + + " ),\n" + + " CAST(linenumber AS VARCHAR)\n" + + " ) AS ipaddress\n" + + " ) AS ip\n" + + " FROM lineitem\n" + + " )\n" + + "SELECT\n" + + " CHECKSUM(l.ip) \n" + + "FROM lineitem_ex l,\n" + + " partsupp p\n" + + "WHERE\n" + + " l.partkey = p.partkey"); + } +} diff --git a/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestTpchDistributedQueries.java b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestTpchDistributedQueries.java new file mode 100644 index 000000000000..d6afb3f46c50 --- /dev/null +++ b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestTpchDistributedQueries.java @@ -0,0 +1,98 @@ +/* + * 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; + +import com.facebook.presto.nativeworker.NativeQueryRunnerUtils; +import com.facebook.presto.nativeworker.PrestoNativeQueryRunnerUtils; +import com.facebook.presto.testing.QueryRunner; +import com.google.common.base.Strings; +import org.intellij.lang.annotations.Language; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + +public class TestTpchDistributedQueries + extends AbstractTestQueriesNative +{ + private static final String storageFormat = "PARQUET"; + + @Override + protected QueryRunner createQueryRunner() throws Exception + { + return PrestoNativeQueryRunnerUtils.createNativeQueryRunner(true, storageFormat); + } + + @Override + protected void createTables() + { + try { + QueryRunner javaQueryRunner = PrestoNativeQueryRunnerUtils.createJavaQueryRunner(storageFormat); + NativeQueryRunnerUtils.createAllTables(javaQueryRunner, false); + javaQueryRunner.close(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + public void testTooLongQuery() + { + // Generate a super-long query: SELECT x,x,x,x,x,... FROM (VALUES 1,2,3,4,5) t(x) + @Language("SQL") String longQuery = "SELECT x" + Strings.repeat(",x", 500_000) + " FROM (VALUES 1,2,3,4,5) t(x)"; + assertQueryFails(longQuery, "Query text length \\(1000037\\) exceeds the maximum length \\(1000000\\)"); + } + + @Test + public void testAnalyzePropertiesSystemTable() + { + assertQuery("SELECT COUNT(*) FROM system.metadata.analyze_properties WHERE catalog_name = 'tpch'", "SELECT 0"); + } + + @Test + public void testAnalyze() + { + assertUpdate("ANALYZE orders", 15000); + assertQueryFails("ANALYZE orders WITH (foo = 'bar')", ".* does not support analyze property 'foo'.*"); + } + + @Test + public void testTooManyStages() + { + @Language("SQL") String query = "WITH\n" + + " t1 AS (SELECT nationkey AS x FROM nation where name='UNITED STATES'),\n" + + " t2 AS (SELECT a.x+b.x+c.x+d.x AS x FROM t1 a, t1 b, t1 c, t1 d),\n" + + " t3 AS (SELECT a.x+b.x+c.x+d.x AS x FROM t2 a, t2 b, t2 c, t2 d),\n" + + " t4 AS (SELECT a.x+b.x+c.x+d.x AS x FROM t3 a, t3 b, t3 c, t3 d),\n" + + " t5 AS (SELECT a.x+b.x+c.x+d.x AS x FROM t4 a, t4 b, t4 c, t4 d)\n" + + "SELECT x FROM t5\n"; + assertQueryFails(query, "Number of stages in the query \\([0-9]+\\) exceeds the allowed maximum \\([0-9]+\\).*"); + } + + @Test + public void testTableSampleSystem() + { + int total = computeActual("SELECT orderkey FROM orders").getMaterializedRows().size(); + + boolean sampleSizeFound = false; + for (int i = 0; i < 100; i++) { + int sampleSize = computeActual("SELECT orderkey FROM ORDERS TABLESAMPLE SYSTEM (50)").getMaterializedRows().size(); + if (sampleSize > 0 && sampleSize < total) { + sampleSizeFound = true; + break; + } + } + assertTrue(sampleSizeFound, "Table sample returned unexpected number of rows"); + } +} diff --git a/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestWindowQueries.java b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestWindowQueries.java new file mode 100644 index 000000000000..0599b7cd2929 --- /dev/null +++ b/presto-native-tests/src/test/java/com.facebook.presto.nativetests/TestWindowQueries.java @@ -0,0 +1,654 @@ +/* + * 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; + +import com.facebook.presto.nativeworker.NativeQueryRunnerUtils; +import com.facebook.presto.nativeworker.PrestoNativeQueryRunnerUtils; +import com.facebook.presto.testing.QueryRunner; +import com.facebook.presto.tests.AbstractTestWindowQueries; +import org.testng.annotations.Test; + +public class TestWindowQueries + extends AbstractTestWindowQueries +{ + private String frameTypeDiffersError = ".*Window frame of type RANGE does not match types of the ORDER BY and frame column.*"; + private String unsupportedWindowTypeError = ".*Unsupported window type: 2.*"; + private String invalidOffsetError = ".*Window frame offset value must not be negative or null.*"; + private String functionNotRegisteredError = ".*Scalar function presto.default.plus not registered with arguments.*"; + + private String storageFormat = "PARQUET"; + + @Override + protected QueryRunner createQueryRunner() throws Exception + { + return PrestoNativeQueryRunnerUtils.createNativeQueryRunner(true, storageFormat); + } + + @Override + protected void createTables() + { + try { + QueryRunner javaQueryRunner = PrestoNativeQueryRunnerUtils.createJavaQueryRunner("PARQUET"); + NativeQueryRunnerUtils.createAllTables(javaQueryRunner, false); + javaQueryRunner.close(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + @Test + public void testAllPartitionSameValuesGroup() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND 2 FOLLOWING) " + + "FROM (VALUES 'a', 'a', 'a') T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING) " + + "FROM (VALUES 'a', 'a', 'a') T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) " + + "FROM (VALUES 'a', 'a', 'a') T(a)", + unsupportedWindowTypeError); + + // test frame bounds at partition bounds + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a GROUPS BETWEEN 10 PRECEDING AND 10 FOLLOWING) " + + "FROM (VALUES 'a', 'a', 'a') T(a)", + unsupportedWindowTypeError); + } + + @Override + @Test + public void testConstantOffset() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST GROUPS BETWEEN 1 PRECEDING AND 2 FOLLOWING) " + + "FROM (VALUES 3, 3, 3, 2, 2, 1, null, null) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST GROUPS CURRENT ROW) " + + "FROM (VALUES 3, 3, 3, 2, 2, 1, null, null) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST GROUPS BETWEEN 0 PRECEDING AND 0 FOLLOWING) " + + "FROM (VALUES 3, 3, 3, 2, 2, 1, null, null) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST GROUPS BETWEEN 1 FOLLOWING AND 2 FOLLOWING) " + + "FROM (VALUES 3, 3, 3, 2, 2, 1, null, null) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING) " + + "FROM (VALUES 3, 3, 3, 2, 2, 1, null, null) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST GROUPS BETWEEN 2 FOLLOWING AND 1 FOLLOWING) " + + "FROM (VALUES 3, 3, 3, 2, 2, 1, null, null) T(a)", + unsupportedWindowTypeError); + } + + @Override + @Test + public void testEmptyFrameRealBounds() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE BETWEEN 0.5 FOLLOWING AND 1.5 FOLLOWING) " + + "FROM (VALUES 1, 2, 4) T(a)", frameTypeDiffersError, true); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a NULLS FIRST RANGE BETWEEN 2 PRECEDING AND 1.5 PRECEDING) " + + "FROM (VALUES null, 1, 2) T(a)", + frameTypeDiffersError, true); + } + + @Override + @Test + public void testEmptyFrameGroup() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS LAST GROUPS BETWEEN 90 PRECEDING AND 100 PRECEDING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS LAST GROUPS BETWEEN 100 FOLLOWING AND 90 FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + unsupportedWindowTypeError); + } + + // TODO: This test is flaky so disabled for now. + @Override + @Test(enabled = false) + public void testInvalidOffset() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC RANGE x PRECEDING) " + + "FROM (VALUES (1, 0.1), (2, -0.2)) T(a, x)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC RANGE BETWEEN 1 PRECEDING AND x FOLLOWING) " + + "FROM (VALUES (1, 0.1), (2, -0.2)) T(a, x)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC RANGE x PRECEDING) " + + "FROM (VALUES (1, 0.1), (2, -0.2)) T(a, x)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC RANGE BETWEEN 1 PRECEDING AND x FOLLOWING) " + + "FROM (VALUES (1, 0.1), (2, -0.2)) T(a, x)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC RANGE x PRECEDING) " + + "FROM (VALUES (1, 0.1), (2, null)) T(a, x)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC RANGE BETWEEN 1 PRECEDING AND x FOLLOWING) " + + "FROM (VALUES (1, 0.1), (2, null)) T(a, x)", + frameTypeDiffersError); + + // fail if offset is invalid for null sort key + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC RANGE BETWEEN 1 PRECEDING AND x FOLLOWING) " + + "FROM (VALUES (1, 0.1), (null, null)) T(a, x)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC RANGE BETWEEN 1 PRECEDING AND x FOLLOWING) " + + "FROM (VALUES (1, 0.1), (null, -0.1)) T(a, x)", + invalidOffsetError); + + // test invalid offset of different types + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE x PRECEDING) " + + "FROM (VALUES (1, BIGINT '-1')) T(a, x)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE x PRECEDING) " + + "FROM (VALUES (1, INTEGER '-1')) T(a, x)", + invalidOffsetError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE x PRECEDING) " + + "FROM (VALUES (SMALLINT '1', SMALLINT '-1')) T(a, x)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE x PRECEDING) " + + "FROM (VALUES (TINYINT '1', TINYINT '-1')) T(a, x)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE x PRECEDING) " + + "FROM (VALUES (1, -1.1e0)) T(a, x)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE x PRECEDING) " + + "FROM (VALUES (1, REAL '-1.1')) T(a, x)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE x PRECEDING) " + + "FROM (VALUES (1, -1.0001)) T(a, x)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE x PRECEDING) " + + "FROM (VALUES (DATE '2001-01-31', INTERVAL '-1' YEAR)) T(a, x)", + functionNotRegisteredError, true); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE x PRECEDING) " + + "FROM (VALUES (DATE '2001-01-31', INTERVAL '-1' MONTH)) T(a, x)", + functionNotRegisteredError, true); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE x PRECEDING) " + + "FROM (VALUES (DATE '2001-01-31', INTERVAL '-1' DAY)) T(a, x)", + functionNotRegisteredError, true); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE x PRECEDING) " + + "FROM (VALUES (DATE '2001-01-31', INTERVAL '-1' HOUR)) T(a, x)", + functionNotRegisteredError, true); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE x PRECEDING) " + + "FROM (VALUES (DATE '2001-01-31', INTERVAL '-1' MINUTE)) T(a, x)", + functionNotRegisteredError, true); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE x PRECEDING) " + + "FROM (VALUES (DATE '2001-01-31', INTERVAL '-1' SECOND)) T(a, x)", + functionNotRegisteredError, true); + } + + @Override + @Test + public void testInvalidOffsetGroup() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC GROUPS x PRECEDING) " + + "FROM (VALUES (1, 1), (2, -2)) T(a, x)", + unsupportedWindowTypeError); + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC GROUPS x PRECEDING) " + + "FROM (VALUES (1, 1), (2, -2)) T(a, x)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC GROUPS BETWEEN 1 PRECEDING AND x FOLLOWING) " + + "FROM (VALUES (1, 1), (2, -2)) T(a, x)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC GROUPS x PRECEDING) " + + "FROM (VALUES (1, 1), (2, -2)) T(a, x)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC GROUPS BETWEEN 1 PRECEDING AND x FOLLOWING) " + + "FROM (VALUES (1, 1), (2, -2)) T(a, x)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC GROUPS x PRECEDING) " + + "FROM (VALUES (1, 1), (2, null)) T(a, x)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC GROUPS BETWEEN 1 PRECEDING AND x FOLLOWING) " + + "FROM (VALUES (1, 1), (2, null)) T(a, x)", + unsupportedWindowTypeError); + + // fail if offset is invalid for null sort key + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC GROUPS BETWEEN 1 PRECEDING AND x FOLLOWING) " + + "FROM (VALUES (1, 1), (null, null)) T(a, x)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC GROUPS BETWEEN 1 PRECEDING AND x FOLLOWING) " + + "FROM (VALUES (1, 1), (null, -1)) T(a, x)", + unsupportedWindowTypeError); + + // test invalid offset of different types + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a GROUPS x PRECEDING) " + + "FROM (VALUES (1, BIGINT '-1')) T(a, x)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a GROUPS x PRECEDING) " + + "FROM (VALUES (1, INTEGER '-1')) T(a, x)", + unsupportedWindowTypeError); + } + + @Override + @Test + public void testMixedTypeFrameBounds() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS LAST GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS LAST GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS LAST GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS LAST GROUPS BETWEEN 1 PRECEDING AND CURRENT ROW) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS LAST GROUPS BETWEEN 1 PRECEDING AND UNBOUNDED FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS LAST GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + unsupportedWindowTypeError); + } + + @Override + @Test + public void testMixedTypeFrameBoundsAscendingNullsFirst() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST RANGE BETWEEN UNBOUNDED PRECEDING AND 0.5 PRECEDING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST RANGE BETWEEN UNBOUNDED PRECEDING AND 1.5 FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST RANGE BETWEEN CURRENT ROW AND 1.5 FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST RANGE BETWEEN 1.5 PRECEDING AND CURRENT ROW) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST RANGE BETWEEN 0.5 PRECEDING AND UNBOUNDED FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST RANGE BETWEEN 0.5 FOLLOWING AND UNBOUNDED FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + } + + @Override + @Test + public void testMixedTypeFrameBoundsAscendingNullsLast() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS LAST RANGE BETWEEN UNBOUNDED PRECEDING AND 0.5 PRECEDING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS LAST RANGE BETWEEN UNBOUNDED PRECEDING AND 1.5 FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS LAST RANGE BETWEEN CURRENT ROW AND 1.5 FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS LAST RANGE BETWEEN 1.5 PRECEDING AND CURRENT ROW) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS LAST RANGE BETWEEN 0.5 PRECEDING AND UNBOUNDED FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS LAST RANGE BETWEEN 0.5 FOLLOWING AND UNBOUNDED FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + } + + @Override + @Test + public void testMixedTypeFrameBoundsDescendingNullsFirst() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC NULLS FIRST RANGE BETWEEN UNBOUNDED PRECEDING AND 0.5 PRECEDING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC NULLS FIRST RANGE BETWEEN UNBOUNDED PRECEDING AND 0.5 FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC NULLS FIRST RANGE BETWEEN CURRENT ROW AND 1.5 FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC NULLS FIRST RANGE BETWEEN 1.5 PRECEDING AND CURRENT ROW) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC NULLS FIRST RANGE BETWEEN 1.5 PRECEDING AND UNBOUNDED FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC NULLS FIRST RANGE BETWEEN 1.5 FOLLOWING AND UNBOUNDED FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + } + + @Override + @Test + public void testMixedTypeFrameBoundsDescendingNullsLast() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC NULLS LAST RANGE BETWEEN UNBOUNDED PRECEDING AND 0.5 PRECEDING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC NULLS LAST RANGE BETWEEN UNBOUNDED PRECEDING AND 1.5 FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC NULLS LAST RANGE BETWEEN CURRENT ROW AND 1.5 FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC NULLS LAST RANGE BETWEEN 0.5 PRECEDING AND CURRENT ROW) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC NULLS LAST RANGE BETWEEN 0.5 PRECEDING AND UNBOUNDED FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a DESC NULLS LAST RANGE BETWEEN 1.5 FOLLOWING AND UNBOUNDED FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + frameTypeDiffersError); + } + + @Override + @Test + public void testMultipleWindowFunctionsGroup() + { + // two functions with frame type GROUPS + assertQueryFails("SELECT x, array_agg(date) OVER(ORDER BY x GROUPS BETWEEN 1 PRECEDING AND 1 PRECEDING), avg(number) OVER(ORDER BY x GROUPS BETWEEN 1 FOLLOWING AND 1 FOLLOWING) " + + "FROM (VALUES " + + "(2, DATE '2222-01-01', 4.4), " + + "(1, DATE '1111-01-01', 2.2), " + + "(3, DATE '3333-01-01', 6.6)) T(x, date, number)", + unsupportedWindowTypeError); + + // three functions with different frame types + assertQueryFails("SELECT " + + "x, " + + "array_agg(a) OVER(ORDER BY x RANGE BETWEEN 2 PRECEDING AND CURRENT ROW), " + + "array_agg(a) OVER(ORDER BY x GROUPS BETWEEN 1 FOLLOWING AND 2 FOLLOWING), " + + "array_agg(a) OVER(ORDER BY x ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) " + + "FROM (VALUES " + + "(1.0, 1), " + + "(2.0, 2), " + + "(3.0, 3), " + + "(4.0, 4), " + + "(5.0, 5), " + + "(6.0, 6)) T(x, a)", + unsupportedWindowTypeError); + } + + @Override + public void testNonConstantOffset() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE BETWEEN x * 10 PRECEDING AND y / 10.0 FOLLOWING) " + + "FROM (VALUES (1, 0.1, 10), (2, 0.2, 20), (4, 0.4, 40)) T(a, x, y)", + frameTypeDiffersError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE BETWEEN x * 10 PRECEDING AND y / 10.0 FOLLOWING) " + + "FROM (VALUES (1, 0.1, 10), (2, 0.2, 20), (4, 0.4, 40), (null, 0.5, 50)) T(a, x, y)", + frameTypeDiffersError); + } + + @Override + @Test + public void testNonConstantOffsetGroup() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a GROUPS BETWEEN x PRECEDING AND y FOLLOWING) " + + "FROM (VALUES ('a', 1, 1), ('b', 2, 0), ('c', 0, 3)) T(a, x, y)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a GROUPS BETWEEN x FOLLOWING AND y FOLLOWING) " + + "FROM (VALUES ('a', 1, 1), ('b', 2, 0), ('c', 3, 3), ('d', 0, 0)) T(a, x, y)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a GROUPS BETWEEN x PRECEDING AND y PRECEDING) " + + "FROM (VALUES ('a', 1, 1), ('b', 0, 2), ('c', 2, 1), ('d', 0, 2)) T(a, x, y)", + unsupportedWindowTypeError); + } + + @Override + @Test + public void testNoValueFrameBoundsGroup() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a ASC NULLS FIRST GROUPS BETWEEN CURRENT ROW AND CURRENT ROW) " + + "FROM (VALUES 1, null, null, 2, 1) T(a)", + unsupportedWindowTypeError); + } + + @Override + @Test + public void testOnlyNullsGroup() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a GROUPS BETWEEN 1 PRECEDING AND 2 FOLLOWING) " + + "FROM (VALUES CAST(null AS integer), null, null) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND 2 FOLLOWING) " + + "FROM (VALUES CAST(null AS integer), null, null) T(a)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING) " + + "FROM (VALUES CAST(null AS integer), null, null) T(a)", + unsupportedWindowTypeError); + } + + @Override + @Test + public void testWindowPartitioning() + { + assertQueryFails("SELECT a, p, array_agg(a) OVER(PARTITION BY p ORDER BY a ASC NULLS FIRST RANGE BETWEEN 0.5 PRECEDING AND 1 FOLLOWING) " + + "FROM (VALUES (1, 'x'), (2, 'x'), (null, 'x'), (null, 'y'), (2, 'y')) T(a, p)", + frameTypeDiffersError); + + assertQueryFails("SELECT a, p, array_agg(a) OVER(PARTITION BY p ORDER BY a ASC NULLS FIRST RANGE BETWEEN 0.5 PRECEDING AND 1 FOLLOWING) " + + "FROM (VALUES (1, 'x'), (2, 'x'), (null, 'x'), (null, 'y'), (2, 'y'), (null, null), (null, null), (1, null)) T(a, p)", + frameTypeDiffersError); + } + + @Override + @Test + public void testWindowPartitioningGroup() + { + assertQueryFails("SELECT a, p, array_agg(a) OVER(PARTITION BY p ORDER BY a ASC NULLS FIRST GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) " + + "FROM (VALUES (1, 'x'), (2, 'x'), (null, 'x'), (null, 'y'), (2, 'y')) T(a, p)", + unsupportedWindowTypeError); + + assertQueryFails("SELECT a, p, array_agg(a) OVER(PARTITION BY p ORDER BY a ASC NULLS FIRST GROUPS BETWEEN 0 PRECEDING AND 1 FOLLOWING) " + + "FROM (VALUES (1, 'x'), (2, 'x'), (null, 'x'), (null, 'y'), (2, 'y'), (null, null), (null, null), (1, null)) T(a, p)", + unsupportedWindowTypeError); + } + + @Override + @Test + public void testTypes() + { + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE BETWEEN DOUBLE '0.5' PRECEDING AND TINYINT '1' FOLLOWING) " + + "FROM (VALUES 1, null, 2) T(a)", + frameTypeDiffersError); + + assertQuery("SELECT array_agg(a) OVER(ORDER BY a RANGE BETWEEN 0.5 PRECEDING AND 1.000 FOLLOWING) " + + "FROM (VALUES REAL '1', null, 2) T(a)", + "VALUES " + + "ARRAY[CAST('1' AS REAL), CAST('2' AS REAL)], " + + "ARRAY[CAST('2' AS REAL)], " + + "ARRAY[null]"); + + assertQuery("SELECT x, array_agg(x) OVER(ORDER BY x DESC RANGE BETWEEN interval '1' month PRECEDING AND interval '1' month FOLLOWING) " + + "FROM (VALUES DATE '2001-01-31', DATE '2001-08-25', DATE '2001-09-25', DATE '2001-09-26') T(x)", + "VALUES " + + "(DATE '2001-09-26', ARRAY[DATE '2001-09-26', DATE '2001-09-25']), " + + "(DATE '2001-09-25', ARRAY[DATE '2001-09-26', DATE '2001-09-25', DATE '2001-08-25']), " + + "(DATE '2001-08-25', ARRAY[DATE '2001-09-25', DATE '2001-08-25']), " + + "(DATE '2001-01-31', ARRAY[DATE '2001-01-31'])"); + + // January 31 + 1 month sets the frame bound to the last day of February. March 1 is out of range. + assertQuery("SELECT x, array_agg(x) OVER(ORDER BY x RANGE BETWEEN CURRENT ROW AND interval '1' month FOLLOWING) " + + "FROM (VALUES DATE '2001-01-31', DATE '2001-02-28', DATE '2001-03-01') T(x)", + "VALUES " + + "(DATE '2001-01-31', ARRAY[DATE '2001-01-31', DATE '2001-02-28']), " + + "(DATE '2001-02-28', ARRAY[DATE '2001-02-28', DATE '2001-03-01']), " + + "(DATE '2001-03-01', ARRAY[DATE '2001-03-01'])"); + + // H2 and Presto has some type conversion problem for Interval type, hence use the same query runner for this query + assertQueryFails("SELECT x, array_agg(x) OVER(ORDER BY x RANGE BETWEEN interval '1' year PRECEDING AND interval '1' month FOLLOWING) " + + "FROM (VALUES " + + "INTERVAL '1' month, " + + "INTERVAL '2' month, " + + "INTERVAL '5' year) T(x)", + functionNotRegisteredError, true); + } + + @Override + @Test + public void testEmptyFrameIntegralBounds() + { + assertQuery("SELECT array_agg(a) OVER(ORDER BY a DESC NULLS LAST RANGE BETWEEN 1 PRECEDING AND 10 PRECEDING) " + + "FROM (VALUES 1, 2, 3, null, null, 2, 1, null, null) T(a)", + "VALUES " + + "CAST(null AS array), " + + "null, " + + "null, " + + "null, " + + "null, " + + "ARRAY[null, null, null, null], " + + "ARRAY[null, null, null, null], " + + "ARRAY[null, null, null, null], " + + "ARRAY[null, null, null, null]"); + + assertQuery("SELECT array_agg(a) OVER(ORDER BY a DESC NULLS LAST RANGE BETWEEN 10 FOLLOWING AND 1 FOLLOWING) " + + "FROM (VALUES 1, 2, 3, null, null, 2, 1, null, null) T(a)", + "VALUES " + + "CAST(null AS array), " + + "null, " + + "null, " + + "null, " + + "null, " + + "ARRAY[null, null, null, null], " + + "ARRAY[null, null, null, null], " + + "ARRAY[null, null, null, null], " + + "ARRAY[null, null, null, null]"); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE BETWEEN 1 FOLLOWING AND 2 FOLLOWING) " + + "FROM (VALUES 1.0, 1.1) T(a)", frameTypeDiffersError, true); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a NULLS LAST RANGE BETWEEN 1 FOLLOWING AND 2 FOLLOWING) " + + "FROM (VALUES 1.0, 1.1, null) T(a)", frameTypeDiffersError, true); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a RANGE BETWEEN 2 PRECEDING AND 1 PRECEDING) " + + "FROM (VALUES 1.0, 1.1) T(a)", frameTypeDiffersError, true); + + assertQueryFails("SELECT array_agg(a) OVER(ORDER BY a NULLS FIRST RANGE BETWEEN 2 PRECEDING AND 1 PRECEDING) " + + "FROM (VALUES null, 1.0, 1.1) T(a)", frameTypeDiffersError, true); + + assertQuery("SELECT array_agg(a) OVER(ORDER BY a RANGE BETWEEN 2 PRECEDING AND 1 PRECEDING) " + + "FROM (VALUES 1, 2) T(a)", + "VALUES " + + "null, " + + "ARRAY[1]"); + + assertQuery("SELECT array_agg(a) OVER(ORDER BY a NULLS FIRST RANGE BETWEEN 2 PRECEDING AND 1 PRECEDING) " + + "FROM (VALUES null, 1, 2) T(a)", + "VALUES " + + "ARRAY[null], " + + "null, " + + "ARRAY[1]"); + } + + @Override + @Test + public void testMultipleWindowFunctions() + { + assertQuery("SELECT x, array_agg(date) OVER(ORDER BY x RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING), avg(number) OVER(ORDER BY x RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING) " + + "FROM (VALUES " + + "(2, DATE '2222-01-01', 4.4), " + + "(1, DATE '1111-01-01', 2.2), " + + "(3, DATE '3333-01-01', 6.6)) T(x, date, number)", + "VALUES " + + "(1, ARRAY[DATE '1111-01-01', DATE '2222-01-01'], 3.3), " + + "(2, ARRAY[DATE '1111-01-01', DATE '2222-01-01', DATE '3333-01-01'], 4.4), " + + "(3, ARRAY[DATE '2222-01-01', DATE '3333-01-01'], 5.5)"); + + assertQueryFails("SELECT x, array_agg(a) OVER(ORDER BY x RANGE BETWEEN 2 PRECEDING AND CURRENT ROW), array_agg(a) OVER(ORDER BY x RANGE BETWEEN CURRENT ROW AND 2 FOLLOWING) " + + "FROM (VALUES " + + "(1.0, 1), " + + "(2.0, 2), " + + "(3.0, 3), " + + "(4.0, 4), " + + "(5.0, 5), " + + "(6.0, 6)) T(x, a)", frameTypeDiffersError, true); + } +} diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/AbstractTestEngineOnlyQueries.java b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestEngineOnlyQueries.java similarity index 100% rename from presto-tests/src/test/java/com/facebook/presto/tests/AbstractTestEngineOnlyQueries.java rename to presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestEngineOnlyQueries.java diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestOrderByQueries.java b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestOrderByQueries.java index 9e633ff52e9e..62a086c951bf 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestOrderByQueries.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestOrderByQueries.java @@ -86,11 +86,6 @@ public void testOrderByWithOutputColumnReference() assertQueryOrdered("SELECT -a AS a, a AS b FROM (VALUES 1, 2) t(a) GROUP BY a ORDER BY t.a+2*a", "VALUES (-2, 2), (-1, 1)"); assertQueryOrdered("SELECT -a AS a, a AS b FROM (VALUES 1, 2) t(a) GROUP BY t.a ORDER BY t.a+2*a", "VALUES (-2, 2), (-1, 1)"); - // lambdas - assertQueryOrdered("SELECT x AS y FROM (values (1,2), (2,3)) t(x, y) GROUP BY x ORDER BY apply(x, x -> -x) + 2*x", "VALUES 1, 2"); - assertQueryOrdered("SELECT -y AS x FROM (values (1,2), (2,3)) t(x, y) GROUP BY y ORDER BY apply(x, x -> -x)", "VALUES -2, -3"); - assertQueryOrdered("SELECT -y AS x FROM (values (1,2), (2,3)) t(x, y) GROUP BY y ORDER BY sum(apply(-y, x -> x * 1.0))", "VALUES -3, -2"); - // distinct assertQueryOrdered("SELECT DISTINCT -a AS b FROM (VALUES 1, 2) t(a) ORDER BY b", "VALUES -2, -1"); assertQueryOrdered("SELECT DISTINCT -a AS b FROM (VALUES 1, 2) t(a) ORDER BY 1", "VALUES -2, -1"); @@ -106,6 +101,14 @@ public void testOrderByWithOutputColumnReference() assertQueryFails("SELECT a, a* -1 AS a FROM (VALUES -1, 0, 2) t(a) ORDER BY a", ".*'a' is ambiguous"); } + @Test + public void testOrderByWithOutputColumnReferenceInLambdas() + { + assertQueryOrdered("SELECT x AS y FROM (values (1,2), (2,3)) t(x, y) GROUP BY x ORDER BY apply(x, x -> -x) + 2*x", "VALUES 1, 2"); + assertQueryOrdered("SELECT -y AS x FROM (values (1,2), (2,3)) t(x, y) GROUP BY y ORDER BY apply(x, x -> -x)", "VALUES -2, -3"); + assertQueryOrdered("SELECT -y AS x FROM (values (1,2), (2,3)) t(x, y) GROUP BY y ORDER BY sum(apply(-y, x -> x * 1.0))", "VALUES -3, -2"); + } + @Test public void testOrderByWithAggregation() { diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java index 080c53087509..a318dcccd5b7 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java @@ -290,9 +290,13 @@ public void testNonDeterministic() .distinct() .count(); assertTrue(distinctCount >= 8, "rand() must produce different rows"); + } - materializedResult = computeActual("SELECT apply(1, x -> x + rand()) FROM orders LIMIT 10"); - distinctCount = materializedResult.getMaterializedRows().stream() + @Test + public void testNonDeterministicInLambda() + { + MaterializedResult materializedResult = computeActual("SELECT apply(1, x -> x + rand()) FROM orders LIMIT 10"); + long distinctCount = materializedResult.getMaterializedRows().stream() .map(row -> row.getField(0)) .distinct() .count(); @@ -361,11 +365,16 @@ public void testLambdaInValuesAndUnnest() } @Test - public void testTryLambdaRepeated() + public void testApplyLambdaRepeated() { assertQuery("SELECT x + x FROM (SELECT apply(a, i -> i * i) x FROM (VALUES 3) t(a))", "SELECT 18"); assertQuery("SELECT apply(a, i -> i * i) + apply(a, i -> i * i) FROM (VALUES 3) t(a)", "SELECT 18"); assertQuery("SELECT apply(a, i -> i * i), apply(a, i -> i * i) FROM (VALUES 3) t(a)", "SELECT 9, 9"); + } + + @Test + public void testTryLambdaRepeated() + { assertQuery("SELECT try(10 / a) + try(10 / a) FROM (VALUES 5) t(a)", "SELECT 4"); assertQuery("SELECT try(10 / a), try(10 / a) FROM (VALUES 5) t(a)", "SELECT 2, 2"); } @@ -437,8 +446,11 @@ public void testRowSubscript() // Row subscript in join condition assertQuery("SELECT n.name, r.name FROM nation n JOIN region r ON ROW (n.name, n.regionkey)[2] = ROW (r.name, r.regionkey)[2] ORDER BY n.name LIMIT 1", "VALUES ('ALGERIA', 'AFRICA')"); + } - //Row subscript in a lambda + @Test + public void testRowSubscriptInLambda() + { assertQuery("SELECT apply(ROW (1, 2), r -> r[2])", "SELECT 2"); } @@ -2400,12 +2412,17 @@ public void testIn() assertQuery("SELECT 1 in (1, NULL, 3)", "values true"); assertQuery("SELECT 2 in (1, NULL, 3)", "values null"); assertQuery("SELECT x FROM (values DATE '1970-01-01', DATE '1970-01-03') t(x) WHERE x IN (DATE '1970-01-01')", "values DATE '1970-01-01'"); + assertQuery("SELECT COUNT(*) FROM (values 1) t(x) WHERE x IN (null, 0)", "SELECT 0"); + assertQuery("SELECT d IN (DECIMAL '2.0', DECIMAL '30.0') FROM (VALUES (2.0E0)) t(d)", "SELECT true"); // coercion with type only coercion inside IN list + } + + @Test + public void testInTimestampWithTimezone() + { assertEquals( computeActual("SELECT x FROM (values TIMESTAMP '1970-01-01 00:01:00+00:00', TIMESTAMP '1970-01-01 08:01:00+08:00', TIMESTAMP '1970-01-01 00:01:00+08:00') t(x) WHERE x IN (TIMESTAMP '1970-01-01 00:01:00+00:00')") .getOnlyColumn().collect(toList()), ImmutableList.of(zonedDateTime("1970-01-01 00:01:00.000 UTC"), zonedDateTime("1970-01-01 08:01:00.000 +08:00"))); - assertQuery("SELECT COUNT(*) FROM (values 1) t(x) WHERE x IN (null, 0)", "SELECT 0"); - assertQuery("SELECT d IN (DECIMAL '2.0', DECIMAL '30.0') FROM (VALUES (2.0E0)) t(d)", "SELECT true"); // coercion with type only coercion inside IN list } @Test @@ -3202,14 +3219,9 @@ public void testTry() assertQueryFails("SELECT TRY()", "line 1:8: The 'try' function must have exactly one argument"); // check that TRY is not pushed down - assertQueryFails("SELECT TRY(x) IS NULL FROM (SELECT 1/y AS x FROM (VALUES 1, 2, 3, 0, 4) t(y))", "/ by zero"); + assertQueryFails("SELECT TRY(x) IS NULL FROM (SELECT 1/y AS x FROM (VALUES 1, 2, 3, 0, 4) t(y))", ".*(/|division) by zero.*"); assertQuery("SELECT x IS NULL FROM (SELECT TRY(1/y) AS x FROM (VALUES 3, 0, 4) t(y))", "VALUES false, true, false"); - // test try with lambda function - assertQuery("SELECT TRY(apply(5, x -> x + 1) / 0)", "SELECT NULL"); - assertQuery("SELECT TRY(apply(5 + RANDOM(1), x -> x + 1) / 0)", "SELECT NULL"); - assertQuery("SELECT apply(5 + RANDOM(1), x -> x + TRY(1 / 0))", "SELECT NULL"); - // test try with invalid JSON assertQuery("SELECT JSON_FORMAT(TRY(JSON 'INVALID'))", "SELECT NULL"); assertQuery("SELECT JSON_FORMAT(TRY (JSON_PARSE('INVALID')))", "SELECT NULL"); @@ -3236,6 +3248,14 @@ public void testTry() assertQuery("SELECT TRY(1 / x) FROM (SELECT NULL as x)", "SELECT NULL"); } + @Test + public void testTryWithLambda() + { + assertQuery("SELECT TRY(apply(5, x -> x + 1) / 0)", "SELECT NULL"); + assertQuery("SELECT TRY(apply(5 + RANDOM(1), x -> x + 1) / 0)", "SELECT NULL"); + assertQuery("SELECT apply(5 + RANDOM(1), x -> x + TRY(1 / 0))", "SELECT NULL"); + } + @Test public void testTryNoMergeProjections() { @@ -6020,6 +6040,11 @@ public void testSetUnion() assertQuery( "select group_id, set_union(numbers) from (values (1, array[1, 2]), (2, array[2, 3]), (3, array[4, 5]), (4, array[5, 6])) as t(group_id, numbers) group by group_id", "select group_id, numbers from (values (1, array[1, 2]), (2, array[2, 3]), (3, array[4, 5]), (4, array[5, 6])) as t(group_id, numbers)"); + } + + @Test + public void testSetUnionWithNulls() + { // all nulls should return empty array to match behavior of array_distinct(flatten(array_agg(x))) assertQuery( "select set_union(x) from (values null, null, null) as t(x)", @@ -6135,7 +6160,8 @@ public void testApproxMostFrequentWithLong() MaterializedResult actual2 = computeActual("SELECT approx_most_frequent(2, cast(x as bigint), 15) FROM (values 1, 2, 1, 3, 1, 2, 3, 4, 5) t(x)"); assertEquals(actual2.getRowCount(), 1); - assertEquals(actual2.getMaterializedRows().get(0).getFields().get(0), ImmutableMap.of(1L, 3L, 2L, 2L)); + Object actual2Value = actual2.getMaterializedRows().get(0).getFields().get(0); + assertTrue(actual2Value.equals(ImmutableMap.of(1L, 3L, 2L, 2L)) || actual2Value.equals(ImmutableMap.of(1L, 3L, 3L, 2L))); } @Test @@ -6147,7 +6173,8 @@ public void testApproxMostFrequentWithVarchar() MaterializedResult actual2 = computeActual("SELECT approx_most_frequent(2, x, 15) FROM (values 'A', 'B', 'A', 'C', 'A', 'B', 'C', 'D', 'E') t(x)"); assertEquals(actual2.getRowCount(), 1); - assertEquals(actual2.getMaterializedRows().get(0).getFields().get(0), ImmutableMap.of("A", 3L, "B", 2L)); + Object actual2Value = actual2.getMaterializedRows().get(0).getFields().get(0); + assertTrue(actual2Value.equals(ImmutableMap.of("A", 3L, "B", 2L)) || actual2Value.equals(ImmutableMap.of("A", 3L, "C", 2L))); } @Test @@ -6580,15 +6607,6 @@ public void testDuplicateUnnestItem() "VALUES (1, 2, 'a', 2, 'a', 1, 'a', 1), (1, 3, 'b', 3, 'b', 2, 'b', 2), (1, NULL, NULL, NULL, NULL, 3, 'c', 3)"); assertQuery("SELECT * from ( SELECT ARRAY[1] AS kv FROM (select 1)) CROSS JOIN UNNEST( kv, kv ) WITH ORDINALITY AS t(r1, r2, ord)", "VALUES (ARRAY[1], 1, 1, 1)"); - assertQuery("SELECT * from (select * FROM (values 1) as t(k)) CROSS JOIN unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)]) AS r(r1, r2, r3, r4)", - "VALUES (1, 2, 3, 2, 3), (1, 3, 5, 3, 5)"); - assertQuery("SELECT * from (select * FROM (values 1) as t(k)) CROSS JOIN unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)], ARRAY[row(10, 13, 15), row(23, 25, 20)]) AS r(r1, r2, r3, r4, r5, r6, r7)", - "VALUES (1, 2, 3, 2, 3, 10, 13, 15), (1, 3, 5, 3, 5, 23, 25, 20)"); - assertQuery("SELECT * from (select * FROM (values 1) as t(k)) CROSS JOIN unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)]) WITH ORDINALITY AS r(r1, r2, r3, r4, ord)", - "VALUES (1, 2, 3, 2, 3, 1), (1, 3, 5, 3, 5, 2)"); - assertQuery("SELECT * from (select * FROM (values 1) as t(k)) CROSS JOIN unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)], ARRAY[row(10, 13, 15), row(23, 25, 20)]) WITH ORDINALITY AS r(r1, r2, r3, r4, r5, r6, r7, ord)", - "VALUES (1, 2, 3, 2, 3, 10, 13, 15, 1), (1, 3, 5, 3, 5, 23, 25, 20, 2)"); - Session useLegacyUnnest = Session.builder(getSession()) .setSystemProperty(LEGACY_UNNEST, "true") .build(); @@ -6624,11 +6642,6 @@ public void testDuplicateUnnestItem() assertQuery("SELECT * from unnest(ARRAY[2, 3], ARRAY[10,11,12], ARRAY[2, 3]) WITH ORDINALITY AS r(r1, r2, r3, ord)", "VALUES (2, 10, 2, 1), (3, 11, 3, 2), (NULL, 12, NULL, 3)"); assertQuery("SELECT * from unnest(ARRAY[2, 3], ARRAY[2, 3], ARRAY[10,11,12]) WITH ORDINALITY AS r(r1, r2, r3, ord)", "VALUES (2, 2, 10, 1), (3, 3, 11, 2), (NULL, NULL, 12, 3)"); - assertQuery("SELECT * from unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)]) AS r(r1, r2, r3, r4)", - "VALUES (2, 3, 2, 3), (3, 5, 3, 5)"); - assertQuery("SELECT * from unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)]) WITH ORDINALITY AS r(r1, r2, r3, r4, ord)", - "VALUES (2, 3, 2, 3, 1), (3, 5, 3, 5, 2)"); - assertQuery(useLegacyUnnest, "SELECT cast(r1 as row(x int, y int)), cast(r2 as row(x int, y int)) from unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)]) AS r(r1, r2)", "VALUES (row(2, 3), row(2, 3)), (row(3, 5), row(3, 5))"); assertQuery(useLegacyUnnest, "SELECT cast(r1 as row(x int, y int)), cast(r2 as row(x int, y int)), cast(r3 as row(x int, y int, z int)) from unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)], ARRAY[row(10, 13, 15), row(23, 25, 20)]) AS r(r1, r2, r3)", @@ -6643,6 +6656,24 @@ public void testDuplicateUnnestItem() "VALUES (2, 2, 1, 2, 2), (2, 2, 1, 3, 3), (3, 3, 2, 2, 2), (3, 3, 2, 3, 3)"); } + @Test + public void testDuplicateUnnestRows() + { + assertQuery("SELECT * from (select * FROM (values 1) as t(k)) CROSS JOIN unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)]) AS r(r1, r2, r3, r4)", + "VALUES (1, 2, 3, 2, 3), (1, 3, 5, 3, 5)"); + assertQuery("SELECT * from (select * FROM (values 1) as t(k)) CROSS JOIN unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)], ARRAY[row(10, 13, 15), row(23, 25, 20)]) AS r(r1, r2, r3, r4, r5, r6, r7)", + "VALUES (1, 2, 3, 2, 3, 10, 13, 15), (1, 3, 5, 3, 5, 23, 25, 20)"); + assertQuery("SELECT * from (select * FROM (values 1) as t(k)) CROSS JOIN unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)]) WITH ORDINALITY AS r(r1, r2, r3, r4, ord)", + "VALUES (1, 2, 3, 2, 3, 1), (1, 3, 5, 3, 5, 2)"); + assertQuery("SELECT * from (select * FROM (values 1) as t(k)) CROSS JOIN unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)], ARRAY[row(10, 13, 15), row(23, 25, 20)]) WITH ORDINALITY AS r(r1, r2, r3, r4, r5, r6, r7, ord)", + "VALUES (1, 2, 3, 2, 3, 10, 13, 15, 1), (1, 3, 5, 3, 5, 23, 25, 20, 2)"); + + assertQuery("SELECT * from unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)]) AS r(r1, r2, r3, r4)", + "VALUES (2, 3, 2, 3), (3, 5, 3, 5)"); + assertQuery("SELECT * from unnest(ARRAY[row(2, 3), row(3, 5)], ARRAY[row(2, 3), row(3, 5)]) WITH ORDINALITY AS r(r1, r2, r3, r4, ord)", + "VALUES (2, 3, 2, 3, 1), (3, 5, 3, 5, 2)"); + } + @Test public void testDependentWindowFunction() { @@ -6993,14 +7024,14 @@ public void testArraySplitIntoChunks() } @Test - public void testArrayCumSum() + public void testArrayCumSumIntegers() { // int String sql = "select array_cum_sum(k) from (values (array[cast(5 as INTEGER), 6, 0]), (ARRAY[]), (CAST(NULL AS array(integer)))) t(k)"; assertQuery(sql, "values array[cast(5 as integer), cast(11 as integer), cast(11 as integer)], array[], null"); sql = "select array_cum_sum(k) from (values (array[cast(5 as INTEGER), 6, 0]), (ARRAY[]), (CAST(NULL AS array(integer))), (ARRAY [cast(2147483647 as INTEGER), 2147483647, 2147483647])) t(k)"; - assertQueryFails(sql, "integer addition overflow:.*"); + assertQueryFails(sql, "integer (addition )?overflow:.*", true); sql = "select array_cum_sum(k) from (values (array[cast(5 as INTEGER), 6, null, 2, 3])) t(k)"; assertQuery(sql, "values array[cast(5 as integer), cast(11 as integer), cast(null as integer), cast(null as integer), cast(null as integer)]"); @@ -7017,9 +7048,24 @@ public void testArrayCumSum() sql = "select array_cum_sum(k) from (values (array[cast(null as bigint), 6, null, 2, 3])) t(k)"; assertQuery(sql, "values array[cast(null as bigint), cast(null as bigint), cast(null as bigint), cast(null as bigint), cast(null as bigint)]"); + } + + @Test + public void testArrayCumSumDecimals() + { + String sql = "select array_cum_sum(k) from (values (array[cast(5.1 as decimal(38, 1)), 6, 0]), (ARRAY[]), (CAST(NULL AS array(decimal)))) t(k)"; + assertQuery(sql, "values array[cast(5.1 as decimal), cast(11.1 as decimal), cast(11.1 as decimal)], array[], null"); + sql = "select array_cum_sum(k) from (values (array[cast(5.1 as decimal(38, 1)), 6, null, 3]), (array[cast(null as decimal(38, 1)), 6, null, 3])) t(k)"; + assertQuery(sql, "values array[cast(5.1 as decimal), cast(11.1 as decimal), cast(null as decimal), cast(null as decimal)], " + + "array[cast(null as decimal), cast(null as decimal), cast(null as decimal), cast(null as decimal)]"); + } + + @Test + public void testArrayCumSumReals() + { // real - sql = "select array_cum_sum(k) from (values (array[cast(null as real), 6, null, 2, 3])) t(k)"; + String sql = "select array_cum_sum(k) from (values (array[cast(null as real), 6, null, 2, 3])) t(k)"; assertQuery(sql, "values array[cast(null as real), cast(null as real), cast(null as real), cast(null as real), cast(null as real)]"); MaterializedResult raw = computeActual("SELECT array_cum_sum(k) FROM (values (ARRAY [cast(5.1 as real), 6.1, 0.5]), (ARRAY[]), (CAST(NULL AS array(real))), " + @@ -7080,17 +7126,12 @@ public void testArrayCumSum() for (int i = 2; i < actualDouble.size(); ++i) { assertNull(actualDouble.get(i)); } + } - // decimal - sql = "select array_cum_sum(k) from (values (array[cast(5.1 as decimal(38, 1)), 6, 0]), (ARRAY[]), (CAST(NULL AS array(decimal)))) t(k)"; - assertQuery(sql, "values array[cast(5.1 as decimal), cast(11.1 as decimal), cast(11.1 as decimal)], array[], null"); - - sql = "select array_cum_sum(k) from (values (array[cast(5.1 as decimal(38, 1)), 6, null, 3]), (array[cast(null as decimal(38, 1)), 6, null, 3])) t(k)"; - assertQuery(sql, "values array[cast(5.1 as decimal), cast(11.1 as decimal), cast(null as decimal), cast(null as decimal)], " + - "array[cast(null as decimal), cast(null as decimal), cast(null as decimal), cast(null as decimal)]"); - - // varchar - sql = "select array_cum_sum(k) from (values (array[cast('5.1' as varchar), '6', '0']), (ARRAY[]), (CAST(NULL AS array(varchar)))) t(k)"; + @Test + public void testArrayCumSumVarchar() + { + String sql = "select array_cum_sum(k) from (values (array[cast('5.1' as varchar), '6', '0']), (ARRAY[]), (CAST(NULL AS array(varchar)))) t(k)"; assertQueryFails(sql, ".*cannot be applied to.*"); sql = "select array_cum_sum(k) from (values (array[cast(null as varchar), '6', '0'])) t(k)"; @@ -7788,12 +7829,20 @@ public void testRemoveMapCast() "values 0.5, 0.1"); assertQuery(enableOptimization, "select element_at(feature, key) from (values (map(array[cast(1 as integer), 2, 3, 4], array[0.3, 0.5, 0.9, 0.1]), cast(2 as bigint)), (map(array[cast(1 as integer), 2, 3, 4], array[0.3, 0.5, 0.9, 0.1]), 400000000000)) t(feature, key)", "values 0.5, null"); - assertQueryFails(enableOptimization, "select feature[key] from (values (map(array[cast(1 as integer), 2, 3, 4], array[0.3, 0.5, 0.9, 0.1]), cast(2 as bigint)), (map(array[cast(1 as integer), 2, 3, 4], array[0.3, 0.5, 0.9, 0.1]), 400000000000)) t(feature, key)", - ".*Out of range for integer.*"); assertQuery(enableOptimization, "select feature[key] from (values (map(array[cast(1 as varchar), '2', '3', '4'], array[0.3, 0.5, 0.9, 0.1]), cast('2' as varchar)), (map(array[cast(1 as varchar), '2', '3', '4'], array[0.3, 0.5, 0.9, 0.1]), '4')) t(feature, key)", "values 0.5, 0.1"); } + @Test + public void testRemoveMapCastFailure() + { + Session enableOptimization = Session.builder(getSession()) + .setSystemProperty(REMOVE_MAP_CAST, "true") + .build(); + assertQueryFails(enableOptimization, "select feature[key] from (values (map(array[cast(1 as integer), 2, 3, 4], array[0.3, 0.5, 0.9, 0.1]), cast(2 as bigint)), (map(array[cast(1 as integer), 2, 3, 4], array[0.3, 0.5, 0.9, 0.1]), 400000000000)) t(feature, key)", + ".*Out of range for integer.*"); + } + // Test to guardrail problems in constraint framework mentioned in https://github.com/prestodb/presto/pull/22171 @Test public void testGuardConstraintFramework() diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueryFramework.java b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueryFramework.java index 303ae0dfe780..6ab65e61bd80 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueryFramework.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueryFramework.java @@ -310,6 +310,11 @@ protected void assertQueryFails(QueryRunner queryRunner, @Language("SQL") String QueryAssertions.assertQueryFails(queryRunner, getSession(), sql, expectedMessageRegExp); } + protected void assertQueryFails(@Language("SQL") String sql, @Language("RegExp") String expectedMessageRegExp, Boolean usePatternMatcher) + { + QueryAssertions.assertQueryFails(queryRunner, getSession(), sql, expectedMessageRegExp, usePatternMatcher); + } + protected void assertQueryFails(Session session, @Language("SQL") String sql, @Language("RegExp") String expectedMessageRegExp) { QueryAssertions.assertQueryFails(queryRunner, session, sql, expectedMessageRegExp); @@ -330,6 +335,11 @@ protected void assertQueryError(@Language("SQL") String sql, @Language("RegExp") assertQueryError(queryRunner, getSession(), sql, expectedMessageRegExp); } + protected void assertQueryFails(Session session, @Language("SQL") String sql, @Language("RegExp") String expectedMessageRegExp, Boolean usePatternMatcher) + { + QueryAssertions.assertQueryFails(queryRunner, session, sql, expectedMessageRegExp, usePatternMatcher); + } + protected void assertQueryReturnsEmptyResult(@Language("SQL") String sql) { QueryAssertions.assertQueryReturnsEmptyResult(queryRunner, getSession(), sql); diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestWindowQueries.java b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestWindowQueries.java index 14fa5afa8ebf..b218ff280bd6 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestWindowQueries.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestWindowQueries.java @@ -1016,7 +1016,7 @@ public void testEmptyInput() } @Test - public void testEmptyFrame() + public void testEmptyFrameIntegralBounds() { assertQuery("SELECT array_agg(a) OVER(ORDER BY a DESC NULLS LAST RANGE BETWEEN 1 PRECEDING AND 10 PRECEDING) " + "FROM (VALUES 1, 2, 3, null, null, 2, 1, null, null) T(a)", @@ -1044,13 +1044,6 @@ public void testEmptyFrame() "ARRAY[null, null, null, null], " + "ARRAY[null, null, null, null]"); - assertQuery("SELECT array_agg(a) OVER(ORDER BY a RANGE BETWEEN 0.5 FOLLOWING AND 1.5 FOLLOWING) " + - "FROM (VALUES 1, 2, 4) T(a)", - "VALUES " + - "ARRAY[2], " + - "null, " + - "null"); - assertQuery("SELECT array_agg(a) OVER(ORDER BY a RANGE BETWEEN 1 FOLLOWING AND 2 FOLLOWING) " + "FROM (VALUES 1.0, 1.1) T(a)", "VALUES " + @@ -1089,6 +1082,17 @@ public void testEmptyFrame() "ARRAY[null], " + "null, " + "ARRAY[1]"); + } + + @Test + public void testEmptyFrameRealBounds() + { + assertQuery("SELECT array_agg(a) OVER(ORDER BY a RANGE BETWEEN 0.5 FOLLOWING AND 1.5 FOLLOWING) " + + "FROM (VALUES 1, 2, 4) T(a)", + "VALUES " + + "ARRAY[2], " + + "null, " + + "null"); assertQuery("SELECT array_agg(a) OVER(ORDER BY a NULLS FIRST RANGE BETWEEN 2 PRECEDING AND 1.5 PRECEDING) " + "FROM (VALUES null, 1, 2) T(a)", diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/QueryAssertions.java b/presto-tests/src/main/java/com/facebook/presto/tests/QueryAssertions.java index 035051acf286..6c39ba53366e 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/QueryAssertions.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/QueryAssertions.java @@ -37,6 +37,7 @@ import java.util.OptionalLong; import java.util.function.Consumer; import java.util.function.Supplier; +import java.util.regex.Pattern; import static com.google.common.base.Strings.nullToEmpty; import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly; @@ -350,6 +351,30 @@ protected static void assertQueryFails(QueryRunner queryRunner, Session session, } } + private static void assertExceptionWithPatternMatch(String sql, Exception exception, @Language("RegExp") String regex) + { + Pattern p = Pattern.compile(regex, Pattern.MULTILINE); + if (!(p.matcher(exception.getMessage()).find())) { + fail(format("Expected exception message '%s' to match '%s' for query: %s", exception.getMessage(), regex, sql), exception); + } + } + + protected static void assertQueryFails(QueryRunner queryRunner, Session session, @Language("SQL") String sql, @Language("RegExp") String expectedMessageRegExp, Boolean usePatternMatcher) + { + try { + queryRunner.execute(session, sql); + fail(format("Expected query to fail: %s", sql)); + } + catch (RuntimeException ex) { + if (usePatternMatcher) { + assertExceptionWithPatternMatch(sql, ex, expectedMessageRegExp); + } + else { + assertExceptionMessage(sql, ex, expectedMessageRegExp); + } + } + } + protected static void assertQueryReturnsEmptyResult(QueryRunner queryRunner, Session session, @Language("SQL") String sql) { try {