| 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 | |