laminar_db/ffi/
connection.rs1use std::ffi::{c_char, CStr};
4use std::ptr;
5
6use crate::api::Connection;
7
8use super::error::{
9 clear_last_error, set_last_error, LAMINAR_ERR_INVALID_UTF8, LAMINAR_ERR_NULL_POINTER,
10 LAMINAR_OK,
11};
12use super::query::{LaminarQueryResult, LaminarQueryStream};
13
14#[repr(C)]
16pub struct LaminarConnection {
17 pub(crate) inner: Connection,
18}
19
20#[no_mangle]
26pub unsafe extern "C" fn laminar_open(out: *mut *mut LaminarConnection) -> i32 {
27 clear_last_error();
28
29 if out.is_null() {
30 return LAMINAR_ERR_NULL_POINTER;
31 }
32
33 match Connection::open() {
34 Ok(conn) => {
35 let handle = Box::new(LaminarConnection { inner: conn });
36 unsafe { *out = Box::into_raw(handle) };
37 LAMINAR_OK
38 }
39 Err(e) => {
40 let code = e.code();
41 set_last_error(e);
42 code
43 }
44 }
45}
46
47#[no_mangle]
53pub unsafe extern "C" fn laminar_close(conn: *mut LaminarConnection) -> i32 {
54 clear_last_error();
55
56 if conn.is_null() {
57 return LAMINAR_ERR_NULL_POINTER;
58 }
59
60 let handle = unsafe { Box::from_raw(conn) };
61 match handle.inner.close() {
62 Ok(()) => LAMINAR_OK,
63 Err(e) => {
64 let code = e.code();
65 set_last_error(e);
66 code
67 }
68 }
69}
70
71#[no_mangle]
78pub unsafe extern "C" fn laminar_execute(
79 conn: *mut LaminarConnection,
80 sql: *const c_char,
81 out: *mut *mut LaminarQueryResult,
82) -> i32 {
83 clear_last_error();
84
85 if conn.is_null() || sql.is_null() {
86 return LAMINAR_ERR_NULL_POINTER;
87 }
88
89 let Ok(sql_str) = (unsafe { CStr::from_ptr(sql) }).to_str() else {
90 return LAMINAR_ERR_INVALID_UTF8;
91 };
92
93 let conn_ref = unsafe { &(*conn).inner };
94
95 match conn_ref.execute(sql_str) {
96 Ok(result) => {
97 use crate::api::ExecuteResult;
98 match result {
99 ExecuteResult::Query(stream) => match stream.collect() {
100 Ok(query_result) => {
101 if !out.is_null() {
102 let handle = Box::new(LaminarQueryResult::new(query_result));
103 unsafe { *out = Box::into_raw(handle) };
104 }
105 LAMINAR_OK
106 }
107 Err(e) => {
108 let code = e.code();
109 set_last_error(e);
110 code
111 }
112 },
113 ExecuteResult::Metadata(batch) => {
114 if !out.is_null() {
115 let query_result = crate::api::QueryResult::from_batch(batch);
116 let handle = Box::new(LaminarQueryResult::new(query_result));
117 unsafe { *out = Box::into_raw(handle) };
118 }
119 LAMINAR_OK
120 }
121 ExecuteResult::Ddl(_) | ExecuteResult::RowsAffected(_) => {
122 if !out.is_null() {
123 unsafe { *out = ptr::null_mut() };
124 }
125 LAMINAR_OK
126 }
127 }
128 }
129 Err(e) => {
130 let code = e.code();
131 set_last_error(e);
132 code
133 }
134 }
135}
136
137#[no_mangle]
143pub unsafe extern "C" fn laminar_query(
144 conn: *mut LaminarConnection,
145 sql: *const c_char,
146 out: *mut *mut LaminarQueryResult,
147) -> i32 {
148 clear_last_error();
149
150 if conn.is_null() || sql.is_null() || out.is_null() {
151 return LAMINAR_ERR_NULL_POINTER;
152 }
153
154 let Ok(sql_str) = (unsafe { CStr::from_ptr(sql) }).to_str() else {
155 return LAMINAR_ERR_INVALID_UTF8;
156 };
157
158 let conn_ref = unsafe { &(*conn).inner };
159
160 match conn_ref.query(sql_str) {
161 Ok(result) => {
162 let handle = Box::new(LaminarQueryResult::new(result));
163 unsafe { *out = Box::into_raw(handle) };
164 LAMINAR_OK
165 }
166 Err(e) => {
167 let code = e.code();
168 set_last_error(e);
169 code
170 }
171 }
172}
173
174#[no_mangle]
180pub unsafe extern "C" fn laminar_query_stream(
181 conn: *mut LaminarConnection,
182 sql: *const c_char,
183 out: *mut *mut LaminarQueryStream,
184) -> i32 {
185 clear_last_error();
186
187 if conn.is_null() || sql.is_null() || out.is_null() {
188 return LAMINAR_ERR_NULL_POINTER;
189 }
190
191 let Ok(sql_str) = (unsafe { CStr::from_ptr(sql) }).to_str() else {
192 return LAMINAR_ERR_INVALID_UTF8;
193 };
194
195 let conn_ref = unsafe { &(*conn).inner };
196
197 match conn_ref.query_stream(sql_str) {
198 Ok(stream) => {
199 let handle = Box::new(LaminarQueryStream::new(stream));
200 unsafe { *out = Box::into_raw(handle) };
201 LAMINAR_OK
202 }
203 Err(e) => {
204 let code = e.code();
205 set_last_error(e);
206 code
207 }
208 }
209}
210
211#[no_mangle]
217pub unsafe extern "C" fn laminar_start(conn: *mut LaminarConnection) -> i32 {
218 clear_last_error();
219
220 if conn.is_null() {
221 return LAMINAR_ERR_NULL_POINTER;
222 }
223
224 let conn_ref = unsafe { &(*conn).inner };
225
226 match conn_ref.start() {
227 Ok(()) => LAMINAR_OK,
228 Err(e) => {
229 let code = e.code();
230 set_last_error(e);
231 code
232 }
233 }
234}
235
236#[no_mangle]
242pub unsafe extern "C" fn laminar_is_closed(conn: *mut LaminarConnection, out: *mut bool) -> i32 {
243 clear_last_error();
244
245 if conn.is_null() || out.is_null() {
246 return LAMINAR_ERR_NULL_POINTER;
247 }
248
249 unsafe {
250 *out = (*conn).inner.is_closed();
251 }
252 LAMINAR_OK
253}
254
255#[cfg(test)]
256#[allow(clippy::borrow_as_ptr)]
257mod tests {
258 use super::*;
259
260 #[test]
261 fn test_open_close() {
262 let mut conn: *mut LaminarConnection = ptr::null_mut();
263 let rc = unsafe { laminar_open(&mut conn) };
265 assert_eq!(rc, LAMINAR_OK);
266 assert!(!conn.is_null());
267
268 let rc = unsafe { laminar_close(conn) };
270 assert_eq!(rc, LAMINAR_OK);
271 }
272
273 #[test]
274 fn test_open_null_pointer() {
275 let rc = unsafe { laminar_open(ptr::null_mut()) };
277 assert_eq!(rc, LAMINAR_ERR_NULL_POINTER);
278 }
279
280 #[test]
281 fn test_close_null_pointer() {
282 let rc = unsafe { laminar_close(ptr::null_mut()) };
284 assert_eq!(rc, LAMINAR_ERR_NULL_POINTER);
285 }
286
287 #[test]
288 fn test_execute_create_source() {
289 let mut conn: *mut LaminarConnection = ptr::null_mut();
290 unsafe {
292 let rc = laminar_open(&mut conn);
293 assert_eq!(rc, LAMINAR_OK);
294
295 let sql = b"CREATE SOURCE ffi_test (id BIGINT, name VARCHAR)\0";
296 let rc = laminar_execute(conn, sql.as_ptr().cast(), ptr::null_mut());
297 assert_eq!(rc, LAMINAR_OK);
298
299 laminar_close(conn);
300 }
301 }
302
303 #[test]
304 fn test_is_closed() {
305 let mut conn: *mut LaminarConnection = ptr::null_mut();
306 let mut is_closed = true;
307
308 unsafe {
310 laminar_open(&mut conn);
311 let rc = laminar_is_closed(conn, &mut is_closed);
312 assert_eq!(rc, LAMINAR_OK);
313 assert!(!is_closed);
314
315 laminar_close(conn);
316 }
317 }
318}