duckdb 0.8.2-dev2842.0 → 0.8.2-dev3007.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/binding.gyp +1 -0
- package/package.json +1 -1
- package/src/duckdb/extension/json/include/json_deserializer.hpp +1 -1
- package/src/duckdb/extension/json/include/json_serializer.hpp +1 -1
- package/src/duckdb/extension/json/json_deserializer.cpp +7 -5
- package/src/duckdb/extension/json/json_serializer.cpp +2 -3
- package/src/duckdb/src/common/adbc/adbc.cpp +400 -145
- package/src/duckdb/src/common/adbc/driver_manager.cpp +79 -31
- package/src/duckdb/src/common/adbc/nanoarrow/allocator.cpp +57 -0
- package/src/duckdb/src/common/adbc/nanoarrow/metadata.cpp +121 -0
- package/src/duckdb/src/common/adbc/nanoarrow/schema.cpp +474 -0
- package/src/duckdb/src/common/adbc/nanoarrow/single_batch_array_stream.cpp +84 -0
- package/src/duckdb/src/common/arrow/arrow_converter.cpp +4 -2
- package/src/duckdb/src/common/multi_file_reader.cpp +6 -0
- package/src/duckdb/src/execution/window_executor.cpp +5 -8
- package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
- package/src/duckdb/src/include/duckdb/common/adbc/adbc.h +1 -0
- package/src/duckdb/src/include/duckdb/common/adbc/adbc.hpp +3 -3
- package/src/duckdb/src/include/duckdb/common/adbc/single_batch_array_stream.hpp +16 -0
- package/src/duckdb/src/include/duckdb/common/arrow/arrow_appender.hpp +1 -2
- package/src/duckdb/src/include/duckdb/common/arrow/arrow_converter.hpp +0 -2
- package/src/duckdb/src/include/duckdb/common/arrow/nanoarrow/nanoarrow.h +462 -0
- package/src/duckdb/src/include/duckdb/common/arrow/nanoarrow/nanoarrow.hpp +14 -0
- package/src/duckdb/src/include/duckdb/common/types/data_chunk.hpp +0 -2
- package/src/duckdb/src/include/duckdb/main/chunk_scan_state.hpp +2 -4
- package/src/duckdb/src/include/duckdb.h +16 -0
- package/src/duckdb/src/main/capi/arrow-c.cpp +41 -0
- package/src/duckdb/src/main/capi/prepared-c.cpp +60 -30
- package/src/duckdb/src/main/chunk_scan_state.cpp +6 -0
- package/src/duckdb/src/main/client_context.cpp +1 -1
- package/src/duckdb/src/optimizer/topn_optimizer.cpp +7 -0
- package/src/duckdb/src/parser/transform/constraint/transform_constraint.cpp +55 -38
- package/src/duckdb/src/storage/compression/bitpacking.cpp +1 -1
- package/src/duckdb/ub_src_common_adbc_nanoarrow.cpp +8 -0
- package/src/duckdb_node.hpp +1 -0
- package/src/statement.cpp +1 -1
@@ -6,12 +6,14 @@
|
|
6
6
|
|
7
7
|
#include "duckdb.h"
|
8
8
|
#include "duckdb/common/arrow/arrow_wrapper.hpp"
|
9
|
-
#include "duckdb/common/arrow/
|
9
|
+
#include "duckdb/common/arrow/nanoarrow/nanoarrow.hpp"
|
10
10
|
|
11
11
|
#ifndef DUCKDB_AMALGAMATION
|
12
12
|
#include "duckdb/main/connection.hpp"
|
13
13
|
#endif
|
14
14
|
|
15
|
+
#include "duckdb/common/adbc/single_batch_array_stream.hpp"
|
16
|
+
|
15
17
|
#include <string.h>
|
16
18
|
#include <stdlib.h>
|
17
19
|
|
@@ -33,7 +35,7 @@ duckdb_adbc::AdbcStatusCode duckdb_adbc_init(size_t count, struct duckdb_adbc::A
|
|
33
35
|
driver->ConnectionGetTableTypes = duckdb_adbc::ConnectionGetTableTypes;
|
34
36
|
driver->StatementNew = duckdb_adbc::StatementNew;
|
35
37
|
driver->StatementRelease = duckdb_adbc::StatementRelease;
|
36
|
-
|
38
|
+
driver->StatementBind = duckdb_adbc::StatementBind;
|
37
39
|
driver->StatementBindStream = duckdb_adbc::StatementBindStream;
|
38
40
|
driver->StatementExecuteQuery = duckdb_adbc::StatementExecuteQuery;
|
39
41
|
driver->StatementPrepare = duckdb_adbc::StatementPrepare;
|
@@ -44,9 +46,13 @@ duckdb_adbc::AdbcStatusCode duckdb_adbc_init(size_t count, struct duckdb_adbc::A
|
|
44
46
|
driver->ConnectionRollback = duckdb_adbc::ConnectionRollback;
|
45
47
|
driver->ConnectionReadPartition = duckdb_adbc::ConnectionReadPartition;
|
46
48
|
driver->StatementExecutePartitions = duckdb_adbc::StatementExecutePartitions;
|
49
|
+
driver->ConnectionGetInfo = duckdb_adbc::ConnectionGetInfo;
|
50
|
+
driver->StatementGetParameterSchema = duckdb_adbc::StatementGetParameterSchema;
|
47
51
|
driver->ConnectionGetTableSchema = duckdb_adbc::ConnectionGetTableSchema;
|
48
52
|
driver->StatementSetSubstraitPlan = duckdb_adbc::StatementSetSubstraitPlan;
|
49
53
|
|
54
|
+
driver->ConnectionGetInfo = duckdb_adbc::ConnectionGetInfo;
|
55
|
+
driver->StatementGetParameterSchema = duckdb_adbc::StatementGetParameterSchema;
|
50
56
|
return ADBC_STATUS_OK;
|
51
57
|
}
|
52
58
|
|
@@ -58,19 +64,30 @@ struct DuckDBAdbcStatementWrapper {
|
|
58
64
|
::duckdb_arrow result;
|
59
65
|
::duckdb_prepared_statement statement;
|
60
66
|
char *ingestion_table_name;
|
61
|
-
ArrowArrayStream
|
67
|
+
ArrowArrayStream ingestion_stream;
|
62
68
|
IngestionMode ingestion_mode = IngestionMode::CREATE;
|
63
69
|
};
|
70
|
+
|
64
71
|
static AdbcStatusCode QueryInternal(struct AdbcConnection *connection, struct ArrowArrayStream *out, const char *query,
|
65
|
-
struct AdbcError *error)
|
66
|
-
|
67
|
-
|
68
|
-
|
72
|
+
struct AdbcError *error) {
|
73
|
+
AdbcStatement statement;
|
74
|
+
|
75
|
+
auto status = StatementNew(connection, &statement, error);
|
76
|
+
if (status != ADBC_STATUS_OK) {
|
77
|
+
SetError(error, "unable to initialize statement");
|
78
|
+
return status;
|
69
79
|
}
|
70
|
-
|
71
|
-
|
72
|
-
|
80
|
+
status = StatementSetSqlQuery(&statement, query, error);
|
81
|
+
if (status != ADBC_STATUS_OK) {
|
82
|
+
SetError(error, "unable to initialize statement");
|
83
|
+
return status;
|
73
84
|
}
|
85
|
+
status = StatementExecuteQuery(&statement, out, nullptr, error);
|
86
|
+
if (status != ADBC_STATUS_OK) {
|
87
|
+
SetError(error, "unable to initialize statement");
|
88
|
+
return status;
|
89
|
+
}
|
90
|
+
|
74
91
|
return ADBC_STATUS_OK;
|
75
92
|
}
|
76
93
|
|
@@ -83,12 +100,20 @@ struct DuckDBAdbcDatabaseWrapper {
|
|
83
100
|
std::string path;
|
84
101
|
};
|
85
102
|
|
86
|
-
void
|
103
|
+
static void EmptyErrorRelease(AdbcError *error) {
|
104
|
+
// The object is valid but doesn't contain any data that needs to be cleaned up
|
105
|
+
// Just set the release to nullptr to indicate that it's no longer valid.
|
106
|
+
error->release = nullptr;
|
107
|
+
return;
|
108
|
+
}
|
109
|
+
|
110
|
+
void InitializeADBCError(AdbcError *error) {
|
87
111
|
if (!error) {
|
88
112
|
return;
|
89
113
|
}
|
90
114
|
error->message = nullptr;
|
91
|
-
|
115
|
+
// Don't set to nullptr, as that indicates that it's invalid
|
116
|
+
error->release = EmptyErrorRelease;
|
92
117
|
std::memset(error->sqlstate, '\0', sizeof(error->sqlstate));
|
93
118
|
error->vendor_code = -1;
|
94
119
|
}
|
@@ -106,18 +131,18 @@ AdbcStatusCode CheckResult(duckdb_state &res, AdbcError *error, const char *erro
|
|
106
131
|
}
|
107
132
|
|
108
133
|
AdbcStatusCode DatabaseNew(struct AdbcDatabase *database, struct AdbcError *error) {
|
109
|
-
|
110
|
-
|
111
|
-
return
|
134
|
+
if (!database) {
|
135
|
+
SetError(error, "Missing database object");
|
136
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
112
137
|
}
|
113
138
|
database->private_data = nullptr;
|
114
139
|
// you can't malloc a struct with a non-trivial C++ constructor
|
115
140
|
// and std::string has a non-trivial constructor. so we need
|
116
141
|
// to use new and delete rather than malloc and free.
|
117
142
|
auto wrapper = new DuckDBAdbcDatabaseWrapper;
|
118
|
-
|
119
|
-
|
120
|
-
return
|
143
|
+
if (!wrapper) {
|
144
|
+
SetError(error, "Allocation error");
|
145
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
121
146
|
}
|
122
147
|
database->private_data = wrapper;
|
123
148
|
auto res = duckdb_create_config(&wrapper->config);
|
@@ -148,14 +173,13 @@ AdbcStatusCode StatementSetSubstraitPlan(struct AdbcStatement *statement, const
|
|
148
173
|
|
149
174
|
AdbcStatusCode DatabaseSetOption(struct AdbcDatabase *database, const char *key, const char *value,
|
150
175
|
struct AdbcError *error) {
|
151
|
-
|
152
|
-
|
153
|
-
return
|
176
|
+
if (!database) {
|
177
|
+
SetError(error, "Missing database object");
|
178
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
154
179
|
}
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
return status;
|
180
|
+
if (!key) {
|
181
|
+
SetError(error, "Missing key");
|
182
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
159
183
|
}
|
160
184
|
|
161
185
|
auto wrapper = (DuckDBAdbcDatabaseWrapper *)database->private_data;
|
@@ -236,9 +260,9 @@ AdbcStatusCode ConnectionGetTableSchema(struct AdbcConnection *connection, const
|
|
236
260
|
}
|
237
261
|
|
238
262
|
AdbcStatusCode ConnectionNew(struct AdbcConnection *connection, struct AdbcError *error) {
|
239
|
-
|
240
|
-
|
241
|
-
return
|
263
|
+
if (!connection) {
|
264
|
+
SetError(error, "Missing connection object");
|
265
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
242
266
|
}
|
243
267
|
|
244
268
|
connection->private_data = nullptr;
|
@@ -345,19 +369,129 @@ AdbcStatusCode ConnectionRollback(struct AdbcConnection *connection, struct Adbc
|
|
345
369
|
return ExecuteQuery(conn, "START TRANSACTION", error);
|
346
370
|
}
|
347
371
|
|
372
|
+
enum class AdbcInfoCode : uint32_t {
|
373
|
+
VENDOR_NAME,
|
374
|
+
VENDOR_VERSION,
|
375
|
+
DRIVER_NAME,
|
376
|
+
DRIVER_VERSION,
|
377
|
+
DRIVER_ARROW_VERSION,
|
378
|
+
UNRECOGNIZED // always the last entry of the enum
|
379
|
+
};
|
380
|
+
|
381
|
+
static AdbcInfoCode ConvertToInfoCode(uint32_t info_code) {
|
382
|
+
switch (info_code) {
|
383
|
+
case 0:
|
384
|
+
return AdbcInfoCode::VENDOR_NAME;
|
385
|
+
case 1:
|
386
|
+
return AdbcInfoCode::VENDOR_VERSION;
|
387
|
+
case 2:
|
388
|
+
return AdbcInfoCode::DRIVER_NAME;
|
389
|
+
case 3:
|
390
|
+
return AdbcInfoCode::DRIVER_VERSION;
|
391
|
+
case 4:
|
392
|
+
return AdbcInfoCode::DRIVER_ARROW_VERSION;
|
393
|
+
default:
|
394
|
+
return AdbcInfoCode::UNRECOGNIZED;
|
395
|
+
}
|
396
|
+
}
|
397
|
+
|
398
|
+
AdbcStatusCode ConnectionGetInfo(struct AdbcConnection *connection, uint32_t *info_codes, size_t info_codes_length,
|
399
|
+
struct ArrowArrayStream *out, struct AdbcError *error) {
|
400
|
+
if (!connection) {
|
401
|
+
SetError(error, "Missing connection object");
|
402
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
403
|
+
}
|
404
|
+
if (!connection->private_data) {
|
405
|
+
SetError(error, "Connection is invalid");
|
406
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
407
|
+
}
|
408
|
+
if (!out) {
|
409
|
+
SetError(error, "Output parameter was not provided");
|
410
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
411
|
+
}
|
412
|
+
|
413
|
+
// If 'info_codes' is NULL, we should output all the info codes we recognize
|
414
|
+
size_t length = info_codes ? info_codes_length : (size_t)AdbcInfoCode::UNRECOGNIZED;
|
415
|
+
|
416
|
+
duckdb::string q = R"EOF(
|
417
|
+
select
|
418
|
+
name::UINTEGER as info_name,
|
419
|
+
info::UNION(
|
420
|
+
string_value VARCHAR,
|
421
|
+
bool_value BOOL,
|
422
|
+
int64_value BIGINT,
|
423
|
+
int32_bitmask INTEGER,
|
424
|
+
string_list VARCHAR[],
|
425
|
+
int32_to_int32_list_map MAP(INTEGER, INTEGER[])
|
426
|
+
) as info_value from values
|
427
|
+
)EOF";
|
428
|
+
|
429
|
+
duckdb::string results = "";
|
430
|
+
|
431
|
+
for (size_t i = 0; i < length; i++) {
|
432
|
+
uint32_t code = info_codes ? info_codes[i] : i;
|
433
|
+
auto info_code = ConvertToInfoCode(code);
|
434
|
+
switch (info_code) {
|
435
|
+
case AdbcInfoCode::VENDOR_NAME: {
|
436
|
+
results += "(0, 'duckdb'),";
|
437
|
+
break;
|
438
|
+
}
|
439
|
+
case AdbcInfoCode::VENDOR_VERSION: {
|
440
|
+
results += duckdb::StringUtil::Format("(1, '%s'),", duckdb_library_version());
|
441
|
+
break;
|
442
|
+
}
|
443
|
+
case AdbcInfoCode::DRIVER_NAME: {
|
444
|
+
results += "(2, 'ADBC DuckDB Driver'),";
|
445
|
+
break;
|
446
|
+
}
|
447
|
+
case AdbcInfoCode::DRIVER_VERSION: {
|
448
|
+
// TODO: fill in driver version
|
449
|
+
results += "(3, '(unknown)'),";
|
450
|
+
break;
|
451
|
+
}
|
452
|
+
case AdbcInfoCode::DRIVER_ARROW_VERSION: {
|
453
|
+
// TODO: fill in arrow version
|
454
|
+
results += "(4, '(unknown)'),";
|
455
|
+
break;
|
456
|
+
}
|
457
|
+
case AdbcInfoCode::UNRECOGNIZED: {
|
458
|
+
// Unrecognized codes are not an error, just ignored
|
459
|
+
continue;
|
460
|
+
}
|
461
|
+
default: {
|
462
|
+
// Codes that we have implemented but not handled here are a developer error
|
463
|
+
SetError(error, "Info code recognized but not handled");
|
464
|
+
return ADBC_STATUS_INTERNAL;
|
465
|
+
}
|
466
|
+
}
|
467
|
+
}
|
468
|
+
if (results.empty()) {
|
469
|
+
// Add a group of values so the query parses
|
470
|
+
q += "(NULL, NULL)";
|
471
|
+
} else {
|
472
|
+
q += results;
|
473
|
+
}
|
474
|
+
q += " tbl(name, info)";
|
475
|
+
if (results.empty()) {
|
476
|
+
// Add an impossible where clause to return an empty result set
|
477
|
+
q += " where true = false";
|
478
|
+
}
|
479
|
+
return QueryInternal(connection, out, q.c_str(), error);
|
480
|
+
}
|
481
|
+
|
348
482
|
AdbcStatusCode ConnectionInit(struct AdbcConnection *connection, struct AdbcDatabase *database,
|
349
483
|
struct AdbcError *error) {
|
350
|
-
|
351
|
-
|
352
|
-
return
|
484
|
+
if (!database) {
|
485
|
+
SetError(error, "Missing database object");
|
486
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
353
487
|
}
|
354
|
-
|
355
|
-
|
356
|
-
return
|
488
|
+
if (!database->private_data) {
|
489
|
+
SetError(error, "Invalid database");
|
490
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
357
491
|
}
|
358
|
-
|
359
|
-
|
360
|
-
return
|
492
|
+
if (!connection) {
|
493
|
+
SetError(error, "Missing connection object");
|
494
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
361
495
|
}
|
362
496
|
auto database_wrapper = (DuckDBAdbcDatabaseWrapper *)database->private_data;
|
363
497
|
|
@@ -433,20 +567,19 @@ void stream_schema(uintptr_t factory_ptr, duckdb::ArrowSchemaWrapper &schema) {
|
|
433
567
|
AdbcStatusCode Ingest(duckdb_connection connection, const char *table_name, struct ArrowArrayStream *input,
|
434
568
|
struct AdbcError *error, IngestionMode ingestion_mode) {
|
435
569
|
|
436
|
-
|
437
|
-
|
438
|
-
return
|
570
|
+
if (!connection) {
|
571
|
+
SetError(error, "Missing connection object");
|
572
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
439
573
|
}
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
return status;
|
574
|
+
if (!input) {
|
575
|
+
SetError(error, "Missing input arrow stream pointer");
|
576
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
444
577
|
}
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
return status;
|
578
|
+
if (!table_name) {
|
579
|
+
SetError(error, "Missing database object name");
|
580
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
449
581
|
}
|
582
|
+
|
450
583
|
auto cconn = (duckdb::Connection *)connection;
|
451
584
|
|
452
585
|
auto arrow_scan = cconn->TableFunction("arrow_scan", {duckdb::Value::POINTER((uintptr_t)input),
|
@@ -458,7 +591,7 @@ AdbcStatusCode Ingest(duckdb_connection connection, const char *table_name, stru
|
|
458
591
|
arrow_scan->Create(table_name);
|
459
592
|
} else {
|
460
593
|
arrow_scan->CreateView("temp_adbc_view", true, true);
|
461
|
-
auto query = "insert into "
|
594
|
+
auto query = duckdb::StringUtil::Format("insert into \"%s\" select * from temp_adbc_view", table_name);
|
462
595
|
auto result = cconn->Query(query);
|
463
596
|
}
|
464
597
|
// After creating a table, the arrow array stream is released. Hence we must set it as released to avoid
|
@@ -477,79 +610,134 @@ AdbcStatusCode Ingest(duckdb_connection connection, const char *table_name, stru
|
|
477
610
|
|
478
611
|
AdbcStatusCode StatementNew(struct AdbcConnection *connection, struct AdbcStatement *statement,
|
479
612
|
struct AdbcError *error) {
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
return status;
|
613
|
+
if (!connection) {
|
614
|
+
SetError(error, "Missing connection object");
|
615
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
484
616
|
}
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
return status;
|
617
|
+
if (!connection->private_data) {
|
618
|
+
SetError(error, "Invalid connection object");
|
619
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
489
620
|
}
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
return status;
|
621
|
+
if (!statement) {
|
622
|
+
SetError(error, "Missing statement object");
|
623
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
494
624
|
}
|
495
625
|
|
496
626
|
statement->private_data = nullptr;
|
497
627
|
|
498
628
|
auto statement_wrapper = (DuckDBAdbcStatementWrapper *)malloc(sizeof(DuckDBAdbcStatementWrapper));
|
499
|
-
|
500
|
-
|
501
|
-
return
|
629
|
+
if (!statement_wrapper) {
|
630
|
+
SetError(error, "Allocation error");
|
631
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
502
632
|
}
|
503
633
|
|
504
634
|
statement->private_data = statement_wrapper;
|
505
635
|
statement_wrapper->connection = (duckdb_connection)connection->private_data;
|
506
636
|
statement_wrapper->statement = nullptr;
|
507
637
|
statement_wrapper->result = nullptr;
|
508
|
-
statement_wrapper->ingestion_stream = nullptr;
|
638
|
+
statement_wrapper->ingestion_stream.release = nullptr;
|
509
639
|
statement_wrapper->ingestion_table_name = nullptr;
|
510
640
|
statement_wrapper->ingestion_mode = IngestionMode::CREATE;
|
511
641
|
return ADBC_STATUS_OK;
|
512
642
|
}
|
513
643
|
|
514
644
|
AdbcStatusCode StatementRelease(struct AdbcStatement *statement, struct AdbcError *error) {
|
645
|
+
if (!statement || !statement->private_data) {
|
646
|
+
return ADBC_STATUS_OK;
|
647
|
+
}
|
648
|
+
auto wrapper = (DuckDBAdbcStatementWrapper *)statement->private_data;
|
649
|
+
if (wrapper->statement) {
|
650
|
+
duckdb_destroy_prepare(&wrapper->statement);
|
651
|
+
wrapper->statement = nullptr;
|
652
|
+
}
|
653
|
+
if (wrapper->result) {
|
654
|
+
duckdb_destroy_arrow(&wrapper->result);
|
655
|
+
wrapper->result = nullptr;
|
656
|
+
}
|
657
|
+
if (wrapper->ingestion_stream.release) {
|
658
|
+
wrapper->ingestion_stream.release(&wrapper->ingestion_stream);
|
659
|
+
wrapper->ingestion_stream.release = nullptr;
|
660
|
+
}
|
661
|
+
if (wrapper->ingestion_table_name) {
|
662
|
+
free(wrapper->ingestion_table_name);
|
663
|
+
wrapper->ingestion_table_name = nullptr;
|
664
|
+
}
|
665
|
+
free(statement->private_data);
|
666
|
+
statement->private_data = nullptr;
|
667
|
+
return ADBC_STATUS_OK;
|
668
|
+
}
|
515
669
|
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
670
|
+
AdbcStatusCode StatementGetParameterSchema(struct AdbcStatement *statement, struct ArrowSchema *schema,
|
671
|
+
struct AdbcError *error) {
|
672
|
+
if (!statement) {
|
673
|
+
SetError(error, "Missing statement object");
|
674
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
675
|
+
}
|
676
|
+
if (!statement->private_data) {
|
677
|
+
SetError(error, "Invalid statement object");
|
678
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
679
|
+
}
|
680
|
+
if (!schema) {
|
681
|
+
SetError(error, "Missing schema object");
|
682
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
683
|
+
}
|
684
|
+
auto wrapper = (DuckDBAdbcStatementWrapper *)statement->private_data;
|
685
|
+
// TODO: we might want to cache this, but then we need to return a deep copy anyways.., so I'm not sure if that
|
686
|
+
// would be worth the extra management
|
687
|
+
auto res = duckdb_prepared_arrow_schema(wrapper->statement, (duckdb_arrow_schema *)&schema);
|
688
|
+
if (res != DuckDBSuccess) {
|
689
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
690
|
+
}
|
691
|
+
return ADBC_STATUS_OK;
|
692
|
+
}
|
693
|
+
|
694
|
+
AdbcStatusCode GetPreparedParameters(duckdb_connection connection, duckdb::unique_ptr<duckdb::QueryResult> &result,
|
695
|
+
ArrowArrayStream *input, AdbcError *error) {
|
696
|
+
|
697
|
+
auto cconn = (duckdb::Connection *)connection;
|
698
|
+
|
699
|
+
try {
|
700
|
+
auto arrow_scan = cconn->TableFunction("arrow_scan", {duckdb::Value::POINTER((uintptr_t)input),
|
701
|
+
duckdb::Value::POINTER((uintptr_t)stream_produce),
|
702
|
+
duckdb::Value::POINTER((uintptr_t)input->get_schema)});
|
703
|
+
result = arrow_scan->Execute();
|
704
|
+
// After creating a table, the arrow array stream is released. Hence we must set it as released to avoid
|
705
|
+
// double-releasing it
|
706
|
+
input->release = nullptr;
|
707
|
+
} catch (std::exception &ex) {
|
708
|
+
if (error) {
|
709
|
+
error->message = strdup(ex.what());
|
534
710
|
}
|
535
|
-
|
536
|
-
|
711
|
+
return ADBC_STATUS_INTERNAL;
|
712
|
+
} catch (...) {
|
713
|
+
return ADBC_STATUS_INTERNAL;
|
537
714
|
}
|
538
715
|
return ADBC_STATUS_OK;
|
539
716
|
}
|
540
717
|
|
718
|
+
static AdbcStatusCode IngestToTableFromBoundStream(DuckDBAdbcStatementWrapper *statement, AdbcError *error) {
|
719
|
+
// See ADBC_INGEST_OPTION_TARGET_TABLE
|
720
|
+
D_ASSERT(statement->ingestion_stream.release);
|
721
|
+
D_ASSERT(statement->ingestion_table_name);
|
722
|
+
|
723
|
+
// Take the input stream from the statement
|
724
|
+
auto stream = statement->ingestion_stream;
|
725
|
+
statement->ingestion_stream.release = nullptr;
|
726
|
+
|
727
|
+
// Ingest into a table from the bound stream
|
728
|
+
return Ingest(statement->connection, statement->ingestion_table_name, &stream, error, statement->ingestion_mode);
|
729
|
+
}
|
730
|
+
|
541
731
|
AdbcStatusCode StatementExecuteQuery(struct AdbcStatement *statement, struct ArrowArrayStream *out,
|
542
732
|
int64_t *rows_affected, struct AdbcError *error) {
|
543
|
-
|
544
|
-
|
545
|
-
return
|
733
|
+
if (!statement) {
|
734
|
+
SetError(error, "Missing statement object");
|
735
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
546
736
|
}
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
return status;
|
737
|
+
if (!statement->private_data) {
|
738
|
+
SetError(error, "Invalid statement object");
|
739
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
551
740
|
}
|
552
|
-
|
553
741
|
auto wrapper = (DuckDBAdbcStatementWrapper *)statement->private_data;
|
554
742
|
|
555
743
|
// TODO: Set affected rows, careful with early return
|
@@ -557,16 +745,59 @@ AdbcStatusCode StatementExecuteQuery(struct AdbcStatement *statement, struct Arr
|
|
557
745
|
*rows_affected = 0;
|
558
746
|
}
|
559
747
|
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
748
|
+
const auto has_stream = wrapper->ingestion_stream.release != nullptr;
|
749
|
+
const auto to_table = wrapper->ingestion_table_name != nullptr;
|
750
|
+
|
751
|
+
if (has_stream && to_table) {
|
752
|
+
return IngestToTableFromBoundStream(wrapper, error);
|
564
753
|
}
|
565
754
|
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
755
|
+
if (has_stream) {
|
756
|
+
// A stream was bound to the statement, use that to bind parameters
|
757
|
+
duckdb::unique_ptr<duckdb::QueryResult> result;
|
758
|
+
ArrowArrayStream stream = wrapper->ingestion_stream;
|
759
|
+
wrapper->ingestion_stream.release = nullptr;
|
760
|
+
auto adbc_res = GetPreparedParameters(wrapper->connection, result, &stream, error);
|
761
|
+
if (adbc_res != ADBC_STATUS_OK) {
|
762
|
+
return adbc_res;
|
763
|
+
}
|
764
|
+
if (!result) {
|
765
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
766
|
+
}
|
767
|
+
duckdb::unique_ptr<duckdb::DataChunk> chunk;
|
768
|
+
while ((chunk = result->Fetch()) != nullptr) {
|
769
|
+
if (chunk->size() == 0) {
|
770
|
+
SetError(error, "Please provide a non-empty chunk to be bound");
|
771
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
772
|
+
}
|
773
|
+
if (chunk->size() != 1) {
|
774
|
+
// TODO: add support for binding multiple rows
|
775
|
+
SetError(error, "Binding multiple rows at once is not supported yet");
|
776
|
+
return ADBC_STATUS_NOT_IMPLEMENTED;
|
777
|
+
}
|
778
|
+
duckdb_clear_bindings(wrapper->statement);
|
779
|
+
for (idx_t col_idx = 0; col_idx < chunk->ColumnCount(); col_idx++) {
|
780
|
+
auto val = chunk->GetValue(col_idx, 0);
|
781
|
+
auto duck_val = (duckdb_value)&val;
|
782
|
+
auto res = duckdb_bind_value(wrapper->statement, 1 + col_idx, duck_val);
|
783
|
+
if (res != DuckDBSuccess) {
|
784
|
+
SetError(error, duckdb_prepare_error(wrapper->statement));
|
785
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
786
|
+
}
|
787
|
+
}
|
788
|
+
|
789
|
+
auto res = duckdb_execute_prepared_arrow(wrapper->statement, &wrapper->result);
|
790
|
+
if (res != DuckDBSuccess) {
|
791
|
+
SetError(error, duckdb_query_arrow_error(wrapper->result));
|
792
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
793
|
+
}
|
794
|
+
}
|
795
|
+
} else {
|
796
|
+
auto res = duckdb_execute_prepared_arrow(wrapper->statement, &wrapper->result);
|
797
|
+
if (res != DuckDBSuccess) {
|
798
|
+
SetError(error, duckdb_query_arrow_error(wrapper->result));
|
799
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
800
|
+
}
|
570
801
|
}
|
571
802
|
|
572
803
|
if (out) {
|
@@ -590,21 +821,25 @@ AdbcStatusCode StatementPrepare(struct AdbcStatement *statement, struct AdbcErro
|
|
590
821
|
SetError(error, "Missing statement object");
|
591
822
|
return ADBC_STATUS_INVALID_ARGUMENT;
|
592
823
|
}
|
593
|
-
if (!
|
594
|
-
SetError(error, "
|
824
|
+
if (!statement->private_data) {
|
825
|
+
SetError(error, "Invalid statement object");
|
595
826
|
return ADBC_STATUS_INVALID_ARGUMENT;
|
596
827
|
}
|
597
828
|
return ADBC_STATUS_OK;
|
598
829
|
}
|
599
830
|
|
600
831
|
AdbcStatusCode StatementSetSqlQuery(struct AdbcStatement *statement, const char *query, struct AdbcError *error) {
|
601
|
-
|
602
|
-
|
603
|
-
return
|
832
|
+
if (!statement) {
|
833
|
+
SetError(error, "Missing statement object");
|
834
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
604
835
|
}
|
605
|
-
|
606
|
-
|
607
|
-
return
|
836
|
+
if (!statement->private_data) {
|
837
|
+
SetError(error, "Invalid statement object");
|
838
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
839
|
+
}
|
840
|
+
if (!query) {
|
841
|
+
SetError(error, "Missing query");
|
842
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
608
843
|
}
|
609
844
|
|
610
845
|
auto wrapper = (DuckDBAdbcStatementWrapper *)statement->private_data;
|
@@ -613,31 +848,74 @@ AdbcStatusCode StatementSetSqlQuery(struct AdbcStatement *statement, const char
|
|
613
848
|
return CheckResult(res, error, error_msg);
|
614
849
|
}
|
615
850
|
|
851
|
+
AdbcStatusCode StatementBind(struct AdbcStatement *statement, struct ArrowArray *values, struct ArrowSchema *schemas,
|
852
|
+
struct AdbcError *error) {
|
853
|
+
if (!statement) {
|
854
|
+
SetError(error, "Missing statement object");
|
855
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
856
|
+
}
|
857
|
+
if (!statement->private_data) {
|
858
|
+
SetError(error, "Invalid statement object");
|
859
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
860
|
+
}
|
861
|
+
if (!values) {
|
862
|
+
SetError(error, "Missing values object");
|
863
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
864
|
+
}
|
865
|
+
if (!schemas) {
|
866
|
+
SetError(error, "Invalid schemas object");
|
867
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
868
|
+
}
|
869
|
+
|
870
|
+
auto wrapper = (DuckDBAdbcStatementWrapper *)statement->private_data;
|
871
|
+
if (wrapper->ingestion_stream.release) {
|
872
|
+
// Free the stream that was previously bound
|
873
|
+
wrapper->ingestion_stream.release(&wrapper->ingestion_stream);
|
874
|
+
}
|
875
|
+
auto status = BatchToArrayStream(values, schemas, &wrapper->ingestion_stream, error);
|
876
|
+
return status;
|
877
|
+
}
|
878
|
+
|
616
879
|
AdbcStatusCode StatementBindStream(struct AdbcStatement *statement, struct ArrowArrayStream *values,
|
617
880
|
struct AdbcError *error) {
|
618
|
-
|
619
|
-
|
620
|
-
return
|
881
|
+
if (!statement) {
|
882
|
+
SetError(error, "Missing statement object");
|
883
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
621
884
|
}
|
622
|
-
|
623
|
-
|
624
|
-
return
|
885
|
+
if (!statement->private_data) {
|
886
|
+
SetError(error, "Invalid statement object");
|
887
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
888
|
+
}
|
889
|
+
if (!values) {
|
890
|
+
SetError(error, "Missing values object");
|
891
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
625
892
|
}
|
893
|
+
|
626
894
|
auto wrapper = (DuckDBAdbcStatementWrapper *)statement->private_data;
|
627
|
-
wrapper->ingestion_stream
|
895
|
+
if (wrapper->ingestion_stream.release) {
|
896
|
+
// Release any resources currently held by the ingestion stream before we overwrite it
|
897
|
+
wrapper->ingestion_stream.release(&wrapper->ingestion_stream);
|
898
|
+
}
|
899
|
+
wrapper->ingestion_stream = *values;
|
900
|
+
values->release = nullptr;
|
628
901
|
return ADBC_STATUS_OK;
|
629
902
|
}
|
630
903
|
|
631
904
|
AdbcStatusCode StatementSetOption(struct AdbcStatement *statement, const char *key, const char *value,
|
632
905
|
struct AdbcError *error) {
|
633
|
-
|
634
|
-
|
635
|
-
return
|
906
|
+
if (!statement) {
|
907
|
+
SetError(error, "Missing statement object");
|
908
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
636
909
|
}
|
637
|
-
|
638
|
-
|
639
|
-
return
|
910
|
+
if (!statement->private_data) {
|
911
|
+
SetError(error, "Invalid statement object");
|
912
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
913
|
+
}
|
914
|
+
if (!key) {
|
915
|
+
SetError(error, "Missing key object");
|
916
|
+
return ADBC_STATUS_INVALID_ARGUMENT;
|
640
917
|
}
|
918
|
+
|
641
919
|
auto wrapper = (DuckDBAdbcStatementWrapper *)statement->private_data;
|
642
920
|
|
643
921
|
if (strcmp(key, ADBC_INGEST_OPTION_TARGET_TABLE) == 0) {
|
@@ -659,29 +937,6 @@ AdbcStatusCode StatementSetOption(struct AdbcStatement *statement, const char *k
|
|
659
937
|
return ADBC_STATUS_INVALID_ARGUMENT;
|
660
938
|
}
|
661
939
|
|
662
|
-
AdbcStatusCode QueryInternal(struct AdbcConnection *connection, struct ArrowArrayStream *out, const char *query,
|
663
|
-
struct AdbcError *error) {
|
664
|
-
AdbcStatement statement;
|
665
|
-
|
666
|
-
auto status = StatementNew(connection, &statement, error);
|
667
|
-
if (status != ADBC_STATUS_OK) {
|
668
|
-
SetError(error, "unable to initialize statement");
|
669
|
-
return status;
|
670
|
-
}
|
671
|
-
status = StatementSetSqlQuery(&statement, query, error);
|
672
|
-
if (status != ADBC_STATUS_OK) {
|
673
|
-
SetError(error, "unable to initialize statement");
|
674
|
-
return status;
|
675
|
-
}
|
676
|
-
status = StatementExecuteQuery(&statement, out, nullptr, error);
|
677
|
-
if (status != ADBC_STATUS_OK) {
|
678
|
-
SetError(error, "unable to initialize statement");
|
679
|
-
return status;
|
680
|
-
}
|
681
|
-
|
682
|
-
return ADBC_STATUS_OK;
|
683
|
-
}
|
684
|
-
|
685
940
|
AdbcStatusCode ConnectionGetObjects(struct AdbcConnection *connection, int depth, const char *catalog,
|
686
941
|
const char *db_schema, const char *table_name, const char **table_type,
|
687
942
|
const char *column_name, struct ArrowArrayStream *out, struct AdbcError *error) {
|