be/src/util/adbc_driver_registry.cpp
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | #include "util/adbc_driver_registry.h" |
19 | | |
20 | | #include <arrow-adbc/adbc_driver_manager.h> |
21 | | #include <glog/logging.h> |
22 | | #include <stdlib.h> |
23 | | |
24 | | #include <memory> |
25 | | #include <mutex> |
26 | | #include <string> |
27 | | #include <utility> |
28 | | |
29 | | #include "common/check.h" |
30 | | |
31 | | namespace doris { |
32 | | |
33 | | namespace { |
34 | | |
35 | 21 | std::string resolve_path(const std::string& driver_path) { |
36 | | // A driver that is not on disk yet still needs a stable cache key, so keep the original string. |
37 | 21 | std::unique_ptr<char, decltype(&free)> resolved(realpath(driver_path.c_str(), nullptr), &free); |
38 | 21 | if (resolved == nullptr) { |
39 | 4 | return driver_path; |
40 | 4 | } |
41 | 17 | return {resolved.get()}; |
42 | 21 | } |
43 | | |
44 | | // Drivers own the strings they put in AdbcError, so every populated error must be released. |
45 | 4 | std::string take_error_message(AdbcError* error) { |
46 | 4 | DORIS_CHECK(error != nullptr); |
47 | 4 | std::string message = error->message != nullptr ? error->message : ""; |
48 | 4 | if (error->release != nullptr) { |
49 | 3 | error->release(error); |
50 | 3 | } |
51 | 4 | return message; |
52 | 4 | } |
53 | | |
54 | | } // namespace |
55 | | |
56 | 28 | AdbcDriverRegistry& AdbcDriverRegistry::instance() { |
57 | | // Allocated and never freed, on purpose. A plain function-local static is destroyed during |
58 | | // static destruction, and this registry must outlive that: it hands out AdbcDriver pointers |
59 | | // documented to stay valid for the life of the process, and it holds each driver's |
60 | | // manager-side state, which only the driver's release callback frees -- a call this registry |
61 | | // never makes, because dlclosing a driver that owns background threads is a use-after-free. |
62 | | // Destroying the map would therefore drop the last reference to memory that stays live anyway, |
63 | | // which is also exactly what LeakSanitizer reports at exit (its check runs after every static |
64 | | // destructor). |
65 | 28 | static auto* registry = new AdbcDriverRegistry(); |
66 | 28 | return *registry; |
67 | 28 | } |
68 | | |
69 | | Status AdbcDriverRegistry::get_or_load(const std::string& driver_path, |
70 | 22 | const std::string& entrypoint, const AdbcDriver** out) { |
71 | 22 | DORIS_CHECK(out != nullptr); |
72 | 22 | if (driver_path.empty()) { |
73 | 1 | return Status::InvalidArgument("ADBC: driver path is empty"); |
74 | 1 | } |
75 | | |
76 | 21 | const std::string key = resolve_path(driver_path); |
77 | | |
78 | 21 | std::lock_guard<std::mutex> lock(_mutex); |
79 | 21 | auto it = _drivers.find(key); |
80 | 21 | if (it != _drivers.end()) { |
81 | 17 | if (!it->second.loaded) { |
82 | 1 | return it->second.load_status; |
83 | 1 | } |
84 | 16 | *out = &it->second.driver; |
85 | 16 | return Status::OK(); |
86 | 17 | } |
87 | | |
88 | 4 | Entry entry; |
89 | 4 | AdbcError error = ADBC_ERROR_INIT; |
90 | 4 | const AdbcStatusCode code = |
91 | 4 | AdbcLoadDriver(key.c_str(), entrypoint.empty() ? nullptr : entrypoint.c_str(), |
92 | 4 | ADBC_VERSION_1_1_0, &entry.driver, &error); |
93 | 4 | if (code != ADBC_STATUS_OK) { |
94 | 3 | const std::string message = take_error_message(&error); |
95 | | // The path is what the user controls, so it has to be in the message for them to fix it. |
96 | | // AdbcStatusCode is a uint8_t, so spell out the name as well as the number. |
97 | 3 | entry.load_status = Status::InternalError( |
98 | 3 | "ADBC: failed to load driver '{}' ({}, code {}): {}", driver_path, |
99 | 3 | AdbcStatusCodeMessage(code), static_cast<int>(code), |
100 | 3 | message.empty() ? "no error message from the driver manager" : message); |
101 | 3 | LOG(WARNING) << entry.load_status; |
102 | 3 | return _drivers.emplace(key, std::move(entry)).first->second.load_status; |
103 | 3 | } |
104 | | // A successful load can still leave a warning behind, and it is the driver's memory to free. |
105 | 1 | take_error_message(&error); |
106 | 1 | entry.loaded = true; |
107 | | |
108 | 1 | *out = &_drivers.emplace(key, std::move(entry)).first->second.driver; |
109 | 1 | return Status::OK(); |
110 | 4 | } |
111 | | |
112 | 6 | size_t AdbcDriverRegistry::loaded_count() const { |
113 | 6 | std::lock_guard<std::mutex> lock(_mutex); |
114 | 6 | return _drivers.size(); |
115 | 6 | } |
116 | | |
117 | | } // namespace doris |