1#include "duckdb/main/capi/capi_internal.hpp"
2#include "duckdb/main/query_result.hpp"
3#include "duckdb/main/prepared_statement_data.hpp"
4#include "duckdb/common/types/decimal.hpp"
5
6using duckdb::Connection;
7using duckdb::date_t;
8using duckdb::dtime_t;
9using duckdb::ExtractStatementsWrapper;
10using duckdb::hugeint_t;
11using duckdb::LogicalType;
12using duckdb::MaterializedQueryResult;
13using duckdb::PreparedStatementWrapper;
14using duckdb::QueryResultType;
15using duckdb::timestamp_t;
16using duckdb::Value;
17
18idx_t duckdb_extract_statements(duckdb_connection connection, const char *query,
19 duckdb_extracted_statements *out_extracted_statements) {
20 if (!connection || !query || !out_extracted_statements) {
21 return 0;
22 }
23 auto wrapper = new ExtractStatementsWrapper();
24 Connection *conn = reinterpret_cast<Connection *>(connection);
25 try {
26 wrapper->statements = conn->ExtractStatements(query);
27 } catch (const duckdb::ParserException &e) {
28 wrapper->error = e.what();
29 }
30
31 *out_extracted_statements = (duckdb_extracted_statements)wrapper;
32 return wrapper->statements.size();
33}
34
35duckdb_state duckdb_prepare_extracted_statement(duckdb_connection connection,
36 duckdb_extracted_statements extracted_statements, idx_t index,
37 duckdb_prepared_statement *out_prepared_statement) {
38 Connection *conn = reinterpret_cast<Connection *>(connection);
39 auto source_wrapper = (ExtractStatementsWrapper *)extracted_statements;
40
41 if (!connection || !out_prepared_statement || index >= source_wrapper->statements.size()) {
42 return DuckDBError;
43 }
44 auto wrapper = new PreparedStatementWrapper();
45 wrapper->statement = conn->Prepare(statement: std::move(source_wrapper->statements[index]));
46
47 *out_prepared_statement = (duckdb_prepared_statement)wrapper;
48 return wrapper->statement->HasError() ? DuckDBError : DuckDBSuccess;
49}
50
51const char *duckdb_extract_statements_error(duckdb_extracted_statements extracted_statements) {
52 auto wrapper = (ExtractStatementsWrapper *)extracted_statements;
53 if (!wrapper || wrapper->error.empty()) {
54 return nullptr;
55 }
56 return wrapper->error.c_str();
57}
58
59duckdb_state duckdb_prepare(duckdb_connection connection, const char *query,
60 duckdb_prepared_statement *out_prepared_statement) {
61 if (!connection || !query || !out_prepared_statement) {
62 return DuckDBError;
63 }
64 auto wrapper = new PreparedStatementWrapper();
65 Connection *conn = reinterpret_cast<Connection *>(connection);
66 wrapper->statement = conn->Prepare(query);
67 *out_prepared_statement = (duckdb_prepared_statement)wrapper;
68 return !wrapper->statement->HasError() ? DuckDBSuccess : DuckDBError;
69}
70
71const char *duckdb_prepare_error(duckdb_prepared_statement prepared_statement) {
72 auto wrapper = reinterpret_cast<PreparedStatementWrapper *>(prepared_statement);
73 if (!wrapper || !wrapper->statement || !wrapper->statement->HasError()) {
74 return nullptr;
75 }
76 return wrapper->statement->error.Message().c_str();
77}
78
79idx_t duckdb_nparams(duckdb_prepared_statement prepared_statement) {
80 auto wrapper = reinterpret_cast<PreparedStatementWrapper *>(prepared_statement);
81 if (!wrapper || !wrapper->statement || wrapper->statement->HasError()) {
82 return 0;
83 }
84 return wrapper->statement->n_param;
85}
86
87duckdb_type duckdb_param_type(duckdb_prepared_statement prepared_statement, idx_t param_idx) {
88 auto wrapper = reinterpret_cast<PreparedStatementWrapper *>(prepared_statement);
89 if (!wrapper || !wrapper->statement || wrapper->statement->HasError()) {
90 return DUCKDB_TYPE_INVALID;
91 }
92 LogicalType param_type;
93 if (!wrapper->statement->data->TryGetType(param_idx, result&: param_type)) {
94 return DUCKDB_TYPE_INVALID;
95 }
96 return ConvertCPPTypeToC(sql_type: param_type);
97}
98
99duckdb_state duckdb_clear_bindings(duckdb_prepared_statement prepared_statement) {
100 auto wrapper = reinterpret_cast<PreparedStatementWrapper *>(prepared_statement);
101 if (!wrapper || !wrapper->statement || wrapper->statement->HasError()) {
102 return DuckDBError;
103 }
104 wrapper->values.clear();
105 return DuckDBSuccess;
106}
107
108static duckdb_state duckdb_bind_value(duckdb_prepared_statement prepared_statement, idx_t param_idx, Value val) {
109 auto wrapper = reinterpret_cast<PreparedStatementWrapper *>(prepared_statement);
110 if (!wrapper || !wrapper->statement || wrapper->statement->HasError()) {
111 return DuckDBError;
112 }
113 if (param_idx <= 0 || param_idx > wrapper->statement->n_param) {
114 return DuckDBError;
115 }
116 if (param_idx > wrapper->values.size()) {
117 wrapper->values.resize(new_size: param_idx);
118 }
119 wrapper->values[param_idx - 1] = val;
120 return DuckDBSuccess;
121}
122
123duckdb_state duckdb_bind_boolean(duckdb_prepared_statement prepared_statement, idx_t param_idx, bool val) {
124 return duckdb_bind_value(prepared_statement, param_idx, val: Value::BOOLEAN(value: val));
125}
126
127duckdb_state duckdb_bind_int8(duckdb_prepared_statement prepared_statement, idx_t param_idx, int8_t val) {
128 return duckdb_bind_value(prepared_statement, param_idx, val: Value::TINYINT(value: val));
129}
130
131duckdb_state duckdb_bind_int16(duckdb_prepared_statement prepared_statement, idx_t param_idx, int16_t val) {
132 return duckdb_bind_value(prepared_statement, param_idx, val: Value::SMALLINT(value: val));
133}
134
135duckdb_state duckdb_bind_int32(duckdb_prepared_statement prepared_statement, idx_t param_idx, int32_t val) {
136 return duckdb_bind_value(prepared_statement, param_idx, val: Value::INTEGER(value: val));
137}
138
139duckdb_state duckdb_bind_int64(duckdb_prepared_statement prepared_statement, idx_t param_idx, int64_t val) {
140 return duckdb_bind_value(prepared_statement, param_idx, val: Value::BIGINT(value: val));
141}
142
143static hugeint_t duckdb_internal_hugeint(duckdb_hugeint val) {
144 hugeint_t internal;
145 internal.lower = val.lower;
146 internal.upper = val.upper;
147 return internal;
148}
149
150duckdb_state duckdb_bind_hugeint(duckdb_prepared_statement prepared_statement, idx_t param_idx, duckdb_hugeint val) {
151 return duckdb_bind_value(prepared_statement, param_idx, val: Value::HUGEINT(value: duckdb_internal_hugeint(val)));
152}
153
154duckdb_state duckdb_bind_uint8(duckdb_prepared_statement prepared_statement, idx_t param_idx, uint8_t val) {
155 return duckdb_bind_value(prepared_statement, param_idx, val: Value::UTINYINT(value: val));
156}
157
158duckdb_state duckdb_bind_uint16(duckdb_prepared_statement prepared_statement, idx_t param_idx, uint16_t val) {
159 return duckdb_bind_value(prepared_statement, param_idx, val: Value::USMALLINT(value: val));
160}
161
162duckdb_state duckdb_bind_uint32(duckdb_prepared_statement prepared_statement, idx_t param_idx, uint32_t val) {
163 return duckdb_bind_value(prepared_statement, param_idx, val: Value::UINTEGER(value: val));
164}
165
166duckdb_state duckdb_bind_uint64(duckdb_prepared_statement prepared_statement, idx_t param_idx, uint64_t val) {
167 return duckdb_bind_value(prepared_statement, param_idx, val: Value::UBIGINT(value: val));
168}
169
170duckdb_state duckdb_bind_float(duckdb_prepared_statement prepared_statement, idx_t param_idx, float val) {
171 return duckdb_bind_value(prepared_statement, param_idx, val: Value::FLOAT(value: val));
172}
173
174duckdb_state duckdb_bind_double(duckdb_prepared_statement prepared_statement, idx_t param_idx, double val) {
175 return duckdb_bind_value(prepared_statement, param_idx, val: Value::DOUBLE(value: val));
176}
177
178duckdb_state duckdb_bind_date(duckdb_prepared_statement prepared_statement, idx_t param_idx, duckdb_date val) {
179 return duckdb_bind_value(prepared_statement, param_idx, val: Value::DATE(date: date_t(val.days)));
180}
181
182duckdb_state duckdb_bind_time(duckdb_prepared_statement prepared_statement, idx_t param_idx, duckdb_time val) {
183 return duckdb_bind_value(prepared_statement, param_idx, val: Value::TIME(time: dtime_t(val.micros)));
184}
185
186duckdb_state duckdb_bind_timestamp(duckdb_prepared_statement prepared_statement, idx_t param_idx,
187 duckdb_timestamp val) {
188 return duckdb_bind_value(prepared_statement, param_idx, val: Value::TIMESTAMP(timestamp: timestamp_t(val.micros)));
189}
190
191duckdb_state duckdb_bind_interval(duckdb_prepared_statement prepared_statement, idx_t param_idx, duckdb_interval val) {
192 return duckdb_bind_value(prepared_statement, param_idx, val: Value::INTERVAL(months: val.months, days: val.days, micros: val.micros));
193}
194
195duckdb_state duckdb_bind_varchar(duckdb_prepared_statement prepared_statement, idx_t param_idx, const char *val) {
196 try {
197 return duckdb_bind_value(prepared_statement, param_idx, val: Value(val));
198 } catch (...) {
199 return DuckDBError;
200 }
201}
202
203duckdb_state duckdb_bind_varchar_length(duckdb_prepared_statement prepared_statement, idx_t param_idx, const char *val,
204 idx_t length) {
205 try {
206 return duckdb_bind_value(prepared_statement, param_idx, val: Value(std::string(val, length)));
207 } catch (...) {
208 return DuckDBError;
209 }
210}
211
212duckdb_state duckdb_bind_decimal(duckdb_prepared_statement prepared_statement, idx_t param_idx, duckdb_decimal val) {
213 auto hugeint_val = duckdb_internal_hugeint(val: val.value);
214 if (val.width > duckdb::Decimal::MAX_WIDTH_INT64) {
215 return duckdb_bind_value(prepared_statement, param_idx, val: Value::DECIMAL(value: hugeint_val, width: val.width, scale: val.scale));
216 }
217 auto value = hugeint_val.lower;
218 return duckdb_bind_value(prepared_statement, param_idx, val: Value::DECIMAL(value: (int64_t)value, width: val.width, scale: val.scale));
219}
220
221duckdb_state duckdb_bind_blob(duckdb_prepared_statement prepared_statement, idx_t param_idx, const void *data,
222 idx_t length) {
223 return duckdb_bind_value(prepared_statement, param_idx, val: Value::BLOB(data: duckdb::const_data_ptr_cast(src: data), len: length));
224}
225
226duckdb_state duckdb_bind_null(duckdb_prepared_statement prepared_statement, idx_t param_idx) {
227 return duckdb_bind_value(prepared_statement, param_idx, val: Value());
228}
229
230duckdb_state duckdb_execute_prepared(duckdb_prepared_statement prepared_statement, duckdb_result *out_result) {
231 auto wrapper = reinterpret_cast<PreparedStatementWrapper *>(prepared_statement);
232 if (!wrapper || !wrapper->statement || wrapper->statement->HasError()) {
233 return DuckDBError;
234 }
235 auto result = wrapper->statement->Execute(values&: wrapper->values, allow_stream_result: false);
236 return duckdb_translate_result(result: std::move(result), out: out_result);
237}
238
239template <class T>
240void duckdb_destroy(void **wrapper) {
241 if (!wrapper) {
242 return;
243 }
244
245 auto casted = (T *)*wrapper;
246 if (casted) {
247 delete casted;
248 }
249 *wrapper = nullptr;
250}
251
252void duckdb_destroy_extracted(duckdb_extracted_statements *extracted_statements) {
253 duckdb_destroy<ExtractStatementsWrapper>(wrapper: reinterpret_cast<void **>(extracted_statements));
254}
255
256void duckdb_destroy_prepare(duckdb_prepared_statement *prepared_statement) {
257 duckdb_destroy<PreparedStatementWrapper>(wrapper: reinterpret_cast<void **>(prepared_statement));
258}
259