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
7namespace duckdb {
8
9struct 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
21struct 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
31unique_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 &param : 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
55void duckdb_add_replacement_scan(duckdb_database db, duckdb_replacement_callback_t replacement, void *extra_data,
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
71void 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
79void 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
88void 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