diff --git a/velox/common/CMakeLists.txt b/velox/common/CMakeLists.txt index 82f0ca23b969..6661427654be 100644 --- a/velox/common/CMakeLists.txt +++ b/velox/common/CMakeLists.txt @@ -15,6 +15,7 @@ add_subdirectory(base) add_subdirectory(caching) add_subdirectory(compression) add_subdirectory(config) +add_subdirectory(dynamic_registry) add_subdirectory(encode) add_subdirectory(file) add_subdirectory(hyperloglog) diff --git a/velox/common/dynamic_registry/CMakeLists.txt b/velox/common/dynamic_registry/CMakeLists.txt new file mode 100644 index 000000000000..31b37df70832 --- /dev/null +++ b/velox/common/dynamic_registry/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# 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. + +velox_add_library(velox_dynamic_library_loader DynamicLibraryLoader.cpp) + +velox_link_libraries(velox_dynamic_library_loader PRIVATE velox_exception) + +if(${VELOX_BUILD_TESTING}) + add_subdirectory(tests) +endif() diff --git a/velox/common/dynamic_registry/DynamicLibraryLoader.cpp b/velox/common/dynamic_registry/DynamicLibraryLoader.cpp new file mode 100644 index 000000000000..24a41664e512 --- /dev/null +++ b/velox/common/dynamic_registry/DynamicLibraryLoader.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 +#include "velox/common/base/Exceptions.h" + +namespace facebook::velox { + +static constexpr const char* kSymbolName = "registry"; + +void loadDynamicLibrary(const char* fileName) { + // Try to dynamically load the shared library. + void* handler = dlopen(fileName, RTLD_NOW); + + if (handler == nullptr) { + VELOX_USER_FAIL("Error while loading shared library: {}", dlerror()); + } + + // Lookup the symbol. + void* registrySymbol = dlsym(handler, kSymbolName); + auto loadUserLibrary = reinterpret_cast(registrySymbol); + const char* error = dlerror(); + + if (error != nullptr) { + VELOX_USER_FAIL("Couldn't find Velox registry symbol: {}", error); + } + // Invoke the registry function. + loadUserLibrary(); + LOG(INFO) << "Loaded: " << fileName; +} + +} // namespace facebook::velox diff --git a/velox/common/dynamic_registry/DynamicLibraryLoader.h b/velox/common/dynamic_registry/DynamicLibraryLoader.h new file mode 100644 index 000000000000..09a6abd4811e --- /dev/null +++ b/velox/common/dynamic_registry/DynamicLibraryLoader.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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. + */ + +#pragma once + +namespace facebook::velox { + +/// Dynamically opens and registers functions defined in a shared library. +/// +/// The library being linked needs to provide a function with the following +/// signature: +/// +/// void registry(); +/// +/// The registration function needs to be defined in the root-level namespace, +/// and be enclosed by a extern "C" directive to prevent the compiler from +/// mangling the symbol name. + +/// The function uses dlopen to load the shared library. +/// It then searches for the "void registry()" C function which typically +/// contains all the registration code for the UDFs defined in library. After +/// locating the function it executes the registration bringing the +/// user-defined Velox components such as function in the scope of the +/// Velox runtime. +/// +/// Loading a library twice can cause a components to be registered twice. +/// This can fail for certain Velox components. + +void loadDynamicLibrary(const char* fileName); + +} // namespace facebook::velox diff --git a/velox/common/dynamic_registry/DynamicUdf.h b/velox/common/dynamic_registry/DynamicUdf.h new file mode 100644 index 000000000000..c852316eebfa --- /dev/null +++ b/velox/common/dynamic_registry/DynamicUdf.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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. + */ + +// This header must be included in any dynamically loaded user-defined +// function library. It enforces the correct function declaration with +// the proper signature. +#pragma once + +#include "velox/functions/Udf.h" + +extern "C" { +// The "registry()" declaration and "check()" function ensure the correct +// registry signature function is defined by the user. +void registry(); +void check() { + registry(); +} +} diff --git a/velox/common/dynamic_registry/README.md b/velox/common/dynamic_registry/README.md new file mode 100644 index 000000000000..dcbc7f01c81f --- /dev/null +++ b/velox/common/dynamic_registry/README.md @@ -0,0 +1,34 @@ +# Dynamic Loading of Velox Extensions + +This generic utility adds extensibility features to load User Defined Functions (UDFs), connectors, or types without having to fork and build Velox, through the use of shared libraries. + +## Getting started +1. Create a cpp file for your dynamic library +For dynamically loaded function registration, the format followed is mirrored of that of built-in function registration with some noted differences. Using [MyDynamicTestFunction.cpp](tests/MyDynamicTestFunction.cpp) as an example, the function uses the extern "C" keyword to protect against name mangling. A registry() function call is also necessary here. +Make sure to also include the necessary header file: + ``` + #include "velox/functions/DynamicUdf.h" + ``` + +2. Register functions dynamically by creating .dylib (MacOS) or .so (Linux) shared libraries. +These shared libraries may be made using CMakeLists like the following: + + ``` + add_library(name_of_dynamic_fn SHARED TestFunction.cpp) + target_link_libraries(name_of_dynamic_fn PRIVATE fmt::fmt glog::glog xsimd) + target_link_options(name_of_dynamic_fn PRIVATE "-Wl,-undefined,dynamic_lookup") + ``` + Above, the `fmt::fmt` and `xsimd` libraries are required for all necessary symbols + to be defined when loading the `TestFunction.cpp` dynamically. + Additionally `glog::glog` is currently required on MacOs. + The `target_link_options` allows for symbols to be resolved at runtime on MacOS. + + On Linux `glog::glog` and the `target_link_options` are optional. + +## Notes + In Velox, a function's signature is determined solely by its name and argument types. + Return type is not taken into account. As a result, if a function with an identical + signature is added but with a different return type, it will overwrite the existing function. + + Function overloading is supported therefore multiple functions can share the same name as + long as they differ in the number or types of arguments. \ No newline at end of file diff --git a/velox/common/dynamic_registry/tests/CMakeLists.txt b/velox/common/dynamic_registry/tests/CMakeLists.txt new file mode 100644 index 000000000000..e6d6446f670c --- /dev/null +++ b/velox/common/dynamic_registry/tests/CMakeLists.txt @@ -0,0 +1,93 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# 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. + +# To test functions being added by dynamically linked libraries, we compile +# `MyDynamicFunction.cpp` as a small .so library, and use the +# VELOX_TEST_DYNAMIC_LIBRARY_PATH macro to locate the .so binary. + +add_library(velox_function_my_dynamic SHARED MyDynamicFunction.cpp) +add_library(velox_overwrite_int_function_my_dynamic SHARED + MyDynamicIntFunctionOverwrite.cpp) +add_library(velox_overwrite_varchar_function_my_dynamic SHARED + MyDynamicVarcharFunctionOverwrite.cpp) +add_library(velox_function_err_my_dynamic SHARED MyDynamicErrFunction.cpp) +add_library(velox_overload_int_function_my_dynamic SHARED + MyDynamicIntFunctionOverload.cpp) +add_library(velox_overload_varchar_function_my_dynamic SHARED + MyDynamicVarcharFunctionOverload.cpp) + +set(CMAKE_DYLIB_TEST_LINK_LIBRARIES fmt::fmt glog::glog xsimd) + +target_link_libraries( + velox_function_my_dynamic + PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES}) + +target_link_libraries( + velox_overwrite_int_function_my_dynamic + PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES}) + +target_link_libraries( + velox_overwrite_varchar_function_my_dynamic + PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES}) + +target_link_libraries( + velox_function_err_my_dynamic + PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES}) + +target_link_libraries( + velox_overload_int_function_my_dynamic + PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES}) + +target_link_libraries( + velox_overload_varchar_function_my_dynamic + PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES}) + +set(COMMON_LIBRARY_LINK_OPTIONS "-Wl,-undefined,dynamic_lookup") + +target_link_options(velox_function_my_dynamic PRIVATE + ${COMMON_LIBRARY_LINK_OPTIONS}) +target_link_options(velox_overwrite_int_function_my_dynamic PRIVATE + ${COMMON_LIBRARY_LINK_OPTIONS}) +target_link_options(velox_overwrite_varchar_function_my_dynamic PRIVATE + ${COMMON_LIBRARY_LINK_OPTIONS}) +target_link_options(velox_function_err_my_dynamic PRIVATE + ${COMMON_LIBRARY_LINK_OPTIONS}) +target_link_options(velox_overload_int_function_my_dynamic PRIVATE + ${COMMON_LIBRARY_LINK_OPTIONS}) +target_link_options(velox_overload_varchar_function_my_dynamic PRIVATE + ${COMMON_LIBRARY_LINK_OPTIONS}) + +# Here's the actual test which will dynamically load the library defined above. +add_executable(velox_function_dynamic_link_test DynamicLinkTest.cpp) + +target_link_libraries( + velox_function_dynamic_link_test + velox_functions_test_lib + velox_dynamic_library_loader + velox_function_registry + xsimd + GTest::gmock + GTest::gtest + GTest::gtest_main) + +target_compile_definitions( + velox_function_dynamic_link_test + PRIVATE VELOX_TEST_DYNAMIC_LIBRARY_PATH="${CMAKE_CURRENT_BINARY_DIR}") +target_compile_definitions( + velox_function_dynamic_link_test + PRIVATE + VELOX_TEST_DYNAMIC_LIBRARY_PATH_SUFFIX="${CMAKE_SHARED_LIBRARY_SUFFIX}") + +add_test(NAME velox_function_dynamic_link_test + COMMAND velox_function_dynamic_link_test) diff --git a/velox/common/dynamic_registry/tests/DynamicLinkTest.cpp b/velox/common/dynamic_registry/tests/DynamicLinkTest.cpp new file mode 100644 index 000000000000..69e4e99e78ce --- /dev/null +++ b/velox/common/dynamic_registry/tests/DynamicLinkTest.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/common/base/tests/GTestUtils.h" +#include "velox/common/dynamic_registry/DynamicLibraryLoader.h" +#include "velox/expression/SimpleFunctionRegistry.h" +#include "velox/functions/FunctionRegistry.h" +#include "velox/functions/prestosql/tests/utils/FunctionBaseTest.h" + +namespace facebook::velox::functions::test { + +class DynamicLinkTest : public FunctionBaseTest {}; + +std::string getLibraryPath(const std::string& filename) { + return fmt::format( + "{}/{}{}", + VELOX_TEST_DYNAMIC_LIBRARY_PATH, + filename, + VELOX_TEST_DYNAMIC_LIBRARY_PATH_SUFFIX); +} + +TEST_F(DynamicLinkTest, dynamicLoadFunc) { + const auto dynamicFunction = [&](std::optional a) { + return evaluateOnce("dynamic()", a); + }; + + const auto dynamicFunctionNestedCall = [&](std::optional a) { + return evaluateOnce("mod(dynamic(), 10)", a); + }; + + auto signaturesBefore = getFunctionSignatures().size(); + + VELOX_ASSERT_THROW( + dynamicFunction(0), "Scalar function doesn't exist: dynamic."); + + std::string libraryPath = getLibraryPath("libvelox_function_my_dynamic"); + loadDynamicLibrary(libraryPath.data()); + auto signaturesAfter = getFunctionSignatures().size(); + EXPECT_EQ(signaturesAfter, signaturesBefore + 1); + EXPECT_EQ(123, dynamicFunction(0)); + + auto& registry = exec::simpleFunctions(); + auto resolved = registry.resolveFunction("dynamic", {}); + EXPECT_EQ(TypeKind::BIGINT, resolved->type()->kind()); + + EXPECT_EQ(3, dynamicFunctionNestedCall(0)); +} + +TEST_F(DynamicLinkTest, dynamicLoadSameFuncTwice) { + const auto dynamicFunction = [&](std::optional a) { + return evaluateOnce("dynamic()", a); + }; + auto& registry = exec::simpleFunctions(); + std::string libraryPath = getLibraryPath("libvelox_function_my_dynamic"); + + loadDynamicLibrary(libraryPath.data()); + auto resolved = registry.resolveFunction("dynamic", {}); + EXPECT_EQ(TypeKind::BIGINT, resolved->type()->kind()); + + auto signaturesBefore = getFunctionSignatures().size(); + + loadDynamicLibrary(libraryPath.data()); + auto signaturesAfterSecond = getFunctionSignatures().size(); + EXPECT_EQ(signaturesAfterSecond, signaturesBefore); + auto resolvedAfterSecond = registry.resolveFunction("dynamic", {}); + EXPECT_EQ(TypeKind::BIGINT, resolvedAfterSecond->type()->kind()); +} + +TEST_F(DynamicLinkTest, dynamicLoadOverwriteFunc) { + const auto dynamicIntFunction = [&]() { + return evaluateOnce("dynamic_overwrite()", makeRowVector(ROW({}), 1)); + }; + const auto dynamicVarcharFunction = [&]() { + return evaluateOnce("dynamic_overwrite()", makeRowVector(ROW({}), 1)); + }; + + auto& registry = exec::simpleFunctions(); + auto signaturesBefore = getFunctionSignatures().size(); + + VELOX_ASSERT_THROW( + dynamicVarcharFunction(), + "Scalar function doesn't exist: dynamic_overwrite."); + + std::string libraryPath = + getLibraryPath("libvelox_overwrite_varchar_function_my_dynamic"); + loadDynamicLibrary(libraryPath.data()); + auto signaturesAfterFirst = getFunctionSignatures().size(); + EXPECT_EQ(signaturesAfterFirst, signaturesBefore + 1); + EXPECT_EQ("123", dynamicVarcharFunction()); + auto resolved = registry.resolveFunction("dynamic_overwrite", {}); + EXPECT_EQ(TypeKind::VARCHAR, resolved->type()->kind()); + + VELOX_ASSERT_THROW( + dynamicIntFunction(), + "Expression evaluation result is not of expected type: dynamic_overwrite() -> CONSTANT vector of type VARCHAR"); + + std::string libraryPathInt = + getLibraryPath("libvelox_overwrite_int_function_my_dynamic"); + loadDynamicLibrary(libraryPathInt.data()); + + // The first function loaded should be overwritten. + VELOX_ASSERT_THROW( + dynamicVarcharFunction(), + "Expression evaluation result is not of expected type: dynamic_overwrite() -> CONSTANT vector of type BIGINT"); + EXPECT_EQ(123, dynamicIntFunction()); + auto signaturesAfterSecond = getFunctionSignatures().size(); + EXPECT_EQ(signaturesAfterSecond, signaturesAfterFirst); + auto resolvedAfterSecond = registry.resolveFunction("dynamic_overwrite", {}); + EXPECT_EQ(TypeKind::BIGINT, resolvedAfterSecond->type()->kind()); +} + +TEST_F(DynamicLinkTest, dynamicLoadErrFunc) { + const auto dynamicFunctionFail = [&](const std::optional a, + std::optional b) { + return evaluateOnce("dynamic_err(c0)", a, b); + }; + + const auto dynamicFunctionSuccess = + [&](const facebook::velox::RowVectorPtr& arr) { + return evaluateOnce("dynamic_err(c0)", arr); + }; + + auto signaturesBefore = getFunctionSignatures().size(); + VELOX_ASSERT_THROW( + dynamicFunctionFail(0, 0), "Scalar function doesn't exist: dynamic_err."); + + std::string libraryPath = getLibraryPath("libvelox_function_err_my_dynamic"); + loadDynamicLibrary(libraryPath.data()); + + auto signaturesAfter = getFunctionSignatures().size(); + EXPECT_EQ(signaturesAfter, signaturesBefore + 1); + + // Expecting a fail because we are not passing in an array. + VELOX_ASSERT_THROW( + dynamicFunctionFail(0, 0), + "Scalar function signature is not supported: dynamic_err(BIGINT). Supported signatures: (array(bigint)) -> bigint."); + + auto check = makeRowVector( + {makeNullableArrayVector(std::vector>>{ + {0, 1, 3, 4, 5, 6, 7, 8, 9}})}); + + // Expecting a success because we are passing in an array. + EXPECT_EQ(9, dynamicFunctionSuccess(check)); +} + +TEST_F(DynamicLinkTest, dynamicLoadOverloadFunc) { + const auto dynamicIntFunction = [&](const std::optional a) { + return evaluateOnce("dynamic_overload(c0)", a); + }; + const auto dynamicVarcharFunction = [&](const std::optional a) { + return evaluateOnce("dynamic_overload(c0)", a); + }; + + auto& registry = exec::simpleFunctions(); + auto signaturesBefore = getFunctionSignatures().size(); + + VELOX_ASSERT_THROW( + dynamicVarcharFunction("1"), + "Scalar function doesn't exist: dynamic_overload."); + + std::string libraryPath = + getLibraryPath("libvelox_overload_varchar_function_my_dynamic"); + loadDynamicLibrary(libraryPath.data()); + auto signaturesAfterFirst = getFunctionSignatures().size(); + EXPECT_EQ(signaturesAfterFirst, signaturesBefore + 1); + EXPECT_EQ("1", dynamicVarcharFunction("1")); + auto resolved = registry.resolveFunction("dynamic_overload", {VARCHAR()}); + EXPECT_EQ(TypeKind::VARCHAR, resolved->type()->kind()); + + // We expect no dynamic_5 with int arguments yet. + VELOX_ASSERT_THROW( + dynamicIntFunction(0), + "Scalar function signature is not supported: dynamic_overload(BIGINT). Supported signatures: (varchar) -> varchar."); + + std::string libraryPathInt = + getLibraryPath("libvelox_overload_int_function_my_dynamic"); + loadDynamicLibrary(libraryPathInt.data()); + + // The first function loaded should NOT be overwritten. + EXPECT_EQ("0", dynamicVarcharFunction("0")); + EXPECT_EQ(0, dynamicIntFunction(0)); + auto signaturesAfterSecond = getFunctionSignatures().size(); + EXPECT_EQ(signaturesAfterSecond, signaturesAfterFirst); + auto resolvedAfterSecond = + registry.resolveFunction("dynamic_overload", {BIGINT()}); + EXPECT_EQ(TypeKind::BIGINT, resolvedAfterSecond->type()->kind()); +} + +} // namespace facebook::velox::functions::test diff --git a/velox/common/dynamic_registry/tests/MyDynamicErrFunction.cpp b/velox/common/dynamic_registry/tests/MyDynamicErrFunction.cpp new file mode 100644 index 000000000000..6ba799ac87d1 --- /dev/null +++ b/velox/common/dynamic_registry/tests/MyDynamicErrFunction.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/common/dynamic_registry/DynamicUdf.h" + +// This file defines a mock function that will be dynamically linked and +// registered. There are no restrictions as to how the function needs to be +// defined, but the library (.so) needs to provide a `void registry()` C +// function in the top-level namespace. +// +// (note the extern "C" directive to prevent the compiler from mangling the +// symbol name). + +namespace facebook::velox::common::dynamicRegistry { + +template +struct Dynamic123Function { + VELOX_DEFINE_FUNCTION_TYPES(T); + FOLLY_ALWAYS_INLINE bool call( + int64_t& result, + const arg_type>& array) { + result = array.size(); + return true; + } +}; +} // namespace facebook::velox::common::dynamicRegistry + +extern "C" { +void registry() { + facebook::velox::registerFunction< + facebook::velox::common::dynamicRegistry::Dynamic123Function, + int64_t, + facebook::velox::Array>({"dynamic_err"}); +} +} diff --git a/velox/common/dynamic_registry/tests/MyDynamicFunction.cpp b/velox/common/dynamic_registry/tests/MyDynamicFunction.cpp new file mode 100644 index 000000000000..e932e9e7b07b --- /dev/null +++ b/velox/common/dynamic_registry/tests/MyDynamicFunction.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/common/dynamic_registry/DynamicUdf.h" + +// This file defines a mock function that will be dynamically linked and +// registered. There are no restrictions as to how the function needs to be +// defined, but the library (.so) needs to provide a `void registry()` C +// function in the top-level namespace. +// +// (note the extern "C" directive to prevent the compiler from mangling the +// symbol name). + +namespace facebook::velox::common::dynamicRegistry { + +template +struct Dynamic123Function { + FOLLY_ALWAYS_INLINE bool call(int64_t& result) { + result = 123; + return true; + } +}; + +} // namespace facebook::velox::common::dynamicRegistry + +extern "C" { +// In this case, we assume that facebook::velox::registerFunction +// will be available and resolve when this library gets loaded. +void registry() { + facebook::velox::registerFunction< + facebook::velox::common::dynamicRegistry::Dynamic123Function, + int64_t>({"dynamic"}); +} +} diff --git a/velox/common/dynamic_registry/tests/MyDynamicIntFunctionOverload.cpp b/velox/common/dynamic_registry/tests/MyDynamicIntFunctionOverload.cpp new file mode 100644 index 000000000000..f86d68369aff --- /dev/null +++ b/velox/common/dynamic_registry/tests/MyDynamicIntFunctionOverload.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/common/dynamic_registry/DynamicUdf.h" + +// This file defines a mock function that will be dynamically linked and +// registered. There are no restrictions as to how the function needs to be +// defined, but the library (.so) needs to provide a `void registry()` C +// function in the top-level namespace. +// +// (note the extern "C" directive to prevent the compiler from mangling the +// symbol name). + +namespace facebook::velox::common::dynamicRegistry { + +template +struct Dynamic123Function { + VELOX_DEFINE_FUNCTION_TYPES(T); + FOLLY_ALWAYS_INLINE bool call(int64_t& result, const arg_type& in) { + result = in; + return true; + } +}; + +} // namespace facebook::velox::common::dynamicRegistry + +extern "C" { + +void registry() { + facebook::velox::registerFunction< + facebook::velox::common::dynamicRegistry::Dynamic123Function, + int64_t, + int64_t>({"dynamic_overload"}); +} +} diff --git a/velox/common/dynamic_registry/tests/MyDynamicIntFunctionOverwrite.cpp b/velox/common/dynamic_registry/tests/MyDynamicIntFunctionOverwrite.cpp new file mode 100644 index 000000000000..d70055904579 --- /dev/null +++ b/velox/common/dynamic_registry/tests/MyDynamicIntFunctionOverwrite.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/common/dynamic_registry/DynamicUdf.h" + +// This file defines a mock function that will be dynamically linked and +// registered. There are no restrictions as to how the function needs to be +// defined, but the library (.so) needs to provide a `void registry()` C +// function in the top-level namespace. +// +// (note the extern "C" directive to prevent the compiler from mangling the +// symbol name). + +namespace facebook::velox::common::dynamicRegistry { + +template +struct Dynamic123Function { + FOLLY_ALWAYS_INLINE bool call(int64_t& result) { + result = 123; + return true; + } +}; + +} // namespace facebook::velox::common::dynamicRegistry + +extern "C" { + +void registry() { + facebook::velox::registerFunction< + facebook::velox::common::dynamicRegistry::Dynamic123Function, + int64_t>({"dynamic_overwrite"}); +} +} diff --git a/velox/common/dynamic_registry/tests/MyDynamicVarcharFunctionOverload.cpp b/velox/common/dynamic_registry/tests/MyDynamicVarcharFunctionOverload.cpp new file mode 100644 index 000000000000..56245f0a9fc6 --- /dev/null +++ b/velox/common/dynamic_registry/tests/MyDynamicVarcharFunctionOverload.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/common/dynamic_registry/DynamicUdf.h" + +// This file defines a mock function that will be dynamically linked and +// registered. There are no restrictions as to how the function needs to be +// defined, but the library (.so) needs to provide a `void registry()` C +// function in the top-level namespace. +// +// (note the extern "C" directive to prevent the compiler from mangling the +// symbol name). + +namespace facebook::velox::common::dynamicRegistry { + +template +struct Dynamic123Function { + VELOX_DEFINE_FUNCTION_TYPES(T); + FOLLY_ALWAYS_INLINE bool call( + out_type& result, + const arg_type& in) { + result = in; + return true; + } +}; + +} // namespace facebook::velox::common::dynamicRegistry + +extern "C" { + +void registry() { + facebook::velox::registerFunction< + facebook::velox::common::dynamicRegistry::Dynamic123Function, + facebook::velox::Varchar, + facebook::velox::Varchar>({"dynamic_overload"}); +} +} diff --git a/velox/common/dynamic_registry/tests/MyDynamicVarcharFunctionOverwrite.cpp b/velox/common/dynamic_registry/tests/MyDynamicVarcharFunctionOverwrite.cpp new file mode 100644 index 000000000000..b861326667c9 --- /dev/null +++ b/velox/common/dynamic_registry/tests/MyDynamicVarcharFunctionOverwrite.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/common/dynamic_registry/DynamicUdf.h" + +// This file defines a mock function that will be dynamically linked and +// registered. There are no restrictions as to how the function needs to be +// defined, but the library (.so) needs to provide a `void registry()` C +// function in the top-level namespace. +// +// (note the extern "C" directive to prevent the compiler from mangling the +// symbol name). + +namespace facebook::velox::common::dynamicRegistry { + +template +struct Dynamic123Function { + VELOX_DEFINE_FUNCTION_TYPES(T); + FOLLY_ALWAYS_INLINE bool call(out_type& result) { + result = "123"; + return true; + } +}; + +} // namespace facebook::velox::common::dynamicRegistry + +extern "C" { + +void registry() { + facebook::velox::registerFunction< + facebook::velox::common::dynamicRegistry::Dynamic123Function, + facebook::velox::Varchar>({"dynamic_overwrite"}); +} +}