diff --git a/.github/workflows/prestocpp-linux-build-and-unit-test.yml b/.github/workflows/prestocpp-linux-build-and-unit-test.yml index 13f7c6b3454ee..554daeee95254 100644 --- a/.github/workflows/prestocpp-linux-build-and-unit-test.yml +++ b/.github/workflows/prestocpp-linux-build-and-unit-test.yml @@ -79,6 +79,8 @@ jobs: github.event_name == 'schedule' || needs.changes.outputs.codechange == 'true' run: | source /opt/rh/gcc-toolset-12/enable + cp -r presto-native-tests/presto_cpp/tests presto-native-execution/presto_cpp/main/functions/dynamic_registry + echo "add_subdirectory(tests)" >> presto-native-execution/presto_cpp/main/functions/dynamic_registry/CMakeLists.txt cd presto-native-execution cmake \ -B _build/release \ @@ -126,6 +128,7 @@ jobs: path: | presto-native-execution/_build/release/presto_cpp/main/presto_server presto-native-execution/_build/release/velox/velox/functions/remote/server/velox_functions_remote_server_main + presto-native-execution/_build/release/presto_cpp/main/functions/dynamic_registry/tests/ prestocpp-linux-presto-e2e-tests: needs: [changes, prestocpp-linux-build-for-test] 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 072ba5be1d47b..02deaf46e9ed5 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 @@ -120,6 +120,7 @@ public static class HiveQueryRunnerBuilder private Map extraCoordinatorProperties = new HashMap<>(); private Map hiveProperties = new HashMap<>(); private Map tpcdsProperties = new HashMap<>(); + private Optional pluginDirectory = Optional.empty(); private String security; private boolean addStorageFormatToPath; private boolean coordinatorSidecarEnabled; @@ -171,6 +172,12 @@ public HiveQueryRunnerBuilder setRemoteFunctionServerUds(Optional remote return this; } + public HiveQueryRunnerBuilder setPluginDirectory(Optional pluginDirectory) + { + this.pluginDirectory = pluginDirectory; + return this; + } + public HiveQueryRunnerBuilder setFailOnNestedLoopJoin(boolean failOnNestedLoopJoin) { this.failOnNestedLoopJoin = failOnNestedLoopJoin; @@ -281,7 +288,7 @@ public QueryRunner build() Optional> externalWorkerLauncher = Optional.empty(); if (this.useExternalWorkerLauncher) { externalWorkerLauncher = getExternalWorkerLauncher("hive", serverBinary, cacheMaxSize, remoteFunctionServerUds, - failOnNestedLoopJoin, coordinatorSidecarEnabled, builtInWorkerFunctionsEnabled, enableRuntimeMetricsCollection, enableSsdCache, implicitCastCharNToVarchar); + pluginDirectory, failOnNestedLoopJoin, coordinatorSidecarEnabled, builtInWorkerFunctionsEnabled, enableRuntimeMetricsCollection, enableSsdCache, implicitCastCharNToVarchar); } return HiveQueryRunner.createQueryRunner( ImmutableList.of(), @@ -370,7 +377,7 @@ public QueryRunner build() Optional> externalWorkerLauncher = Optional.empty(); if (this.useExternalWorkerLauncher) { externalWorkerLauncher = getExternalWorkerLauncher("iceberg", serverBinary, cacheMaxSize, remoteFunctionServerUds, - false, false, false, false, false, false); + Optional.empty(), false, false, false, false, false, false); } return IcebergQueryRunner.builder() .setExtraProperties(extraProperties) @@ -464,6 +471,7 @@ public static Optional> getExternalWorkerLaunc String prestoServerPath, int cacheMaxSize, Optional remoteFunctionServerUds, + Optional pluginDirectory, Boolean failOnNestedLoopJoin, boolean isCoordinatorSidecarEnabled, boolean isBuiltInWorkerFunctionsEnabled, @@ -517,6 +525,10 @@ else if (isBuiltInWorkerFunctionsEnabled) { "remote-function-server.signature.files.directory.path=%s%n", configProperties, REMOTE_FUNCTION_CATALOG_NAME, remoteFunctionServerUds.get(), jsonSignaturesPath); } + if (pluginDirectory.isPresent()) { + configProperties = format("%s%n" + "plugin.dir=%s%n", configProperties, pluginDirectory.get()); + } + if (failOnNestedLoopJoin) { configProperties = format("%s%n" + "velox-plan-validator-fail-on-nested-loop-join=true%n", configProperties); } diff --git a/presto-native-tests/CMakeLists.txt b/presto-native-tests/CMakeLists.txt new file mode 100644 index 0000000000000..6cd83f84951cf --- /dev/null +++ b/presto-native-tests/CMakeLists.txt @@ -0,0 +1,71 @@ +# 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. +cmake_minimum_required(VERSION 3.10) + +# set the project name +project(PrestoCpp) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) +message("Appending CMAKE_CXX_FLAGS with ${SCRIPT_CXX_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SCRIPT_CXX_FLAGS}") + +# Known warnings that are benign can be disabled. +set(DISABLED_WARNINGS + "-Wno-nullability-completeness -Wno-deprecated-declarations") + +# Important warnings that must be explicitly enabled. +set(ENABLE_WARNINGS "-Wreorder") + +set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} ${DISABLED_WARNINGS} ${ENABLE_WARNINGS}") + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +if(DEFINED ENV{INSTALL_PREFIX}) + message(STATUS "Dependency install directory set to: $ENV{INSTALL_PREFIX}") + list(APPEND CMAKE_PREFIX_PATH "$ENV{INSTALL_PREFIX}") +endif() + +find_package(gflags REQUIRED) + +find_library(GLOG glog) + +find_package(fmt REQUIRED) + +if(NOT TARGET gflags::gflags) + # This is a bit convoluted, but we want to be able to use gflags::gflags as a + # target even when velox is built as a subproject which uses + # `find_package(gflags)` which does not create a globally imported target that + # we can ALIAS. + add_library(gflags_gflags INTERFACE) + target_link_libraries(gflags_gflags INTERFACE gflags) + add_library(gflags::gflags ALIAS gflags_gflags) +endif() +include_directories(.) + +include_directories(${CMAKE_BINARY_DIR}) + +# Adding this down here prevents warnings in dependencies from stopping the +# build. +if("${TREAT_WARNINGS_AS_ERRORS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") +endif() + +if(DEFINED ENV{INSTALL_PREFIX}) + # Allow installed package headers to be picked up before brew/system package + # headers. We set this after Velox since Velox handles INSTALL_PREFIX its own + # way. + include_directories(BEFORE "$ENV{INSTALL_PREFIX}/include") +endif() + +add_subdirectory(presto_cpp) diff --git a/presto-native-tests/Makefile b/presto-native-tests/Makefile new file mode 100644 index 0000000000000..4147efc6d6070 --- /dev/null +++ b/presto-native-tests/Makefile @@ -0,0 +1,67 @@ +# 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. +.PHONY: build clean debug release submodules + +BUILD_BASE_DIR=_build +BUILD_DIR=release +BUILD_TYPE=Release +TREAT_WARNINGS_AS_ERRORS ?= 1 +ENABLE_WALL ?= 1 +NUM_THREADS ?= $(shell getconf _NPROCESSORS_CONF 2>/dev/null || echo 1) +CMAKE_PREFIX_PATH ?= "/usr/local" + +EXTRA_CMAKE_FLAGS ?= "" + +CMAKE_FLAGS := -DTREAT_WARNINGS_AS_ERRORS=${TREAT_WARNINGS_AS_ERRORS} +CMAKE_FLAGS += -DENABLE_ALL_WARNINGS=${ENABLE_WALL} +CMAKE_FLAGS += -DCMAKE_PREFIX_PATH=$(CMAKE_PREFIX_PATH) +CMAKE_FLAGS += -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) + +SHELL := /bin/bash + +# Use Ninja if available. If Ninja is used, pass through parallelism control flags. +USE_NINJA ?= 1 +ifeq ($(USE_NINJA), 1) +ifneq ($(shell which ninja), ) +CMAKE_FLAGS += -GNinja -DMAX_LINK_JOBS=$(MAX_LINK_JOBS) -DMAX_HIGH_MEM_JOBS=$(MAX_HIGH_MEM_JOBS) +endif +endif + +ifndef USE_CCACHE +ifneq ($(shell which ccache), ) +CMAKE_FLAGS += -DCMAKE_CXX_COMPILER_LAUNCHER=ccache +endif +endif + +all: release #: Build the release version + +clean: #: Delete all build artifacts + rm -rf $(BUILD_BASE_DIR) + +cmake: submodules #: Use CMake to create a Makefile build system + cmake -B "$(BUILD_BASE_DIR)/$(BUILD_DIR)" $(FORCE_COLOR) $(CMAKE_FLAGS) $(EXTRA_CMAKE_FLAGS) + +build: #: Build the software based in BUILD_DIR and BUILD_TYPE variables + cmake --build $(BUILD_BASE_DIR)/$(BUILD_DIR) -j $(NUM_THREADS) + +debug: #: Build with debugging symbols + $(MAKE) cmake BUILD_DIR=debug BUILD_TYPE=Debug + $(MAKE) build BUILD_DIR=debug + +release: #: Build the release version + $(MAKE) cmake BUILD_DIR=release BUILD_TYPE=Release && \ + $(MAKE) build BUILD_DIR=release + +help: #: Show the help messages + @cat $(firstword $(MAKEFILE_LIST)) | \ + awk '/^[-a-z]+:/' | \ + awk -F: '{ printf("%-20s %s\n", $$1, $$NF) }' diff --git a/presto-native-tests/presto_cpp/CMakeLists.txt b/presto-native-tests/presto_cpp/CMakeLists.txt new file mode 100644 index 0000000000000..6a4085713e395 --- /dev/null +++ b/presto-native-tests/presto_cpp/CMakeLists.txt @@ -0,0 +1,12 @@ +# 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. +add_subdirectory(tests) diff --git a/presto-native-tests/presto_cpp/tests/CMakeLists.txt b/presto-native-tests/presto_cpp/tests/CMakeLists.txt new file mode 100644 index 0000000000000..a056e86ef716b --- /dev/null +++ b/presto-native-tests/presto_cpp/tests/CMakeLists.txt @@ -0,0 +1,12 @@ +# 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. +add_subdirectory(custom_functions) diff --git a/presto-native-tests/presto_cpp/tests/custom_functions/CMakeLists.txt b/presto-native-tests/presto_cpp/tests/custom_functions/CMakeLists.txt new file mode 100644 index 0000000000000..a2d62155f91d5 --- /dev/null +++ b/presto-native-tests/presto_cpp/tests/custom_functions/CMakeLists.txt @@ -0,0 +1,39 @@ +# 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. +set(PRESTO_AND_VELOX_INCLUDE_DIRS + ${CMAKE_SOURCE_DIR}/../presto-native-execution + ${CMAKE_SOURCE_DIR}/../presto-native-execution/velox +) + +add_library(presto_cpp_custom_functions_test SHARED CustomFunctions.cpp) + +set(CMAKE_DYLIB_TEST_LINK_LIBRARIES fmt::fmt + xsimd) + +target_include_directories(presto_cpp_custom_functions_test + PRIVATE + ${PRESTO_AND_VELOX_INCLUDE_DIRS} + presto_dynamic_function_registrar +) + +target_link_libraries(presto_cpp_custom_functions_test + PRIVATE + ${CMAKE_DYLIB_TEST_LINK_LIBRARIES}) + +if(APPLE) + set(COMMON_LIBRARY_LINK_OPTIONS "-Wl,-undefined,dynamic_lookup") +else() + set(COMMON_LIBRARY_LINK_OPTIONS "-Wl,--exclude-libs,ALL") +endif() + +target_link_options(presto_cpp_custom_functions_test PRIVATE + ${COMMON_LIBRARY_LINK_OPTIONS}) diff --git a/presto-native-tests/presto_cpp/tests/custom_functions/CustomFunctions.cpp b/presto-native-tests/presto_cpp/tests/custom_functions/CustomFunctions.cpp new file mode 100644 index 0000000000000..cf2b5f252fd03 --- /dev/null +++ b/presto-native-tests/presto_cpp/tests/custom_functions/CustomFunctions.cpp @@ -0,0 +1,112 @@ +/* + * 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. + */ + + #include + #include "presto_cpp/main/functions/dynamic_registry/DynamicFunctionRegistrar.h" + #include "velox/expression/SimpleFunctionRegistry.h" + // This file defines a function that will be dynamically linked and + // registered. This function implements a custom function in the definition, + // and this library (.so) needs to provide a `void registerExtensions()` + // C function in the top-level namespace. + // + // (note the extern "C" directive to prevent the compiler from mangling the + // symbol name). + +namespace custom::functionRegistry { + template + struct CustomAdd { + VELOX_DEFINE_FUNCTION_TYPES(T); + FOLLY_ALWAYS_INLINE bool call( + out_type& result, + const arg_type& x1, + const arg_type& x2) { + result = x1 + x2; + return true; + } + }; + + template + struct SumArray { + VELOX_DEFINE_FUNCTION_TYPES(T); + FOLLY_ALWAYS_INLINE bool call( + out_type& result, + const arg_type>& arr) { + int64_t sum = 0; + for (auto val : arr) { + if (val.has_value()) { + sum += val.value(); + } + } + result = sum; + return true; + } + }; + + template + struct MapSize { + VELOX_DEFINE_FUNCTION_TYPES(T); + FOLLY_ALWAYS_INLINE bool call( + out_type& result, + const arg_type>& m) { + result = m.size(); + return true; + } + }; + + template + struct SumNestedArrayElements { + VELOX_DEFINE_FUNCTION_TYPES(T); + FOLLY_ALWAYS_INLINE bool call( + out_type& result, + const arg_type>>& arr) { + int64_t sum = 0; + for (auto innerOpt : arr) { + if (innerOpt.has_value()) { + for (auto val : innerOpt.value()) { + if (val.has_value()) { + sum += val.value(); + } + } + } + } + result = sum; + return true; + } +}; +} // namespace custom::functionRegistry + +extern "C" { + void registerExtensions() { + facebook::presto::registerPrestoFunction< + custom::functionRegistry::CustomAdd, + int64_t, + int64_t, + int64_t>("dynamic_custom_add"); + + facebook::presto::registerPrestoFunction< + custom::functionRegistry::SumArray, + int64_t, + facebook::velox::Array>("sum_array"); + + facebook::presto::registerPrestoFunction< + custom::functionRegistry::MapSize, + int64_t, + facebook::velox::Map>("map_size"); + + facebook::presto::registerPrestoFunction< + custom::functionRegistry::SumNestedArrayElements, + int64_t, + facebook::velox::Array>>("sum_nested_array_elements"); + } +} diff --git a/presto-native-tests/src/test/java/com/facebook/presto/nativetests/NativeTestsUtils.java b/presto-native-tests/src/test/java/com/facebook/presto/nativetests/NativeTestsUtils.java index 12d4cabed0220..87382ba8ba445 100644 --- a/presto-native-tests/src/test/java/com/facebook/presto/nativetests/NativeTestsUtils.java +++ b/presto-native-tests/src/test/java/com/facebook/presto/nativetests/NativeTestsUtils.java @@ -15,6 +15,13 @@ import com.facebook.presto.nativeworker.NativeQueryRunnerUtils; import com.facebook.presto.testing.QueryRunner; +import com.google.common.collect.ImmutableList; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Optional; import static com.facebook.presto.nativeworker.PrestoNativeQueryRunnerUtils.javaHiveQueryRunnerBuilder; import static com.facebook.presto.nativeworker.PrestoNativeQueryRunnerUtils.nativeHiveQueryRunnerBuilder; @@ -33,6 +40,7 @@ public static QueryRunner createNativeQueryRunner(String storageFormat, boolean .setUseThrift(true) .setImplicitCastCharNToVarchar(charNToVarcharImplicitCast) .setCoordinatorSidecarEnabled(sidecarEnabled) + .setPluginDirectory(sidecarEnabled ? Optional.of(getCustomFunctionsPluginDirectory().toString()) : Optional.empty()) .build(); if (sidecarEnabled) { setupNativeSidecarPlugin(queryRunner); @@ -72,4 +80,35 @@ public static boolean getCharNToVarcharImplicitCastForTest(boolean sidecarEnable { return sidecarEnabled ? false : charNToVarcharImplicitCast; } + + public static Path getCustomFunctionsPluginDirectory() + throws Exception + { + Path prestoRoot = findPrestoRoot(); + + // All candidate paths relative to prestoRoot + List candidates = ImmutableList.of( + prestoRoot.resolve("presto-native-tests/_build/debug/presto_cpp/tests/custom_functions"), + prestoRoot.resolve("presto-native-tests/_build/release/presto_cpp/tests/custom_functions"), + prestoRoot.resolve("presto-native-execution/_build/debug/presto_cpp/main/functions/dynamic_registry/tests/custom_functions"), + prestoRoot.resolve("presto-native-execution/_build/release/presto_cpp/main/functions/dynamic_registry/tests/custom_functions")); + + return candidates.stream() + .filter(Files::exists) + .findFirst() + .orElseThrow(() -> new IllegalStateException("Could not locate custom functions directory")); + } + + private static Path findPrestoRoot() + { + Path dir = Paths.get(System.getProperty("user.dir")).toAbsolutePath(); + while (dir != null) { + if (Files.exists(dir.resolve("presto-native-tests")) || + Files.exists(dir.resolve("presto-native-execution"))) { + return dir; + } + dir = dir.getParent(); + } + throw new IllegalStateException("Could not locate presto root directory."); + } } diff --git a/presto-native-tests/src/test/java/com/facebook/presto/nativetests/TestCustomFunctions.java b/presto-native-tests/src/test/java/com/facebook/presto/nativetests/TestCustomFunctions.java new file mode 100644 index 0000000000000..0ad7b7537f75a --- /dev/null +++ b/presto-native-tests/src/test/java/com/facebook/presto/nativetests/TestCustomFunctions.java @@ -0,0 +1,148 @@ +/* + * 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.testing.QueryRunner; +import com.facebook.presto.tests.AbstractTestQueryFramework; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeSuite; +import org.testng.annotations.Test; +import org.testng.log4testng.Logger; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static com.facebook.presto.nativetests.NativeTestsUtils.getCustomFunctionsPluginDirectory; +import static java.lang.Boolean.parseBoolean; + +public class TestCustomFunctions + extends AbstractTestQueryFramework +{ + private static final Logger logger = Logger.getLogger(TestCustomFunctions.class); + private String storageFormat; + private boolean sidecarEnabled; + + @BeforeSuite + public void buildNativeLibrary() + throws IOException, InterruptedException + { + // If we built with examples on, do not try to build. + // This usually happens during the github pipeline. + try { + getCustomFunctionsPluginDirectory(); + } + catch (Exception e) { + Path prestoRoot = Paths.get(System.getProperty("user.dir")).toAbsolutePath(); + while (prestoRoot != null && !Files.exists(prestoRoot.resolve("presto-native-tests"))) { + prestoRoot = prestoRoot.getParent(); + } + if (prestoRoot == null) { + throw new IllegalStateException("Could not locate presto root directory."); + } + String workingDir = prestoRoot + .resolve("presto-native-tests").toAbsolutePath().toString(); + ProcessBuilder builder = new ProcessBuilder("make", "release"); + builder.directory(new File(workingDir)); + builder.redirectErrorStream(true); + Process process = builder.start(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + System.out.println("[BUILD OUTPUT] " + line); + } + } + int exitCode = process.waitFor(); + if (exitCode != 0) { + throw new IllegalStateException("C++ build failed with exit code " + exitCode); + } + } + } + + @BeforeClass + @Override + public void init() + throws Exception + { + storageFormat = System.getProperty("storageFormat", "PARQUET"); + sidecarEnabled = parseBoolean(System.getProperty("sidecarEnabled", "true")); + super.init(); + } + + @Override + protected QueryRunner createQueryRunner() throws Exception + { + return NativeTestsUtils.createNativeQueryRunner(storageFormat, sidecarEnabled); + } + + @Override + protected void createTables() + { + NativeTestsUtils.createTables(storageFormat); + } + + /// Sidecar is needed to support custom functions in Presto C++. + @Test + public void testCustomFunctions() + { + if (sidecarEnabled) { + logger.info("Sidecar Enabled"); + // Scalar test + assertQuery( + "SELECT dynamic_custom_add(CAST(pow(2, 3) AS BIGINT), CAST(sqrt(25) AS BIGINT))", + "VALUES 13"); + + // Array test + assertQuery( + "SELECT sum_array(ARRAY[1,2,3])", + "VALUES 6"); + + // Nested array test + assertQuery( + "SELECT sum_nested_array_elements(ARRAY[ARRAY[1,2], ARRAY[3,4,5]])", + "VALUES 15"); + + // Map test + assertQuery( + "SELECT map_size(MAP(ARRAY[1,2], ARRAY[10,20]))", + "VALUES 2"); + } + else { + logger.info("Sidecar Disabled"); + // Scalar test + assertQueryFails( + "SELECT dynamic_custom_add(10, 5)", + "line 1:8: Function dynamic_custom_add not registered"); + + // Array test + assertQueryFails( + "SELECT sum_array(ARRAY[1,2,3])", + "line 1:8: Function sum_array not registered"); + + // Nested array test + assertQueryFails( + "SELECT sum_nested_array_elements(ARRAY[ARRAY[1,2], ARRAY[3,4,5]])", + "line 1:8: Function sum_nested_array_elements not registered"); + + // Map test + assertQueryFails( + "SELECT map_size(MAP(ARRAY[1,2], ARRAY[10,20]))", + "line 1:8: Function map_size not registered"); + } + } +}