/src/duckdb/src/main/capi/duckdb-c.cpp
Line | Count | Source |
1 | | #include "duckdb/main/capi/capi_internal.hpp" |
2 | | |
3 | | using duckdb::CClientArrowOptionsWrapper; |
4 | | using duckdb::CClientContextWrapper; |
5 | | using duckdb::Connection; |
6 | | using duckdb::DatabaseWrapper; |
7 | | using duckdb::DBConfig; |
8 | | using duckdb::DBInstanceCacheWrapper; |
9 | | using duckdb::DuckDB; |
10 | | using duckdb::ErrorData; |
11 | | |
12 | 0 | duckdb_instance_cache duckdb_create_instance_cache() { |
13 | 0 | auto wrapper = new DBInstanceCacheWrapper(); |
14 | 0 | wrapper->instance_cache = duckdb::make_uniq<duckdb::DBInstanceCache>(); |
15 | 0 | return reinterpret_cast<duckdb_instance_cache>(wrapper); |
16 | 0 | } |
17 | | |
18 | 0 | void duckdb_destroy_instance_cache(duckdb_instance_cache *instance_cache) { |
19 | 0 | if (instance_cache && *instance_cache) { |
20 | 0 | auto wrapper = reinterpret_cast<DBInstanceCacheWrapper *>(*instance_cache); |
21 | 0 | delete wrapper; |
22 | 0 | *instance_cache = nullptr; |
23 | 0 | } |
24 | 0 | } |
25 | | |
26 | | duckdb_state duckdb_open_internal(DBInstanceCacheWrapper *cache, const char *path, duckdb_database *out, |
27 | 0 | duckdb_config config, char **out_error) { |
28 | 0 | auto wrapper = new DatabaseWrapper(); |
29 | 0 | try { |
30 | 0 | DBConfig default_config; |
31 | 0 | default_config.SetOptionByName("duckdb_api", "capi"); |
32 | |
|
33 | 0 | DBConfig *db_config = &default_config; |
34 | 0 | DBConfig *user_config = reinterpret_cast<DBConfig *>(config); |
35 | 0 | if (user_config) { |
36 | 0 | db_config = user_config; |
37 | 0 | } |
38 | |
|
39 | 0 | if (cache) { |
40 | 0 | duckdb::string path_str; |
41 | 0 | if (path) { |
42 | 0 | path_str = path; |
43 | 0 | } |
44 | 0 | wrapper->database = cache->instance_cache->GetOrCreateInstance(path_str, *db_config); |
45 | 0 | } else { |
46 | 0 | wrapper->database = duckdb::make_shared_ptr<DuckDB>(path, db_config); |
47 | 0 | } |
48 | |
|
49 | 0 | } catch (std::exception &ex) { |
50 | 0 | if (out_error) { |
51 | 0 | ErrorData parsed_error(ex); |
52 | 0 | *out_error = strdup(parsed_error.Message().c_str()); |
53 | 0 | } |
54 | 0 | delete wrapper; |
55 | 0 | return DuckDBError; |
56 | |
|
57 | 0 | } catch (...) { // LCOV_EXCL_START |
58 | 0 | if (out_error) { |
59 | 0 | *out_error = strdup("Unknown error"); |
60 | 0 | } |
61 | 0 | delete wrapper; |
62 | 0 | return DuckDBError; |
63 | 0 | } // LCOV_EXCL_STOP |
64 | | |
65 | 0 | *out = reinterpret_cast<duckdb_database>(wrapper); |
66 | 0 | return DuckDBSuccess; |
67 | 0 | } |
68 | | |
69 | | duckdb_state duckdb_get_or_create_from_cache(duckdb_instance_cache instance_cache, const char *path, |
70 | 0 | duckdb_database *out_database, duckdb_config config, char **out_error) { |
71 | 0 | if (!instance_cache) { |
72 | 0 | if (out_error) { |
73 | 0 | *out_error = strdup("instance cache cannot be nullptr"); |
74 | 0 | } |
75 | 0 | return DuckDBError; |
76 | 0 | } |
77 | 0 | auto cache = reinterpret_cast<DBInstanceCacheWrapper *>(instance_cache); |
78 | 0 | return duckdb_open_internal(cache, path, out_database, config, out_error); |
79 | 0 | } |
80 | | |
81 | 0 | duckdb_state duckdb_open_ext(const char *path, duckdb_database *out, duckdb_config config, char **error) { |
82 | 0 | return duckdb_open_internal(nullptr, path, out, config, error); |
83 | 0 | } |
84 | | |
85 | 0 | duckdb_state duckdb_open(const char *path, duckdb_database *out) { |
86 | 0 | return duckdb_open_ext(path, out, nullptr, nullptr); |
87 | 0 | } |
88 | | |
89 | 0 | void duckdb_close(duckdb_database *database) { |
90 | 0 | if (database && *database) { |
91 | 0 | auto wrapper = reinterpret_cast<DatabaseWrapper *>(*database); |
92 | 0 | delete wrapper; |
93 | 0 | *database = nullptr; |
94 | 0 | } |
95 | 0 | } |
96 | | |
97 | 0 | duckdb_state duckdb_connect(duckdb_database database, duckdb_connection *out) { |
98 | 0 | if (!database || !out) { |
99 | 0 | return DuckDBError; |
100 | 0 | } |
101 | | |
102 | 0 | auto wrapper = reinterpret_cast<DatabaseWrapper *>(database); |
103 | 0 | Connection *connection; |
104 | 0 | try { |
105 | 0 | connection = new Connection(*wrapper->database); |
106 | 0 | } catch (...) { // LCOV_EXCL_START |
107 | 0 | return DuckDBError; |
108 | 0 | } // LCOV_EXCL_STOP |
109 | | |
110 | 0 | *out = reinterpret_cast<duckdb_connection>(connection); |
111 | 0 | return DuckDBSuccess; |
112 | 0 | } |
113 | | |
114 | 0 | void duckdb_interrupt(duckdb_connection connection) { |
115 | 0 | if (!connection) { |
116 | 0 | return; |
117 | 0 | } |
118 | 0 | Connection *conn = reinterpret_cast<Connection *>(connection); |
119 | 0 | conn->Interrupt(); |
120 | 0 | } |
121 | | |
122 | 0 | duckdb_query_progress_type duckdb_query_progress(duckdb_connection connection) { |
123 | 0 | duckdb_query_progress_type query_progress_type; |
124 | 0 | query_progress_type.percentage = -1; |
125 | 0 | query_progress_type.total_rows_to_process = 0; |
126 | 0 | query_progress_type.rows_processed = 0; |
127 | 0 | if (!connection) { |
128 | 0 | return query_progress_type; |
129 | 0 | } |
130 | 0 | Connection *conn = reinterpret_cast<Connection *>(connection); |
131 | 0 | auto query_progress = conn->context->GetQueryProgress(); |
132 | 0 | query_progress_type.total_rows_to_process = query_progress.GetTotalRowsToProcess(); |
133 | 0 | query_progress_type.rows_processed = query_progress.GetRowsProcessed(); |
134 | 0 | query_progress_type.percentage = query_progress.GetPercentage(); |
135 | 0 | return query_progress_type; |
136 | 0 | } |
137 | | |
138 | 0 | void duckdb_disconnect(duckdb_connection *connection) { |
139 | 0 | if (connection && *connection) { |
140 | 0 | Connection *conn = reinterpret_cast<Connection *>(*connection); |
141 | 0 | delete conn; |
142 | 0 | *connection = nullptr; |
143 | 0 | } |
144 | 0 | } |
145 | | |
146 | 0 | void duckdb_connection_get_client_context(duckdb_connection connection, duckdb_client_context *out_context) { |
147 | 0 | if (!connection || !out_context) { |
148 | 0 | return; |
149 | 0 | } |
150 | 0 | Connection *conn = reinterpret_cast<Connection *>(connection); |
151 | 0 | try { |
152 | 0 | auto wrapper = new CClientContextWrapper(*conn->context); |
153 | 0 | *out_context = reinterpret_cast<duckdb_client_context>(wrapper); |
154 | 0 | } catch (...) { |
155 | 0 | *out_context = nullptr; |
156 | 0 | } |
157 | 0 | } |
158 | | |
159 | 0 | void duckdb_connection_get_arrow_options(duckdb_connection connection, duckdb_arrow_options *out_arrow_options) { |
160 | 0 | if (!connection || !out_arrow_options) { |
161 | 0 | return; |
162 | 0 | } |
163 | 0 | Connection *conn = reinterpret_cast<Connection *>(connection); |
164 | 0 | try { |
165 | 0 | auto client_properties = conn->context->GetClientProperties(); |
166 | 0 | auto wrapper = new CClientArrowOptionsWrapper(client_properties); |
167 | 0 | *out_arrow_options = reinterpret_cast<duckdb_arrow_options>(wrapper); |
168 | 0 | } catch (...) { |
169 | 0 | *out_arrow_options = nullptr; |
170 | 0 | } |
171 | 0 | } |
172 | | |
173 | 0 | idx_t duckdb_client_context_get_connection_id(duckdb_client_context context) { |
174 | 0 | auto wrapper = reinterpret_cast<CClientContextWrapper *>(context); |
175 | 0 | return wrapper->context.GetConnectionId(); |
176 | 0 | } |
177 | | |
178 | 0 | void duckdb_destroy_client_context(duckdb_client_context *context) { |
179 | 0 | if (context && *context) { |
180 | 0 | auto wrapper = reinterpret_cast<CClientContextWrapper *>(*context); |
181 | 0 | delete wrapper; |
182 | 0 | *context = nullptr; |
183 | 0 | } |
184 | 0 | } |
185 | | |
186 | 0 | void duckdb_destroy_arrow_options(duckdb_arrow_options *arrow_options) { |
187 | 0 | if (arrow_options && *arrow_options) { |
188 | 0 | auto wrapper = reinterpret_cast<CClientArrowOptionsWrapper *>(*arrow_options); |
189 | 0 | delete wrapper; |
190 | 0 | *arrow_options = nullptr; |
191 | 0 | } |
192 | 0 | } |
193 | | |
194 | 0 | duckdb_state duckdb_query(duckdb_connection connection, const char *query, duckdb_result *out) { |
195 | 0 | Connection *conn = reinterpret_cast<Connection *>(connection); |
196 | 0 | try { |
197 | 0 | auto result = conn->Query(query); |
198 | 0 | return DuckDBTranslateResult(std::move(result), out); |
199 | 0 | } catch (...) { |
200 | 0 | return DuckDBError; |
201 | 0 | } |
202 | 0 | } |
203 | | |
204 | 0 | const char *duckdb_library_version() { |
205 | 0 | return DuckDB::LibraryVersion(); |
206 | 0 | } |
207 | | |
208 | 0 | duckdb_value duckdb_get_table_names(duckdb_connection connection, const char *query, bool qualified) { |
209 | 0 | Connection *conn = reinterpret_cast<Connection *>(connection); |
210 | 0 | try { |
211 | 0 | auto table_names = conn->GetTableNames(query, qualified); |
212 | |
|
213 | 0 | auto count = table_names.size(); |
214 | 0 | auto ptr = malloc(count * sizeof(duckdb_value)); |
215 | 0 | if (!ptr) { |
216 | 0 | return nullptr; |
217 | 0 | } |
218 | 0 | auto list_values = reinterpret_cast<duckdb_value *>(ptr); |
219 | |
|
220 | 0 | try { |
221 | 0 | idx_t name_ix = 0; |
222 | 0 | for (const auto &name : table_names) { |
223 | 0 | list_values[name_ix] = duckdb_create_varchar(name.c_str()); |
224 | 0 | name_ix++; |
225 | 0 | } |
226 | |
|
227 | 0 | auto varchar_type = duckdb_create_logical_type(DUCKDB_TYPE_VARCHAR); |
228 | 0 | auto list_value = duckdb_create_list_value(varchar_type, list_values, count); |
229 | |
|
230 | 0 | for (idx_t i = 0; i < count; i++) { |
231 | 0 | duckdb_destroy_value(&list_values[i]); |
232 | 0 | } |
233 | 0 | duckdb_free(ptr); |
234 | 0 | duckdb_destroy_logical_type(&varchar_type); |
235 | |
|
236 | 0 | return list_value; |
237 | 0 | } catch (...) { |
238 | 0 | duckdb_free(ptr); |
239 | 0 | return nullptr; |
240 | 0 | } |
241 | 0 | } catch (...) { |
242 | 0 | return nullptr; |
243 | 0 | } |
244 | 0 | } |