1use std::ptr;
4
5use arrow::array::RecordBatch;
6
7use crate::api::{QueryResult, QueryStream};
8
9use super::error::{clear_last_error, set_last_error, LAMINAR_ERR_NULL_POINTER, LAMINAR_OK};
10use super::schema::LaminarSchema;
11
12#[repr(C)]
14pub struct LaminarQueryResult {
15 inner: QueryResult,
16}
17
18impl LaminarQueryResult {
19 pub(crate) fn new(result: QueryResult) -> Self {
20 Self { inner: result }
21 }
22}
23
24#[repr(C)]
26pub struct LaminarQueryStream {
27 inner: QueryStream,
28}
29
30impl LaminarQueryStream {
31 pub(crate) fn new(stream: QueryStream) -> Self {
32 Self { inner: stream }
33 }
34}
35
36#[repr(C)]
38pub struct LaminarRecordBatch {
39 inner: RecordBatch,
40}
41
42impl LaminarRecordBatch {
43 pub(crate) fn new(batch: RecordBatch) -> Self {
44 Self { inner: batch }
45 }
46
47 pub(crate) fn into_inner(self) -> RecordBatch {
48 self.inner
49 }
50
51 pub(crate) fn inner(&self) -> &RecordBatch {
52 &self.inner
53 }
54}
55
56#[no_mangle]
62pub unsafe extern "C" fn laminar_result_schema(
63 result: *mut LaminarQueryResult,
64 out: *mut *mut LaminarSchema,
65) -> i32 {
66 clear_last_error();
67
68 if result.is_null() || out.is_null() {
69 return LAMINAR_ERR_NULL_POINTER;
70 }
71
72 let schema = unsafe { (*result).inner.schema() };
73 let handle = Box::new(LaminarSchema::new(schema));
74
75 unsafe { *out = Box::into_raw(handle) };
76 LAMINAR_OK
77}
78
79#[no_mangle]
85pub unsafe extern "C" fn laminar_result_num_rows(
86 result: *mut LaminarQueryResult,
87 out: *mut usize,
88) -> i32 {
89 clear_last_error();
90
91 if result.is_null() || out.is_null() {
92 return LAMINAR_ERR_NULL_POINTER;
93 }
94
95 unsafe {
96 *out = (*result).inner.num_rows();
97 }
98 LAMINAR_OK
99}
100
101#[no_mangle]
107pub unsafe extern "C" fn laminar_result_num_batches(
108 result: *mut LaminarQueryResult,
109 out: *mut usize,
110) -> i32 {
111 clear_last_error();
112
113 if result.is_null() || out.is_null() {
114 return LAMINAR_ERR_NULL_POINTER;
115 }
116
117 unsafe {
118 *out = (*result).inner.num_batches();
119 }
120 LAMINAR_OK
121}
122
123#[no_mangle]
129pub unsafe extern "C" fn laminar_result_get_batch(
130 result: *mut LaminarQueryResult,
131 index: usize,
132 out: *mut *mut LaminarRecordBatch,
133) -> i32 {
134 clear_last_error();
135
136 if result.is_null() || out.is_null() {
137 return LAMINAR_ERR_NULL_POINTER;
138 }
139
140 let result_ref = unsafe { &(*result).inner };
141
142 if let Some(batch) = result_ref.batch(index) {
143 let handle = Box::new(LaminarRecordBatch::new(batch.clone()));
144 unsafe { *out = Box::into_raw(handle) };
145 LAMINAR_OK
146 } else {
147 unsafe { *out = ptr::null_mut() };
148 LAMINAR_ERR_NULL_POINTER
149 }
150}
151
152#[no_mangle]
158pub unsafe extern "C" fn laminar_result_free(result: *mut LaminarQueryResult) {
159 if !result.is_null() {
160 drop(unsafe { Box::from_raw(result) });
161 }
162}
163
164#[no_mangle]
170pub unsafe extern "C" fn laminar_stream_schema(
171 stream: *mut LaminarQueryStream,
172 out: *mut *mut LaminarSchema,
173) -> i32 {
174 clear_last_error();
175
176 if stream.is_null() || out.is_null() {
177 return LAMINAR_ERR_NULL_POINTER;
178 }
179
180 let schema = unsafe { (*stream).inner.schema() };
181 let handle = Box::new(LaminarSchema::new(schema));
182
183 unsafe { *out = Box::into_raw(handle) };
184 LAMINAR_OK
185}
186
187#[no_mangle]
193pub unsafe extern "C" fn laminar_stream_next(
194 stream: *mut LaminarQueryStream,
195 out: *mut *mut LaminarRecordBatch,
196) -> i32 {
197 clear_last_error();
198
199 if stream.is_null() || out.is_null() {
200 return LAMINAR_ERR_NULL_POINTER;
201 }
202
203 let stream_ref = unsafe { &mut (*stream).inner };
204
205 match stream_ref.next() {
206 Ok(Some(batch)) => {
207 let handle = Box::new(LaminarRecordBatch::new(batch));
208 unsafe { *out = Box::into_raw(handle) };
209 LAMINAR_OK
210 }
211 Ok(None) => {
212 unsafe { *out = ptr::null_mut() };
213 LAMINAR_OK
214 }
215 Err(e) => {
216 unsafe { *out = ptr::null_mut() };
217 let code = e.code();
218 set_last_error(e);
219 code
220 }
221 }
222}
223
224#[no_mangle]
230pub unsafe extern "C" fn laminar_stream_try_next(
231 stream: *mut LaminarQueryStream,
232 out: *mut *mut LaminarRecordBatch,
233) -> i32 {
234 clear_last_error();
235
236 if stream.is_null() || out.is_null() {
237 return LAMINAR_ERR_NULL_POINTER;
238 }
239
240 let stream_ref = unsafe { &mut (*stream).inner };
241
242 match stream_ref.try_next() {
243 Ok(Some(batch)) => {
244 let handle = Box::new(LaminarRecordBatch::new(batch));
245 unsafe { *out = Box::into_raw(handle) };
246 LAMINAR_OK
247 }
248 Ok(None) => {
249 unsafe { *out = ptr::null_mut() };
250 LAMINAR_OK
251 }
252 Err(e) => {
253 unsafe { *out = ptr::null_mut() };
254 let code = e.code();
255 set_last_error(e);
256 code
257 }
258 }
259}
260
261#[no_mangle]
267pub unsafe extern "C" fn laminar_stream_is_active(
268 stream: *mut LaminarQueryStream,
269 out: *mut bool,
270) -> i32 {
271 clear_last_error();
272
273 if stream.is_null() || out.is_null() {
274 return LAMINAR_ERR_NULL_POINTER;
275 }
276
277 unsafe {
278 *out = (*stream).inner.is_active();
279 }
280 LAMINAR_OK
281}
282
283#[no_mangle]
289pub unsafe extern "C" fn laminar_stream_cancel(stream: *mut LaminarQueryStream) -> i32 {
290 clear_last_error();
291
292 if stream.is_null() {
293 return LAMINAR_ERR_NULL_POINTER;
294 }
295
296 unsafe {
297 (*stream).inner.cancel();
298 }
299 LAMINAR_OK
300}
301
302#[no_mangle]
308pub unsafe extern "C" fn laminar_stream_free(stream: *mut LaminarQueryStream) {
309 if !stream.is_null() {
310 drop(unsafe { Box::from_raw(stream) });
311 }
312}
313
314#[no_mangle]
320pub unsafe extern "C" fn laminar_batch_num_rows(
321 batch: *mut LaminarRecordBatch,
322 out: *mut usize,
323) -> i32 {
324 clear_last_error();
325
326 if batch.is_null() || out.is_null() {
327 return LAMINAR_ERR_NULL_POINTER;
328 }
329
330 unsafe {
331 *out = (*batch).inner.num_rows();
332 }
333 LAMINAR_OK
334}
335
336#[no_mangle]
342pub unsafe extern "C" fn laminar_batch_num_columns(
343 batch: *mut LaminarRecordBatch,
344 out: *mut usize,
345) -> i32 {
346 clear_last_error();
347
348 if batch.is_null() || out.is_null() {
349 return LAMINAR_ERR_NULL_POINTER;
350 }
351
352 unsafe {
353 *out = (*batch).inner.num_columns();
354 }
355 LAMINAR_OK
356}
357
358#[no_mangle]
364pub unsafe extern "C" fn laminar_batch_free(batch: *mut LaminarRecordBatch) {
365 if !batch.is_null() {
366 drop(unsafe { Box::from_raw(batch) });
367 }
368}
369
370#[cfg(test)]
371#[allow(clippy::borrow_as_ptr)]
372mod tests {
373 use super::*;
374 use crate::ffi::connection::{laminar_close, laminar_open, laminar_query};
375 use crate::ffi::schema::laminar_schema_free;
376
377 #[test]
378 fn test_result_schema() {
379 let mut conn: *mut super::super::connection::LaminarConnection = ptr::null_mut();
380 let mut result: *mut LaminarQueryResult = ptr::null_mut();
381 let mut schema: *mut LaminarSchema = ptr::null_mut();
382
383 unsafe {
385 laminar_open(&mut conn);
386
387 let create_sql = b"CREATE TABLE query_test (id BIGINT PRIMARY KEY, val DOUBLE)\0";
389 crate::ffi::connection::laminar_execute(
390 conn,
391 create_sql.as_ptr().cast(),
392 ptr::null_mut(),
393 );
394
395 let query_sql = b"SELECT * FROM query_test\0";
396 let rc = laminar_query(conn, query_sql.as_ptr().cast(), &mut result);
397 assert_eq!(rc, LAMINAR_OK);
398
399 let rc = laminar_result_schema(result, &mut schema);
401 assert_eq!(rc, LAMINAR_OK);
402 assert!(!schema.is_null());
403
404 laminar_schema_free(schema);
405 laminar_result_free(result);
406 laminar_close(conn);
407 }
408 }
409
410 #[test]
411 fn test_result_counts() {
412 let mut conn: *mut super::super::connection::LaminarConnection = ptr::null_mut();
413 let mut result: *mut LaminarQueryResult = ptr::null_mut();
414
415 unsafe {
417 laminar_open(&mut conn);
418
419 let create_sql = b"CREATE TABLE count_test (id BIGINT PRIMARY KEY)\0";
421 crate::ffi::connection::laminar_execute(
422 conn,
423 create_sql.as_ptr().cast(),
424 ptr::null_mut(),
425 );
426
427 let query_sql = b"SELECT * FROM count_test\0";
428 laminar_query(conn, query_sql.as_ptr().cast(), &mut result);
429
430 let mut num_rows: usize = 999;
431 let rc = laminar_result_num_rows(result, &mut num_rows);
432 assert_eq!(rc, LAMINAR_OK);
433 assert_eq!(num_rows, 0); let mut num_batches: usize = 999;
436 let rc = laminar_result_num_batches(result, &mut num_batches);
437 assert_eq!(rc, LAMINAR_OK);
438
439 laminar_result_free(result);
440 laminar_close(conn);
441 }
442 }
443
444 #[test]
445 fn test_batch_free_null() {
446 unsafe {
448 laminar_batch_free(ptr::null_mut());
449 }
450 }
452}