1 | #include "duckdb/main/capi/capi_internal.hpp" |
2 | #include "duckdb/main/config.hpp" |
3 | #include "duckdb/parser/tableref/table_function_ref.hpp" |
4 | #include "duckdb/parser/expression/constant_expression.hpp" |
5 | #include "duckdb/parser/expression/function_expression.hpp" |
6 | |
7 | namespace duckdb { |
8 | |
9 | struct CAPIReplacementScanData : public ReplacementScanData { |
10 | ~CAPIReplacementScanData() { |
11 | if (delete_callback) { |
12 | delete_callback(extra_data); |
13 | } |
14 | } |
15 | |
16 | duckdb_replacement_callback_t callback; |
17 | void *extra_data; |
18 | duckdb_delete_callback_t delete_callback; |
19 | }; |
20 | |
21 | struct CAPIReplacementScanInfo { |
22 | CAPIReplacementScanInfo(CAPIReplacementScanData *data) : data(data) { |
23 | } |
24 | |
25 | CAPIReplacementScanData *data; |
26 | string function_name; |
27 | vector<Value> parameters; |
28 | string error; |
29 | }; |
30 | |
31 | unique_ptr<TableRef> duckdb_capi_replacement_callback(ClientContext &context, const string &table_name, |
32 | ReplacementScanData *data) { |
33 | auto &scan_data = reinterpret_cast<CAPIReplacementScanData &>(*data); |
34 | |
35 | CAPIReplacementScanInfo info(&scan_data); |
36 | scan_data.callback((duckdb_replacement_scan_info)&info, table_name.c_str(), scan_data.extra_data); |
37 | if (!info.error.empty()) { |
38 | throw BinderException("Error in replacement scan: %s\n" , info.error); |
39 | } |
40 | if (info.function_name.empty()) { |
41 | // no function provided: bail-out |
42 | return nullptr; |
43 | } |
44 | auto table_function = make_uniq<TableFunctionRef>(); |
45 | vector<unique_ptr<ParsedExpression>> children; |
46 | for (auto ¶m : info.parameters) { |
47 | children.push_back(x: make_uniq<ConstantExpression>(args: std::move(param))); |
48 | } |
49 | table_function->function = make_uniq<FunctionExpression>(args&: info.function_name, args: std::move(children)); |
50 | return std::move(table_function); |
51 | } |
52 | |
53 | } // namespace duckdb |
54 | |
55 | void duckdb_add_replacement_scan(duckdb_database db, duckdb_replacement_callback_t replacement, void *, |
56 | duckdb_delete_callback_t delete_callback) { |
57 | if (!db || !replacement) { |
58 | return; |
59 | } |
60 | auto wrapper = reinterpret_cast<duckdb::DatabaseData *>(db); |
61 | auto scan_info = duckdb::make_uniq<duckdb::CAPIReplacementScanData>(); |
62 | scan_info->callback = replacement; |
63 | scan_info->extra_data = extra_data; |
64 | scan_info->delete_callback = delete_callback; |
65 | |
66 | auto &config = duckdb::DBConfig::GetConfig(db&: *wrapper->database->instance); |
67 | config.replacement_scans.push_back( |
68 | x: duckdb::ReplacementScan(duckdb::duckdb_capi_replacement_callback, std::move(scan_info))); |
69 | } |
70 | |
71 | void duckdb_replacement_scan_set_function_name(duckdb_replacement_scan_info info_p, const char *function_name) { |
72 | if (!info_p || !function_name) { |
73 | return; |
74 | } |
75 | auto info = reinterpret_cast<duckdb::CAPIReplacementScanInfo *>(info_p); |
76 | info->function_name = function_name; |
77 | } |
78 | |
79 | void duckdb_replacement_scan_add_parameter(duckdb_replacement_scan_info info_p, duckdb_value parameter) { |
80 | if (!info_p || !parameter) { |
81 | return; |
82 | } |
83 | auto info = reinterpret_cast<duckdb::CAPIReplacementScanInfo *>(info_p); |
84 | auto val = reinterpret_cast<duckdb::Value *>(parameter); |
85 | info->parameters.push_back(x: *val); |
86 | } |
87 | |
88 | void duckdb_replacement_scan_set_error(duckdb_replacement_scan_info info_p, const char *error) { |
89 | if (!info_p || !error) { |
90 | return; |
91 | } |
92 | auto info = reinterpret_cast<duckdb::CAPIReplacementScanInfo *>(info_p); |
93 | info->error = error; |
94 | } |
95 | |