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