Skip to content

Commit ca4bcf1

Browse files
committed
fix: improve error messages
1 parent e5ed40b commit ca4bcf1

File tree

3 files changed

+94
-20
lines changed

3 files changed

+94
-20
lines changed

src/adbc_functions.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ static int64_t CreateConnection(const vector<pair<string, string>> &options) {
7070
// Set driver (required)
7171
database->SetOption("driver", driver);
7272

73+
// Store driver name for error messages
74+
database->SetDriverName(driver);
75+
7376
// Set entrypoint if provided
7477
if (!entrypoint.empty()) {
7578
database->SetOption("entrypoint", entrypoint);

src/include/adbc_connection.hpp

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ class AdbcDatabaseWrapper {
2121
memset(&database, 0, sizeof(database));
2222
}
2323

24+
// Set driver name for error messages (call before Initialize)
25+
void SetDriverName(const string &driver_path) {
26+
driver_name = ExtractDriverName(driver_path);
27+
}
28+
29+
const string &GetDriverName() const {
30+
return driver_name;
31+
}
32+
2433
~AdbcDatabaseWrapper() {
2534
Release();
2635
}
@@ -37,13 +46,13 @@ class AdbcDatabaseWrapper {
3746
void SetOption(const string &key, const string &value) {
3847
AdbcErrorGuard error;
3948
auto status = AdbcDatabaseSetOption(&database, key.c_str(), value.c_str(), error.Get());
40-
CheckAdbc(status, error.Get(), "Failed to set database option '" + key + "'");
49+
CheckAdbc(status, error.Get(), "Failed to set database option '" + key + "'", driver_name);
4150
}
4251

4352
void Initialize() {
4453
AdbcErrorGuard error;
4554
auto status = AdbcDatabaseInit(&database, error.Get());
46-
CheckAdbc(status, error.Get(), "Failed to initialize ADBC database");
55+
CheckAdbc(status, error.Get(), "Failed to initialize ADBC database", driver_name);
4756
initialized = true;
4857
}
4958

@@ -77,6 +86,7 @@ class AdbcDatabaseWrapper {
7786
private:
7887
AdbcDatabase database;
7988
bool initialized;
89+
string driver_name;
8090
};
8191

8292
// RAII wrapper for AdbcConnection
@@ -93,19 +103,19 @@ class AdbcConnectionWrapper {
93103
void Init() {
94104
AdbcErrorGuard error;
95105
auto status = AdbcConnectionNew(&connection, error.Get());
96-
CheckAdbc(status, error.Get(), "Failed to create ADBC connection");
106+
CheckAdbc(status, error.Get(), "Failed to create ADBC connection", GetDriverName());
97107
}
98108

99109
void SetOption(const string &key, const string &value) {
100110
AdbcErrorGuard error;
101111
auto status = AdbcConnectionSetOption(&connection, key.c_str(), value.c_str(), error.Get());
102-
CheckAdbc(status, error.Get(), "Failed to set connection option '" + key + "'");
112+
CheckAdbc(status, error.Get(), "Failed to set connection option '" + key + "'", GetDriverName());
103113
}
104114

105115
void Initialize() {
106116
AdbcErrorGuard error;
107117
auto status = AdbcConnectionInit(&connection, database->Get(), error.Get());
108-
CheckAdbc(status, error.Get(), "Failed to initialize ADBC connection");
118+
CheckAdbc(status, error.Get(), "Failed to initialize ADBC connection", GetDriverName());
109119
initialized = true;
110120
}
111121

@@ -129,12 +139,16 @@ class AdbcConnectionWrapper {
129139
return database;
130140
}
131141

142+
const string &GetDriverName() const {
143+
return database->GetDriverName();
144+
}
145+
132146
// Get connection info (vendor name, driver version, etc.)
133147
// info_codes can be NULL to get all info, or an array of specific codes
134148
void GetInfo(const uint32_t *info_codes, size_t info_codes_length, ArrowArrayStream *out) {
135149
AdbcErrorGuard error;
136150
auto status = AdbcConnectionGetInfo(&connection, info_codes, info_codes_length, out, error.Get());
137-
CheckAdbc(status, error.Get(), "Failed to get connection info");
151+
CheckAdbc(status, error.Get(), "Failed to get connection info", GetDriverName());
138152
}
139153

140154
// Get database objects (catalogs, schemas, tables, columns)
@@ -146,14 +160,14 @@ class AdbcConnectionWrapper {
146160
AdbcErrorGuard error;
147161
auto status = AdbcConnectionGetObjects(&connection, depth, catalog, db_schema,
148162
table_name, table_types, column_name, out, error.Get());
149-
CheckAdbc(status, error.Get(), "Failed to get database objects");
163+
CheckAdbc(status, error.Get(), "Failed to get database objects", GetDriverName());
150164
}
151165

152166
// Get table types (e.g., "TABLE", "VIEW", etc.)
153167
void GetTableTypes(ArrowArrayStream *out) {
154168
AdbcErrorGuard error;
155169
auto status = AdbcConnectionGetTableTypes(&connection, out, error.Get());
156-
CheckAdbc(status, error.Get(), "Failed to get table types");
170+
CheckAdbc(status, error.Get(), "Failed to get table types", GetDriverName());
157171
}
158172

159173
// Get the Arrow schema for a specific table
@@ -162,7 +176,7 @@ class AdbcConnectionWrapper {
162176
AdbcErrorGuard error;
163177
auto status = AdbcConnectionGetTableSchema(&connection, catalog, db_schema,
164178
table_name, schema, error.Get());
165-
CheckAdbc(status, error.Get(), "Failed to get table schema");
179+
CheckAdbc(status, error.Get(), "Failed to get table schema", GetDriverName());
166180
}
167181

168182
// Non-copyable
@@ -189,42 +203,42 @@ class AdbcStatementWrapper {
189203
void Init() {
190204
AdbcErrorGuard error;
191205
auto status = AdbcStatementNew(connection->Get(), &statement, error.Get());
192-
CheckAdbc(status, error.Get(), "Failed to create ADBC statement");
206+
CheckAdbc(status, error.Get(), "Failed to create ADBC statement", GetDriverName());
193207
initialized = true;
194208
}
195209

196210
void SetSqlQuery(const string &query) {
197211
AdbcErrorGuard error;
198212
auto status = AdbcStatementSetSqlQuery(&statement, query.c_str(), error.Get());
199-
CheckAdbc(status, error.Get(), "Failed to set SQL query");
213+
CheckAdbc(status, error.Get(), "Failed to set SQL query", GetDriverName());
200214
}
201215

202216
void SetOption(const string &key, const string &value) {
203217
AdbcErrorGuard error;
204218
auto status = AdbcStatementSetOption(&statement, key.c_str(), value.c_str(), error.Get());
205-
CheckAdbc(status, error.Get(), "Failed to set statement option '" + key + "'");
219+
CheckAdbc(status, error.Get(), "Failed to set statement option '" + key + "'", GetDriverName());
206220
}
207221

208222
void Prepare() {
209223
AdbcErrorGuard error;
210224
auto status = AdbcStatementPrepare(&statement, error.Get());
211-
CheckAdbc(status, error.Get(), "Failed to prepare statement");
225+
CheckAdbc(status, error.Get(), "Failed to prepare statement", GetDriverName());
212226
}
213227

214228
// Bind parameters to the statement (Arrow format)
215229
// The statement takes ownership of the values/schema
216230
void Bind(ArrowArray *values, ArrowSchema *schema) {
217231
AdbcErrorGuard error;
218232
auto status = AdbcStatementBind(&statement, values, schema, error.Get());
219-
CheckAdbc(status, error.Get(), "Failed to bind parameters");
233+
CheckAdbc(status, error.Get(), "Failed to bind parameters", GetDriverName());
220234
}
221235

222236
// Bind an Arrow stream for bulk ingestion
223237
// The statement takes ownership of the stream
224238
void BindStream(ArrowArrayStream *stream) {
225239
AdbcErrorGuard error;
226240
auto status = AdbcStatementBindStream(&statement, stream, error.Get());
227-
CheckAdbc(status, error.Get(), "Failed to bind stream");
241+
CheckAdbc(status, error.Get(), "Failed to bind stream", GetDriverName());
228242
}
229243

230244
// Get the result schema without executing the query (requires Prepare first)
@@ -235,22 +249,22 @@ class AdbcStatementWrapper {
235249
if (status == ADBC_STATUS_NOT_IMPLEMENTED) {
236250
return false;
237251
}
238-
CheckAdbc(status, error.Get(), "Failed to get result schema");
252+
CheckAdbc(status, error.Get(), "Failed to get result schema", GetDriverName());
239253
return true;
240254
}
241255

242256
// Execute and return the ArrowArrayStream - caller takes ownership
243257
void ExecuteQuery(ArrowArrayStream *out, int64_t *rows_affected = nullptr) {
244258
AdbcErrorGuard error;
245259
auto status = AdbcStatementExecuteQuery(&statement, out, rows_affected, error.Get());
246-
CheckAdbc(status, error.Get(), "Failed to execute query");
260+
CheckAdbc(status, error.Get(), "Failed to execute query", GetDriverName());
247261
}
248262

249263
// Execute without expecting a result set (for bulk ingestion)
250264
void ExecuteUpdate(int64_t *rows_affected = nullptr) {
251265
AdbcErrorGuard error;
252266
auto status = AdbcStatementExecuteQuery(&statement, nullptr, rows_affected, error.Get());
253-
CheckAdbc(status, error.Get(), "Failed to execute update");
267+
CheckAdbc(status, error.Get(), "Failed to execute update", GetDriverName());
254268
}
255269

256270
// Cancel any in-progress query (best effort - ignores errors)
@@ -278,6 +292,10 @@ class AdbcStatementWrapper {
278292
return connection;
279293
}
280294

295+
const string &GetDriverName() const {
296+
return connection->GetDriverName();
297+
}
298+
281299
// Non-copyable
282300
AdbcStatementWrapper(const AdbcStatementWrapper &) = delete;
283301
AdbcStatementWrapper &operator=(const AdbcStatementWrapper &) = delete;

src/include/adbc_utils.hpp

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,21 @@ inline const char *StatusCodeToString(AdbcStatusCode code) {
1515
}
1616

1717
// Check ADBC status and throw DuckDB exception on error
18-
inline void CheckAdbc(AdbcStatusCode status, AdbcError *error, const string &context) {
18+
// Optional driver_name parameter to include in error messages for better debugging
19+
inline void CheckAdbc(AdbcStatusCode status, AdbcError *error, const string &context,
20+
const string &driver_name = "") {
1921
if (status == ADBC_STATUS_OK) {
2022
return;
2123
}
2224

23-
string message = context + ": ";
25+
string message;
26+
27+
// Include driver name if provided
28+
if (!driver_name.empty()) {
29+
message = "[" + driver_name + "] ";
30+
}
31+
32+
message += context + ": ";
2433
if (error && error->message) {
2534
message += error->message;
2635
// Add SQLSTATE if available
@@ -57,6 +66,50 @@ inline void CheckAdbc(AdbcStatusCode status, AdbcError *error, const string &con
5766
}
5867
}
5968

69+
// Extract a short driver name from a full path
70+
// e.g., "/path/to/libadbc_driver_sqlite.dylib" -> "sqlite"
71+
inline string ExtractDriverName(const string &driver_path) {
72+
// Find the last path separator
73+
size_t last_sep = driver_path.find_last_of("/\\");
74+
string filename = (last_sep != string::npos) ? driver_path.substr(last_sep + 1) : driver_path;
75+
76+
// Try to extract driver name from common patterns
77+
// Pattern: libadbc_driver_<name>.so/dylib/dll
78+
size_t driver_pos = filename.find("adbc_driver_");
79+
if (driver_pos != string::npos) {
80+
size_t name_start = driver_pos + 12; // length of "adbc_driver_"
81+
size_t name_end = filename.find_first_of(".", name_start);
82+
if (name_end != string::npos) {
83+
return filename.substr(name_start, name_end - name_start);
84+
}
85+
return filename.substr(name_start);
86+
}
87+
88+
// Pattern: <name>_driver.so/dylib/dll
89+
driver_pos = filename.find("_driver");
90+
if (driver_pos != string::npos) {
91+
// Remove lib prefix if present
92+
size_t name_start = 0;
93+
if (filename.substr(0, 3) == "lib") {
94+
name_start = 3;
95+
}
96+
return filename.substr(name_start, driver_pos - name_start);
97+
}
98+
99+
// Fallback: use filename without extension
100+
size_t dot_pos = filename.find('.');
101+
if (dot_pos != string::npos) {
102+
string name = filename.substr(0, dot_pos);
103+
// Remove lib prefix if present
104+
if (name.substr(0, 3) == "lib") {
105+
name = name.substr(3);
106+
}
107+
return name;
108+
}
109+
110+
return filename;
111+
}
112+
60113
// RAII wrapper for AdbcError - ensures proper cleanup
61114
class AdbcErrorGuard {
62115
public:

0 commit comments

Comments
 (0)