Skip to content

Commit

Permalink
dylib presto changes + presto-docs + readme + examples
Browse files Browse the repository at this point in the history
  • Loading branch information
soumiiow committed Feb 19, 2025
1 parent d7c0930 commit ec86c3d
Show file tree
Hide file tree
Showing 16 changed files with 394 additions and 0 deletions.
1 change: 1 addition & 0 deletions presto-docs/src/main/sphinx/presto-cpp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Note: Presto C++ is in active development. See :doc:`Limitations </presto_cpp/li

presto_cpp/features
presto_cpp/limitations
presto_cpp/plugin
presto_cpp/properties
presto_cpp/properties-session

Expand Down
21 changes: 21 additions & 0 deletions presto-docs/src/main/sphinx/presto_cpp/plugin.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
*******************
Presto C++ Plugins
*******************

This chapter outlines the plugins in Presto C++ that are available for various use cases such as to load User Defined Functions (UDFs), connectors, or types.

.. toctree::
:maxdepth: 1

plugin/function_plugin


Setup
-----

1. Place the plugin shared libraries in the ``plugins`` directory.

2. Set the ``plugin.dir`` property to the path of the ``plugins`` directory in the ``config.properties`` file of each of your workers.

3. Start or restart the coordinator and workers to pick up any placed libraries.

45 changes: 45 additions & 0 deletions presto-docs/src/main/sphinx/presto_cpp/plugin/function_plugin.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
=======================
Function Plugin
=======================

Creating a Shared Library for UDFs
----------------------------------
User defined functions (UDFs) allow users to create custom functions without the need to rebuild the executable.
There are many benefits to UDFs, such as:

* Simplify SQL queries by creating UDFs for repetitive logic.
* Implement custom logic pertaining to the specific business use cases of the users.
* Once defined, easily reusable and called multiple times just like built in functions.
* Shorter compile times.

1. To create the UDF, create a new C++ file in the following format:

.. code-block:: c++

#include "presto_cpp/main/dynamic_registry/DynamicFunctionRegistrar.h"

template <typename T>
struct NameOfStruct {
FOLLY_ALWAYS_INLINE bool call(int64_t& result) {
...
}
};

extern "C" {
void registry() {
facebook::presto::registerPrestoFunction<
nameOfStruct,
int64_t>("function_name");
}
}

Note: the ``int64_t`` return type can be changed as needed. For more examples, see the `README <https://github.com/prestodb/presto-native-execution/main/dynamic_registry/README.md>`_.

2. Create a shared library which may be made using CMakeLists.txt like the following:

.. code-block:: text
add_library(name_of_dynamic_fn SHARED TestFunction.cpp)
target_link_libraries(name_of_dynamic_fn PRIVATE fmt::fmt Folly::folly gflags::gflags)
3. Place your shared libraries in the plugin directory. The path to this directory needs to be the same as ``plugin.dir`` property set in :doc:`../plugin`.
12 changes: 12 additions & 0 deletions presto-native-execution/presto_cpp/main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ add_subdirectory(types)
add_subdirectory(http)
add_subdirectory(common)
add_subdirectory(thrift)
add_subdirectory(dynamic_registry)

add_library(
presto_server_lib
Expand Down Expand Up @@ -62,9 +63,11 @@ target_link_libraries(
velox_dwio_orc_reader
velox_dwio_parquet_reader
velox_dwio_parquet_writer
velox_dynamic_library_loader
velox_encode
velox_exec
velox_file
velox_function_registry
velox_functions_lib
velox_functions_prestosql
velox_gcs
Expand Down Expand Up @@ -100,6 +103,15 @@ set_property(TARGET presto_server_lib PROPERTY JOB_POOL_LINK

add_executable(presto_server PrestoMain.cpp)

# The below additional flags are necessary for resolving dependencies for
# loading dynamic libraries.
if(APPLE)
target_link_options(presto_server BEFORE PUBLIC
"-Wl,-undefined,dynamic_lookup")
else()
target_link_options(presto_server BEFORE PUBLIC "-Wl,-export-dynamic")
endif()

# Moving velox_hive_connector and velox_tpch_connector to presto_server_lib
# results in multiple link errors similar to the one below only on GCC.
# "undefined reference to `vtable for velox::connector::tpch::TpchTableHandle`"
Expand Down
25 changes: 25 additions & 0 deletions presto-native-execution/presto_cpp/main/PrestoServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "velox/common/base/StatsReporter.h"
#include "velox/common/caching/CacheTTLController.h"
#include "velox/common/caching/SsdCache.h"
#include "velox/common/dynamic_registry/DynamicLibraryLoader.h"
#include "velox/common/file/FileSystems.h"
#include "velox/common/memory/MmapAllocator.h"
#include "velox/common/memory/SharedArbitrator.h"
Expand Down Expand Up @@ -397,6 +398,7 @@ void PrestoServer::run() {
registerRemoteFunctions();
registerVectorSerdes();
registerPrestoPlanNodeSerDe();
registerDynamicFunctions();

const auto numExchangeHttpClientIoThreads = std::max<size_t>(
systemConfig->exchangeHttpClientNumIoThreadsHwMultiplier() *
Expand Down Expand Up @@ -1600,5 +1602,28 @@ protocol::NodeStatus PrestoServer::fetchNodeStatus() {

return nodeStatus;
}
void PrestoServer::registerDynamicFunctions() {
auto systemConfig = SystemConfig::instance();
if (!systemConfig->pluginDir().empty()) {
// If user provided path is valid, traverse and call dynamic function loader
// for all shared libraries.
const fs::path path(systemConfig->pluginDir());
PRESTO_STARTUP_LOG(INFO) << path;
std::error_code ec;
if (fs::is_directory(path, ec)) {
using recursiveDirectoryIterator =
std::filesystem::recursive_directory_iterator;
std::set<std::string> extensions{".so", ".dylib"};
for (const auto& dirEntry : recursiveDirectoryIterator(path)) {
// Skip any non shared library files and directories from loading.
auto dirEntryPath = dirEntry.path();
if (!fs::is_directory(dirEntry, ec) &&
extensions.find(dirEntryPath.extension()) != extensions.end()) {
velox::loadDynamicLibrary(dirEntryPath.c_str());
}
}
}
}
}

} // namespace facebook::presto
2 changes: 2 additions & 0 deletions presto-native-execution/presto_cpp/main/PrestoServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ class PrestoServer {

VeloxPlanValidator* getVeloxPlanValidator();

virtual void registerDynamicFunctions();

/// Invoked to get the list of filters passed to the http server.
std::vector<std::unique_ptr<proxygen::RequestHandlerFactory>>
getHttpServerFilters();
Expand Down
5 changes: 5 additions & 0 deletions presto-native-execution/presto_cpp/main/common/Configs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ SystemConfig::SystemConfig() {
BOOL_PROP(kEnableRuntimeMetricsCollection, false),
BOOL_PROP(kPlanValidatorFailOnNestedLoopJoin, false),
STR_PROP(kPrestoDefaultNamespacePrefix, "presto.default"),
STR_PROP(kPluginDir, ""),
};
}

Expand Down Expand Up @@ -763,6 +764,10 @@ std::string SystemConfig::prestoDefaultNamespacePrefix() const {
return optionalProperty(kPrestoDefaultNamespacePrefix).value().append(".");
}

std::string SystemConfig::pluginDir() const {
return optionalProperty(kPluginDir).value();
}

NodeConfig::NodeConfig() {
registeredProps_ =
std::unordered_map<std::string, folly::Optional<std::string>>{
Expand Down
5 changes: 5 additions & 0 deletions presto-native-execution/presto_cpp/main/common/Configs.h
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,9 @@ class SystemConfig : public ConfigBase {
static constexpr std::string_view kInternalCommunicationJwtExpirationSeconds{
"internal-communication.jwt.expiration-seconds"};

/// Optional string containing the path to the plugin directory
static constexpr std::string_view kPluginDir{"plugin.dir"};

/// Below are the Presto properties from config.properties that get converted
/// to their velox counterparts in BaseVeloxQueryConfig and used solely from
/// BaseVeloxQueryConfig.
Expand Down Expand Up @@ -898,6 +901,8 @@ class SystemConfig : public ConfigBase {

bool prestoNativeSidecar() const;
std::string prestoDefaultNamespacePrefix() const;

std::string pluginDir() const;
};

/// Provides access to node properties defined in node.properties file.
Expand Down
Original file line number Diff line number Diff line change
@@ -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(examples)
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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

#include "presto_cpp/main/common/Configs.h"
#include "velox/functions/Macros.h"
#include "velox/functions/Registerer.h"

namespace facebook::presto {
template <template <class> class T, typename TReturn, typename... TArgs>
void registerPrestoFunction(
const char* name,
const char* nameSpace = "",
const std::vector<velox::exec::SignatureVariable>& constraints = {},
bool overwrite = true) {
std::string cppNamespace(nameSpace);
if (cppNamespace.empty()) {
auto systemConfig = SystemConfig::instance();
cppNamespace = systemConfig->prestoDefaultNamespacePrefix();
}
if (cppNamespace.back() != '.') {
cppNamespace.append(".");
}
std::string cppName(cppNamespace);
cppName.append(name);
LOG(INFO) << "registering function: " << cppName;
facebook::velox::registerFunction<T, TReturn, TArgs...>(
{cppName}, constraints, overwrite);
}
} // namespace facebook::presto
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Read [here](https://prestodb.io/docs/current/presto-cpp/plugin.html) on how to use the Dynamic Library Loader.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# 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_library(presto_function_my_dynamic SHARED MyDynamicFunction.cpp)
add_library(presto_varchar_function_my_dynamic SHARED
MyDynamicVarcharFunction.cpp)
add_library(presto_array_function_my_dynamic SHARED MyDynamicArrayFunction.cpp)
add_library(presto_non_defualt_function_my_dynamic SHARED
MyDynamicNonDefaultFunction.cpp)

set(CMAKE_DYLIB_TEST_LINK_LIBRARIES fmt::fmt gflags::gflags xsimd)
target_link_libraries(presto_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})
target_link_libraries(presto_varchar_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})
target_link_libraries(presto_array_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})
target_link_libraries(presto_non_defualt_function_my_dynamic
PRIVATE ${CMAKE_DYLIB_TEST_LINK_LIBRARIES})

if(APPLE)
set(COMMON_LIBRARY_LINK_OPTIONS "-Wl,-undefined,dynamic_lookup")
target_link_options(presto_function_my_dynamic PRIVATE
${COMMON_LIBRARY_LINK_OPTIONS})
target_link_options(presto_varchar_function_my_dynamic PRIVATE
${COMMON_LIBRARY_LINK_OPTIONS})
target_link_options(presto_array_function_my_dynamic PRIVATE
${COMMON_LIBRARY_LINK_OPTIONS})
target_link_options(presto_non_defualt_function_my_dynamic PRIVATE
${COMMON_LIBRARY_LINK_OPTIONS})
else()
set(COMMON_LIBRARY_LINK_OPTIONS "-Wl,--exclude-libs,ALL")
endif()
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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 "presto_cpp/main/dynamic_registry/DynamicFunctionRegistrar.h"
#include "velox/type/SimpleFunctionApi.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 <typename T>
struct DynamicFunction {
VELOX_DEFINE_FUNCTION_TYPES(T);
FOLLY_ALWAYS_INLINE bool call(
int64_t& result,
const arg_type<Array<int64_t>>& array) {
result = array.size();
return true;
}
};
} // namespace facebook::velox::common::dynamicRegistry

extern "C" {
void registry() {
facebook::presto::registerPrestoFunction<
facebook::velox::common::dynamicRegistry::DynamicFunction,
int64_t,
facebook::velox::Array<int64_t>>({"dynamic_array"});
}
}
Loading

0 comments on commit ec86c3d

Please sign in to comment.