1#include "duckdb/common/types/date.hpp"
2#include "duckdb/common/types/time.hpp"
3#include "duckdb/common/types/timestamp.hpp"
4#include "duckdb/common/vector_operations/vector_operations.hpp"
5#include "duckdb.h"
6#include "duckdb.hpp"
7
8#include <cstring>
9
10#ifdef _WIN32
11#define strdup _strdup
12#endif
13
14using namespace duckdb;
15
16static SQLType ConvertCTypeToCPP(duckdb_type type);
17static duckdb_type ConvertCPPTypeToC(SQLType type);
18static idx_t GetCTypeSize(duckdb_type type);
19
20struct DatabaseData {
21 DatabaseData() : database(nullptr) {
22 }
23 ~DatabaseData() {
24 if (database) {
25 delete database;
26 }
27 }
28
29 DuckDB *database;
30};
31
32duckdb_state duckdb_open(const char *path, duckdb_database *out) {
33 auto wrapper = new DatabaseData();
34 try {
35 wrapper->database = new DuckDB(path);
36 } catch (...) {
37 delete wrapper;
38 return DuckDBError;
39 }
40 *out = (duckdb_database)wrapper;
41 return DuckDBSuccess;
42}
43
44void duckdb_close(duckdb_database *database) {
45 if (*database) {
46 auto wrapper = (DatabaseData *)*database;
47 delete wrapper;
48 *database = nullptr;
49 }
50}
51
52duckdb_state duckdb_connect(duckdb_database database, duckdb_connection *out) {
53 auto wrapper = (DatabaseData *)database;
54 Connection *connection;
55 try {
56 connection = new Connection(*wrapper->database);
57 } catch (...) {
58 return DuckDBError;
59 }
60 *out = (duckdb_connection)connection;
61 return DuckDBSuccess;
62}
63
64void duckdb_disconnect(duckdb_connection *connection) {
65 if (*connection) {
66 Connection *conn = (Connection *)*connection;
67 delete conn;
68 *connection = nullptr;
69 }
70}
71
72template <class T> void WriteData(duckdb_result *out, ChunkCollection &source, idx_t col) {
73 idx_t row = 0;
74 auto target = (T *)out->columns[col].data;
75 for (auto &chunk : source.chunks) {
76 auto source = FlatVector::GetData<T>(chunk->data[col]);
77 for (idx_t k = 0; k < chunk->size(); k++) {
78 target[row++] = source[k];
79 }
80 }
81}
82
83static duckdb_state duckdb_translate_result(MaterializedQueryResult *result, duckdb_result *out) {
84 assert(result);
85 if (!out) {
86 // no result to write to, only return the status
87 return result->success ? DuckDBSuccess : DuckDBError;
88 }
89 out->error_message = NULL;
90 if (!result->success) {
91 // write the error message
92 out->error_message = strdup(result->error.c_str());
93 return DuckDBError;
94 }
95 // copy the data
96 // first write the meta data
97 out->column_count = result->types.size();
98 out->row_count = result->collection.count;
99 out->columns = (duckdb_column *)malloc(sizeof(duckdb_column) * out->column_count);
100 if (!out->columns) {
101 return DuckDBError;
102 }
103 // zero initialize the columns (so we can cleanly delete it in case a malloc fails)
104 memset(out->columns, 0, sizeof(duckdb_column) * out->column_count);
105 for (idx_t i = 0; i < out->column_count; i++) {
106 out->columns[i].type = ConvertCPPTypeToC(result->sql_types[i]);
107 out->columns[i].name = strdup(result->names[i].c_str());
108 out->columns[i].nullmask = (bool *)malloc(sizeof(bool) * out->row_count);
109 out->columns[i].data = malloc(GetCTypeSize(out->columns[i].type) * out->row_count);
110 if (!out->columns[i].nullmask || !out->columns[i].name || !out->columns[i].data) {
111 // malloc failure
112 return DuckDBError;
113 }
114 // memset data to 0 for VARCHAR columns for safe deletion later
115 if (result->types[i] == TypeId::VARCHAR) {
116 memset(out->columns[i].data, 0, GetCTypeSize(out->columns[i].type) * out->row_count);
117 }
118 }
119 // now write the data
120 for (idx_t col = 0; col < out->column_count; col++) {
121 // first set the nullmask
122 idx_t row = 0;
123 for (auto &chunk : result->collection.chunks) {
124 for (idx_t k = 0; k < chunk->size(); k++) {
125 out->columns[col].nullmask[row++] = FlatVector::IsNull(chunk->data[col], k);
126 }
127 }
128 // then write the data
129 switch (result->sql_types[col].id) {
130 case SQLTypeId::BOOLEAN:
131 WriteData<bool>(out, result->collection, col);
132 break;
133 case SQLTypeId::TINYINT:
134 WriteData<int8_t>(out, result->collection, col);
135 break;
136 case SQLTypeId::SMALLINT:
137 WriteData<int16_t>(out, result->collection, col);
138 break;
139 case SQLTypeId::INTEGER:
140 WriteData<int32_t>(out, result->collection, col);
141 break;
142 case SQLTypeId::BIGINT:
143 WriteData<int64_t>(out, result->collection, col);
144 break;
145 case SQLTypeId::FLOAT:
146 WriteData<float>(out, result->collection, col);
147 break;
148 case SQLTypeId::DECIMAL:
149 case SQLTypeId::DOUBLE:
150 WriteData<double>(out, result->collection, col);
151 break;
152 case SQLTypeId::VARCHAR: {
153 idx_t row = 0;
154 auto target = (const char **)out->columns[col].data;
155 for (auto &chunk : result->collection.chunks) {
156 auto source = FlatVector::GetData<string_t>(chunk->data[col]);
157 for (idx_t k = 0; k < chunk->size(); k++) {
158 if (!FlatVector::IsNull(chunk->data[col], k)) {
159 target[row] = strdup(source[k].GetData());
160 }
161 row++;
162 }
163 }
164 break;
165 }
166 case SQLTypeId::DATE: {
167 idx_t row = 0;
168 auto target = (duckdb_date *)out->columns[col].data;
169 for (auto &chunk : result->collection.chunks) {
170 auto source = FlatVector::GetData<date_t>(chunk->data[col]);
171 for (idx_t k = 0; k < chunk->size(); k++) {
172 if (!FlatVector::IsNull(chunk->data[col], k)) {
173 int32_t year, month, day;
174 Date::Convert(source[k], year, month, day);
175 target[row].year = year;
176 target[row].month = month;
177 target[row].day = day;
178 }
179 row++;
180 }
181 }
182 break;
183 }
184 case SQLTypeId::TIME: {
185 idx_t row = 0;
186 auto target = (duckdb_time *)out->columns[col].data;
187 for (auto &chunk : result->collection.chunks) {
188 auto source = FlatVector::GetData<dtime_t>(chunk->data[col]);
189 for (idx_t k = 0; k < chunk->size(); k++) {
190 if (!FlatVector::IsNull(chunk->data[col], k)) {
191 int32_t hour, min, sec, msec;
192 Time::Convert(source[k], hour, min, sec, msec);
193 target[row].hour = hour;
194 target[row].min = min;
195 target[row].sec = sec;
196 target[row].msec = msec;
197 }
198 row++;
199 }
200 }
201 break;
202 }
203 case SQLTypeId::TIMESTAMP: {
204 idx_t row = 0;
205 auto target = (duckdb_timestamp *)out->columns[col].data;
206 for (auto &chunk : result->collection.chunks) {
207 auto source = FlatVector::GetData<timestamp_t>(chunk->data[col]);
208 for (idx_t k = 0; k < chunk->size(); k++) {
209 if (!FlatVector::IsNull(chunk->data[col], k)) {
210 date_t date;
211 dtime_t time;
212 Timestamp::Convert(source[k], date, time);
213
214 int32_t year, month, day;
215 Date::Convert(date, year, month, day);
216
217 int32_t hour, min, sec, msec;
218 Time::Convert(time, hour, min, sec, msec);
219
220 target[row].date.year = year;
221 target[row].date.month = month;
222 target[row].date.day = day;
223 target[row].time.hour = hour;
224 target[row].time.min = min;
225 target[row].time.sec = sec;
226 target[row].time.msec = msec;
227 }
228 row++;
229 }
230 }
231 break;
232 }
233 default:
234 // unsupported type for C API
235 assert(0);
236 return DuckDBError;
237 }
238 }
239 return DuckDBSuccess;
240}
241
242duckdb_state duckdb_query(duckdb_connection connection, const char *query, duckdb_result *out) {
243 Connection *conn = (Connection *)connection;
244 auto result = conn->Query(query);
245 return duckdb_translate_result(result.get(), out);
246}
247
248static void duckdb_destroy_column(duckdb_column column, idx_t count) {
249 if (column.data) {
250 if (column.type == DUCKDB_TYPE_VARCHAR) {
251 // varchar, delete individual strings
252 auto data = (char **)column.data;
253 for (idx_t i = 0; i < count; i++) {
254 if (data[i]) {
255 free(data[i]);
256 }
257 }
258 }
259 free(column.data);
260 }
261 if (column.nullmask) {
262 free(column.nullmask);
263 }
264 if (column.name) {
265 free(column.name);
266 }
267}
268
269void duckdb_destroy_result(duckdb_result *result) {
270 if (result->error_message) {
271 free(result->error_message);
272 }
273 if (result->columns) {
274 for (idx_t i = 0; i < result->column_count; i++) {
275 duckdb_destroy_column(result->columns[i], result->row_count);
276 }
277 free(result->columns);
278 }
279 memset(result, 0, sizeof(duckdb_result));
280}
281
282struct PreparedStatementWrapper {
283 PreparedStatementWrapper() : statement(nullptr) {
284 }
285 ~PreparedStatementWrapper() {
286 }
287 unique_ptr<PreparedStatement> statement;
288 vector<Value> values;
289};
290
291duckdb_state duckdb_prepare(duckdb_connection connection, const char *query,
292 duckdb_prepared_statement *out_prepared_statement) {
293 if (!connection || !query) {
294 return DuckDBError;
295 }
296 auto wrapper = new PreparedStatementWrapper();
297 Connection *conn = (Connection *)connection;
298 wrapper->statement = conn->Prepare(query);
299 *out_prepared_statement = (duckdb_prepared_statement)wrapper;
300 return wrapper->statement->success ? DuckDBSuccess : DuckDBError;
301}
302
303duckdb_state duckdb_nparams(duckdb_prepared_statement prepared_statement, idx_t *nparams_out) {
304 auto wrapper = (PreparedStatementWrapper *)prepared_statement;
305 if (!wrapper || !wrapper->statement || !wrapper->statement->success || wrapper->statement->is_invalidated) {
306 return DuckDBError;
307 }
308 *nparams_out = wrapper->statement->n_param;
309 return DuckDBSuccess;
310}
311
312static duckdb_state duckdb_bind_value(duckdb_prepared_statement prepared_statement, idx_t param_idx, Value val) {
313 auto wrapper = (PreparedStatementWrapper *)prepared_statement;
314 if (!wrapper || !wrapper->statement || !wrapper->statement->success || wrapper->statement->is_invalidated) {
315 return DuckDBError;
316 }
317 if (param_idx > wrapper->statement->n_param) {
318 return DuckDBError;
319 }
320 if (param_idx > wrapper->values.size()) {
321 wrapper->values.resize(param_idx);
322 }
323 wrapper->values[param_idx - 1] = val;
324 return DuckDBSuccess;
325}
326
327duckdb_state duckdb_bind_boolean(duckdb_prepared_statement prepared_statement, idx_t param_idx, bool val) {
328 return duckdb_bind_value(prepared_statement, param_idx, Value::BOOLEAN(val));
329}
330
331duckdb_state duckdb_bind_int8(duckdb_prepared_statement prepared_statement, idx_t param_idx, int8_t val) {
332 return duckdb_bind_value(prepared_statement, param_idx, Value::TINYINT(val));
333}
334
335duckdb_state duckdb_bind_int16(duckdb_prepared_statement prepared_statement, idx_t param_idx, int16_t val) {
336 return duckdb_bind_value(prepared_statement, param_idx, Value::SMALLINT(val));
337}
338
339duckdb_state duckdb_bind_int32(duckdb_prepared_statement prepared_statement, idx_t param_idx, int32_t val) {
340 return duckdb_bind_value(prepared_statement, param_idx, Value::INTEGER(val));
341}
342
343duckdb_state duckdb_bind_int64(duckdb_prepared_statement prepared_statement, idx_t param_idx, int64_t val) {
344 return duckdb_bind_value(prepared_statement, param_idx, Value::BIGINT(val));
345}
346
347duckdb_state duckdb_bind_float(duckdb_prepared_statement prepared_statement, idx_t param_idx, float val) {
348 return duckdb_bind_value(prepared_statement, param_idx, Value(val));
349}
350
351duckdb_state duckdb_bind_double(duckdb_prepared_statement prepared_statement, idx_t param_idx, double val) {
352 return duckdb_bind_value(prepared_statement, param_idx, Value(val));
353}
354
355duckdb_state duckdb_bind_varchar(duckdb_prepared_statement prepared_statement, idx_t param_idx, const char *val) {
356 return duckdb_bind_value(prepared_statement, param_idx, Value(val));
357}
358
359duckdb_state duckdb_bind_null(duckdb_prepared_statement prepared_statement, idx_t param_idx) {
360 return duckdb_bind_value(prepared_statement, param_idx, Value());
361}
362
363duckdb_state duckdb_execute_prepared(duckdb_prepared_statement prepared_statement, duckdb_result *out_result) {
364 auto wrapper = (PreparedStatementWrapper *)prepared_statement;
365 if (!wrapper || !wrapper->statement || !wrapper->statement->success || wrapper->statement->is_invalidated) {
366 return DuckDBError;
367 }
368 auto result = wrapper->statement->Execute(wrapper->values, false);
369 assert(result->type == QueryResultType::MATERIALIZED_RESULT);
370 auto mat_res = (MaterializedQueryResult *)result.get();
371 return duckdb_translate_result(mat_res, out_result);
372}
373
374void duckdb_destroy_prepare(duckdb_prepared_statement *prepared_statement) {
375 if (!prepared_statement) {
376 return;
377 }
378 auto wrapper = (PreparedStatementWrapper *)*prepared_statement;
379 if (wrapper) {
380 delete wrapper;
381 }
382 *prepared_statement = nullptr;
383}
384
385duckdb_type ConvertCPPTypeToC(SQLType sql_type) {
386 switch (sql_type.id) {
387 case SQLTypeId::BOOLEAN:
388 return DUCKDB_TYPE_BOOLEAN;
389 case SQLTypeId::TINYINT:
390 return DUCKDB_TYPE_TINYINT;
391 case SQLTypeId::SMALLINT:
392 return DUCKDB_TYPE_SMALLINT;
393 case SQLTypeId::INTEGER:
394 return DUCKDB_TYPE_INTEGER;
395 case SQLTypeId::BIGINT:
396 return DUCKDB_TYPE_BIGINT;
397 case SQLTypeId::FLOAT:
398 return DUCKDB_TYPE_FLOAT;
399 case SQLTypeId::DECIMAL:
400 case SQLTypeId::DOUBLE:
401 return DUCKDB_TYPE_DOUBLE;
402 case SQLTypeId::TIMESTAMP:
403 return DUCKDB_TYPE_TIMESTAMP;
404 case SQLTypeId::DATE:
405 return DUCKDB_TYPE_DATE;
406 case SQLTypeId::TIME:
407 return DUCKDB_TYPE_TIME;
408 case SQLTypeId::VARCHAR:
409 return DUCKDB_TYPE_VARCHAR;
410 default:
411 return DUCKDB_TYPE_INVALID;
412 }
413}
414
415SQLType ConvertCTypeToCPP(duckdb_type type) {
416 switch (type) {
417 case DUCKDB_TYPE_BOOLEAN:
418 return SQLType(SQLTypeId::BOOLEAN);
419 case DUCKDB_TYPE_TINYINT:
420 return SQLType::TINYINT;
421 case DUCKDB_TYPE_SMALLINT:
422 return SQLType::SMALLINT;
423 case DUCKDB_TYPE_INTEGER:
424 return SQLType::INTEGER;
425 case DUCKDB_TYPE_BIGINT:
426 return SQLType::BIGINT;
427 case DUCKDB_TYPE_FLOAT:
428 return SQLType::FLOAT;
429 case DUCKDB_TYPE_DOUBLE:
430 return SQLType::DOUBLE;
431 case DUCKDB_TYPE_TIMESTAMP:
432 return SQLType::TIMESTAMP;
433 case DUCKDB_TYPE_DATE:
434 return SQLType::DATE;
435 case DUCKDB_TYPE_TIME:
436 return SQLType::TIME;
437 case DUCKDB_TYPE_VARCHAR:
438 return SQLType::VARCHAR;
439 default:
440 return SQLType(SQLTypeId::INVALID);
441 }
442}
443
444idx_t GetCTypeSize(duckdb_type type) {
445 switch (type) {
446 case DUCKDB_TYPE_BOOLEAN:
447 return sizeof(bool);
448 case DUCKDB_TYPE_TINYINT:
449 return sizeof(int8_t);
450 case DUCKDB_TYPE_SMALLINT:
451 return sizeof(int16_t);
452 case DUCKDB_TYPE_INTEGER:
453 return sizeof(int32_t);
454 case DUCKDB_TYPE_BIGINT:
455 return sizeof(int64_t);
456 case DUCKDB_TYPE_FLOAT:
457 return sizeof(float);
458 case DUCKDB_TYPE_DOUBLE:
459 return sizeof(double);
460 case DUCKDB_TYPE_DATE:
461 return sizeof(duckdb_date);
462 case DUCKDB_TYPE_TIME:
463 return sizeof(duckdb_time);
464 case DUCKDB_TYPE_TIMESTAMP:
465 return sizeof(duckdb_timestamp);
466 case DUCKDB_TYPE_VARCHAR:
467 return sizeof(const char *);
468 default:
469 // unsupported type
470 assert(0);
471 return sizeof(const char *);
472 }
473}
474
475template <class T> T UnsafeFetch(duckdb_result *result, idx_t col, idx_t row) {
476 assert(row < result->row_count);
477 return ((T *)result->columns[col].data)[row];
478}
479
480static Value GetCValue(duckdb_result *result, idx_t col, idx_t row) {
481 if (col >= result->column_count) {
482 return Value();
483 }
484 if (row >= result->row_count) {
485 return Value();
486 }
487 if (result->columns[col].nullmask[row]) {
488 return Value();
489 }
490 switch (result->columns[col].type) {
491 case DUCKDB_TYPE_BOOLEAN:
492 return Value::BOOLEAN(UnsafeFetch<bool>(result, col, row));
493 case DUCKDB_TYPE_TINYINT:
494 return Value::TINYINT(UnsafeFetch<int8_t>(result, col, row));
495 case DUCKDB_TYPE_SMALLINT:
496 return Value::SMALLINT(UnsafeFetch<int16_t>(result, col, row));
497 case DUCKDB_TYPE_INTEGER:
498 return Value::INTEGER(UnsafeFetch<int32_t>(result, col, row));
499 case DUCKDB_TYPE_BIGINT:
500 return Value::BIGINT(UnsafeFetch<int64_t>(result, col, row));
501 case DUCKDB_TYPE_FLOAT:
502 return Value(UnsafeFetch<float>(result, col, row));
503 case DUCKDB_TYPE_DOUBLE:
504 return Value(UnsafeFetch<double>(result, col, row));
505 case DUCKDB_TYPE_DATE: {
506 auto date = UnsafeFetch<duckdb_date>(result, col, row);
507 return Value::DATE(date.year, date.month, date.day);
508 }
509 case DUCKDB_TYPE_TIME: {
510 auto time = UnsafeFetch<duckdb_time>(result, col, row);
511 return Value::TIME(time.hour, time.min, time.sec, time.msec);
512 }
513 case DUCKDB_TYPE_TIMESTAMP: {
514 auto timestamp = UnsafeFetch<duckdb_timestamp>(result, col, row);
515 return Value::TIMESTAMP(timestamp.date.year, timestamp.date.month, timestamp.date.day, timestamp.time.hour,
516 timestamp.time.min, timestamp.time.sec, timestamp.time.msec);
517 }
518 case DUCKDB_TYPE_VARCHAR:
519 return Value(string(UnsafeFetch<const char *>(result, col, row)));
520 default:
521 // invalid type for C to C++ conversion
522 assert(0);
523 return Value();
524 }
525}
526
527bool duckdb_value_boolean(duckdb_result *result, idx_t col, idx_t row) {
528 Value val = GetCValue(result, col, row);
529 if (val.is_null) {
530 return false;
531 } else {
532 return val.CastAs(TypeId::BOOL).value_.boolean;
533 }
534}
535
536int8_t duckdb_value_int8(duckdb_result *result, idx_t col, idx_t row) {
537 Value val = GetCValue(result, col, row);
538 if (val.is_null) {
539 return 0;
540 } else {
541 return val.CastAs(TypeId::INT8).value_.tinyint;
542 }
543}
544
545int16_t duckdb_value_int16(duckdb_result *result, idx_t col, idx_t row) {
546 Value val = GetCValue(result, col, row);
547 if (val.is_null) {
548 return 0;
549 } else {
550 return val.CastAs(TypeId::INT16).value_.smallint;
551 }
552}
553
554int32_t duckdb_value_int32(duckdb_result *result, idx_t col, idx_t row) {
555 Value val = GetCValue(result, col, row);
556 if (val.is_null) {
557 return 0;
558 } else {
559 return val.CastAs(TypeId::INT32).value_.integer;
560 }
561}
562
563int64_t duckdb_value_int64(duckdb_result *result, idx_t col, idx_t row) {
564 Value val = GetCValue(result, col, row);
565 if (val.is_null) {
566 return 0;
567 } else {
568 return val.CastAs(TypeId::INT64).value_.bigint;
569 }
570}
571
572float duckdb_value_float(duckdb_result *result, idx_t col, idx_t row) {
573 Value val = GetCValue(result, col, row);
574 if (val.is_null) {
575 return 0.0;
576 } else {
577 return val.CastAs(TypeId::FLOAT).value_.float_;
578 }
579}
580
581double duckdb_value_double(duckdb_result *result, idx_t col, idx_t row) {
582 Value val = GetCValue(result, col, row);
583 if (val.is_null) {
584 return 0.0;
585 } else {
586 return val.CastAs(TypeId::DOUBLE).value_.double_;
587 }
588}
589
590char *duckdb_value_varchar(duckdb_result *result, idx_t col, idx_t row) {
591 Value val = GetCValue(result, col, row);
592 return strdup(val.ToString(ConvertCTypeToCPP(result->columns[col].type)).c_str());
593}
594