ADBC
Arrow Database Connectivity
Loading...
Searching...
No Matches
status.h
Go to the documentation of this file.
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#pragma once
19
20#include <cassert>
21#include <cstring>
22#include <memory>
23#include <sstream>
24#include <string>
25#include <utility>
26#include <variant>
27#include <vector>
28
29#include <fmt/core.h>
30#include <fmt/format.h>
31
32#include <arrow-adbc/adbc.h>
33
35
36namespace adbc::driver {
37
42class Status {
43 public:
45 Status() : impl_(nullptr) {}
46
48 explicit Status(AdbcStatusCode code, std::string message)
49 : Status(code, std::move(message), {}) {}
50
52 explicit Status(AdbcStatusCode code, const char* message)
53 : Status(code, std::string(message), {}) {}
54
56 explicit Status(AdbcStatusCode code, std::string message,
57 std::vector<std::pair<std::string, std::string>> details)
58 : impl_(std::make_unique<Impl>(code, std::move(message), std::move(details))) {
59 assert(code != ADBC_STATUS_OK);
60 }
61
63 bool ok() const { return impl_ == nullptr; }
64
66 void AddDetail(std::string key, std::string value) {
67 assert(impl_ != nullptr);
68 impl_->details.push_back({std::move(key), std::move(value)});
69 }
70
72 void SetSqlState(std::string sqlstate) {
73 assert(impl_ != nullptr);
74 std::memset(impl_->sql_state, 0, sizeof(impl_->sql_state));
75 for (size_t i = 0; i < sqlstate.size(); i++) {
76 if (i >= sizeof(impl_->sql_state)) {
77 break;
78 }
79
80 impl_->sql_state[i] = sqlstate[i];
81 }
82 }
83
84 std::string message() const { return impl_ ? impl_->message : ""; }
85
87 AdbcStatusCode ToAdbc(AdbcError* adbc_error) const {
88 if (impl_ == nullptr) return ADBC_STATUS_OK;
89 if (adbc_error == nullptr) return impl_->code;
90
91 if (adbc_error->release) {
92 adbc_error->release(adbc_error);
93 }
94
96 auto error_owned_by_adbc_error =
97 new Status(impl_->code, std::move(impl_->message), std::move(impl_->details));
98 adbc_error->message =
99 const_cast<char*>(error_owned_by_adbc_error->impl_->message.c_str());
100 adbc_error->private_data = error_owned_by_adbc_error;
101 } else {
102 adbc_error->message = new char[impl_->message.size() + 1];
103 if (adbc_error->message != nullptr) {
104 std::memcpy(adbc_error->message, impl_->message.c_str(),
105 impl_->message.size() + 1);
106 }
107 }
108
109 std::memcpy(adbc_error->sqlstate, impl_->sql_state, sizeof(impl_->sql_state));
110 adbc_error->release = &CRelease;
111 return impl_->code;
112 }
113
114 static Status FromAdbc(AdbcStatusCode code, AdbcError& error) {
115 // not really meant to be used, just something we have for now while porting
116 if (code == ADBC_STATUS_OK) {
117 if (error.release) {
118 error.release(&error);
119 }
120 return Status();
121 }
122 auto status = Status(code, error.message ? error.message : "(unknown error)");
123 if (error.release) {
124 error.release(&error);
125 }
126 return status;
127 }
128
129 // Helpers to create statuses with known codes
130 static Status Ok() { return Status(); }
131
132#define STATUS_CTOR(NAME, CODE) \
133 template <typename... Args> \
134 [[maybe_unused]] static Status NAME(Args&&... args) { \
135 std::stringstream ss; \
136 ([&] { ss << args; }(), ...); \
137 return Status(ADBC_STATUS_##CODE, ss.str()); \
138 }
139
140 STATUS_CTOR(Internal, INTERNAL)
141 STATUS_CTOR(InvalidArgument, INVALID_ARGUMENT)
142 STATUS_CTOR(InvalidState, INVALID_STATE)
143 STATUS_CTOR(IO, IO)
144 STATUS_CTOR(NotFound, NOT_FOUND)
145 STATUS_CTOR(NotImplemented, NOT_IMPLEMENTED)
146 STATUS_CTOR(Unknown, UNKNOWN)
147
148#undef STATUS_CTOR
149
150 private:
152 struct Impl {
153 // invariant: code is never OK
154 AdbcStatusCode code;
155 std::string message;
156 std::vector<std::pair<std::string, std::string>> details;
157 char sql_state[5];
158
159 explicit Impl(AdbcStatusCode code, std::string message,
160 std::vector<std::pair<std::string, std::string>> details)
161 : code(code), message(std::move(message)), details(std::move(details)) {
162 std::memset(sql_state, 0, sizeof(sql_state));
163 }
164 };
165 // invariant: code is OK iff impl_ is nullptr
166 std::unique_ptr<Impl> impl_;
167
168 // Let the Driver use these to expose C callables wrapping option setters/getters
169 template <typename DatabaseT, typename ConnectionT, typename StatementT>
170 friend class Driver;
171
172 // Allow access to these for drivers transitioning to the framework
173 public:
174 int CDetailCount() const { return impl_ ? static_cast<int>(impl_->details.size()) : 0; }
175
176 AdbcErrorDetail CDetail(int index) const {
177 if (!impl_ || index < 0 || static_cast<size_t>(index) >= impl_->details.size()) {
178 return {nullptr, nullptr, 0};
179 }
180 const auto& detail = impl_->details[index];
181 return {detail.first.c_str(), reinterpret_cast<const uint8_t*>(detail.second.data()),
182 detail.second.size()};
183 }
184
185 private:
186 static void CRelease(AdbcError* error) {
188 auto* error_obj = reinterpret_cast<Status*>(error->private_data);
189 delete error_obj;
190 std::memset(error, 0, ADBC_ERROR_1_1_0_SIZE);
191 } else {
192 delete[] error->message;
193 std::memset(error, 0, ADBC_ERROR_1_0_0_SIZE);
194 }
195 }
196};
197
203template <typename T>
204class Result {
205 public:
207 Result(Status s) // NOLINT(runtime/explicit)
208 : value_(std::move(s)) {
209 assert(!std::get<Status>(value_).ok());
210 }
211
212 template <typename U,
213 // Allow things that can construct T, not just T itself, but
214 // disallow T from being Status so which constructor to use is not
215 // ambiguous.
216 typename E = typename std::enable_if<
217 std::is_constructible<T, U>::value && std::is_convertible<U, T>::value &&
218 !std::is_same<typename std::remove_reference<
219 typename std::remove_cv<U>::type>::type,
220 Status>::value>::type>
221 Result(U&& t) : value_(std::forward<U>(t)) {} // NOLINT(runtime/explicit)
222
224 bool has_value() const { return !std::holds_alternative<Status>(value_); }
225
227 const Status& status() const& {
228 assert(std::holds_alternative<Status>(value_));
229 return std::get<Status>(value_);
230 }
231
233 Status&& status() && {
234 assert(std::holds_alternative<Status>(value_));
235 return std::move(std::get<Status>(value_));
236 }
237
239 T& value() {
240 assert(!std::holds_alternative<Status>(value_));
241 return std::get<T>(value_);
242 }
243
244 private:
245 std::variant<Status, T> value_;
246};
247
248#define RESULT_NAME_SUFFIX _adbc_framework_result
249#define RAISE_RESULT_IMPL(NAME, ERROR, LHS, RHS) \
250 do { \
251 auto&& NAME = (RHS); \
252 if (!(NAME).has_value()) { \
253 return (NAME).status().ToAdbc(ERROR); \
254 } \
255 LHS = std::move((NAME).value()); \
256 } while (0);
257
258#define RAISE_STATUS_IMPL(NAME, ERROR, RHS) \
259 do { \
260 auto&& NAME = (RHS); \
261 if (!(NAME).ok()) { \
262 return (NAME).ToAdbc(ERROR); \
263 } \
264 } while (0);
265
266#define UNWRAP_RESULT_IMPL(name, lhs, rhs) \
267 do { \
268 auto&& name = (rhs); \
269 if (!(name).has_value()) { \
270 return std::move(name).status(); \
271 } \
272 lhs = std::move((name).value()); \
273 } while (0);
274
275#define UNWRAP_STATUS_IMPL(name, rhs) \
276 do { \
277 auto&& name = (rhs); \
278 if (!(name).ok()) { \
279 return std::move(name); \
280 } \
281 } while (0);
282
283#define DRIVER_CONCAT(x, y) x##y
284#define UNWRAP_RESULT_NAME(x, y) DRIVER_CONCAT(x, y)
285
287#define RAISE_RESULT(ERROR, LHS, RHS) \
288 RAISE_RESULT_IMPL(UNWRAP_RESULT_NAME(driver_raise_result, RESULT_NAME_SUFFIX), ERROR, \
289 LHS, RHS)
290
291#define RAISE_STATUS(ERROR, RHS) \
292 RAISE_STATUS_IMPL(UNWRAP_RESULT_NAME(driver_raise_status, RESULT_NAME_SUFFIX), ERROR, \
293 RHS)
294
295#define UNWRAP_RESULT(lhs, rhs) \
296 UNWRAP_RESULT_IMPL(UNWRAP_RESULT_NAME(driver_unwrap_result, RESULT_NAME_SUFFIX), lhs, \
297 rhs)
298
299#define UNWRAP_STATUS(rhs) \
300 UNWRAP_STATUS_IMPL(UNWRAP_RESULT_NAME(driver_unwrap_status, RESULT_NAME_SUFFIX), rhs)
301
302} // namespace adbc::driver
303
304namespace adbc::driver::status {
305
306inline driver::Status Ok() { return driver::Status(); }
307
308#define STATUS_CTOR(NAME, CODE) \
309 template <typename... Args> \
310 [[maybe_unused]] static Status NAME(Args&&... args) { \
311 std::stringstream ss; \
312 ([&] { ss << args; }(), ...); \
313 return Status(ADBC_STATUS_##CODE, ss.str()); \
314 }
315
316// TODO: unit tests for internal utilities
317STATUS_CTOR(Internal, INTERNAL)
318STATUS_CTOR(InvalidArgument, INVALID_ARGUMENT)
319STATUS_CTOR(InvalidState, INVALID_STATE)
320STATUS_CTOR(IO, IO)
321STATUS_CTOR(NotFound, NOT_FOUND)
322STATUS_CTOR(NotImplemented, NOT_IMPLEMENTED)
323STATUS_CTOR(Unknown, UNKNOWN)
324
325#undef STATUS_CTOR
326
327} // namespace adbc::driver::status
328
329namespace adbc::driver::status::fmt {
330
331#define STATUS_CTOR(NAME, CODE) \
332 template <typename... Args> \
333 [[maybe_unused]] static Status NAME(std::string_view format_string, Args&&... args) { \
334 auto message = ::fmt::vformat(format_string, ::fmt::make_format_args(args...)); \
335 return Status(ADBC_STATUS_##CODE, std::move(message)); \
336 }
337
338// TODO: unit tests for internal utilities
339STATUS_CTOR(Internal, INTERNAL)
340STATUS_CTOR(InvalidArgument, INVALID_ARGUMENT)
341STATUS_CTOR(InvalidState, INVALID_STATE)
342STATUS_CTOR(IO, IO)
343STATUS_CTOR(Integrity, INTEGRITY)
344STATUS_CTOR(NotFound, NOT_FOUND)
345STATUS_CTOR(NotImplemented, NOT_IMPLEMENTED)
346STATUS_CTOR(Unknown, UNKNOWN)
347
348#undef STATUS_CTOR
349
350} // namespace adbc::driver::status::fmt
351
352#define UNWRAP_ERRNO_IMPL(NAME, CODE, RHS) \
353 do { \
354 auto&& NAME = (RHS); \
355 if (NAME != 0) { \
356 return adbc::driver::status::CODE("Call failed: ", #RHS, " = (errno ", NAME, ") ", \
357 std::strerror(NAME)); \
358 } \
359 } while (0);
360
361#define UNWRAP_ERRNO(CODE, RHS) \
362 UNWRAP_ERRNO_IMPL(UNWRAP_RESULT_NAME(driver_errno, RESULT_NAME_SUFFIX), CODE, RHS)
363
364#define UNWRAP_NANOARROW_IMPL(NAME, ERROR, CODE, RHS) \
365 do { \
366 auto&& NAME = (RHS); \
367 if (NAME != 0) { \
368 return adbc::driver::status::CODE("nanoarrow call failed: ", #RHS, " = (", NAME, \
369 ") ", std::strerror(NAME), ". ", \
370 (ERROR).message); \
371 } \
372 } while (0);
373
374#define UNWRAP_NANOARROW(ERROR, CODE, RHS) \
375 UNWRAP_NANOARROW_IMPL(UNWRAP_RESULT_NAME(driver_errno_na, RESULT_NAME_SUFFIX), ERROR, \
376 CODE, RHS)
Status && status() &&
Move the status (if present).
Definition status.h:233
Result(U &&t)
Implicit constructor to allow returning a value in functions.
Definition status.h:221
bool has_value() const
Check if this has a value or not.
Definition status.h:224
Result(Status s)
Implicit constructor to allow returning a status in functions.
Definition status.h:207
T & value()
Get the value (if present).
Definition status.h:239
const Status & status() const &
Get the status (if present).
Definition status.h:227
A wrapper around AdbcStatusCode + AdbcError.
Definition status.h:42
Status(AdbcStatusCode code, std::string message, std::vector< std::pair< std::string, std::string > > details)
Construct a non-OK status with a message and details.
Definition status.h:56
Status()
Construct an OK status.
Definition status.h:45
AdbcStatusCode ToAdbc(AdbcError *adbc_error) const
Export this status to an AdbcError.
Definition status.h:87
bool ok() const
Check if this is an error or not.
Definition status.h:63
Status(AdbcStatusCode code, std::string message)
Construct a non-OK status with a message.
Definition status.h:48
void AddDetail(std::string key, std::string value)
Add another error detail.
Definition status.h:66
Status(AdbcStatusCode code, const char *message)
Construct a non-OK status with a message.
Definition status.h:52
void SetSqlState(std::string sqlstate)
Set the sqlstate of this status.
Definition status.h:72
int32_t vendor_code
A vendor-specific error code, if applicable.
Definition adbc.h:288
void * private_data
Opaque implementation-defined state.
Definition adbc.h:308
char * message
The error message.
Definition adbc.h:285
char sqlstate[5]
A SQLSTATE error code, if provided, as defined by the SQL:2003 standard. If not set,...
Definition adbc.h:293
void(*) release(struct AdbcError *error)
Release the contained error.
Definition adbc.h:299
#define ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA
Inform the driver/driver manager that we are using the extended AdbcError struct from ADBC 1....
Definition adbc.h:271
uint8_t AdbcStatusCode
Error codes for operations that may fail.
Definition adbc.h:176
#define ADBC_ERROR_1_1_0_SIZE
The size of the AdbcError structure in ADBC 1.1.0.
Definition adbc.h:356
#define ADBC_STATUS_OK
No error.
Definition adbc.h:179
#define ADBC_ERROR_1_0_0_SIZE
The size of the AdbcError structure in ADBC 1.0.0.
Definition adbc.h:347
A detailed error message for an operation.
Definition adbc.h:283
Extra key-value metadata for an error.
Definition adbc.h:365
Private Status implementation details.
Definition status.h:152