libduckdb_sys_queryscript/
lib.rs

1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4#![allow(deref_nullptr)]
5#![allow(improper_ctypes)]
6
7#[allow(clippy::all)]
8mod bindings {
9    include!(concat!(env!("OUT_DIR"), "/bindgen.rs"));
10}
11#[allow(clippy::all)]
12pub use bindings::*;
13
14pub const DuckDBError: duckdb_state = duckdb_state_DuckDBError;
15pub const DuckDBSuccess: duckdb_state = duckdb_state_DuckDBSuccess;
16
17pub use self::error::*;
18mod error;
19
20#[cfg(test)]
21mod tests {
22    use super::*;
23    use std::convert::TryFrom;
24    use std::ffi::{CStr, CString};
25    use std::mem;
26    use std::os::raw::c_char;
27    use std::ptr;
28
29    use arrow::array::{Array, ArrayData, Int32Array, StructArray};
30    use arrow::datatypes::DataType;
31    use arrow::ffi::{ArrowArray, FFI_ArrowArray, FFI_ArrowSchema};
32
33    unsafe fn print_int_result(mut result: duckdb_result) {
34        for i in 0..duckdb_column_count(&mut result) {
35            print!(
36                "{} ",
37                CStr::from_ptr(duckdb_column_name(&mut result, i)).to_string_lossy()
38            );
39        }
40        println!();
41        // print the data of the result
42        for row_idx in 0..duckdb_row_count(&mut result) {
43            for col_idx in 0..duckdb_column_count(&mut result) {
44                let val = duckdb_value_int32(&mut result, col_idx, row_idx);
45                print!("{val} ");
46            }
47            println!();
48        }
49    }
50
51    #[test]
52    fn test_query_arrow() {
53        unsafe {
54            // open db
55            let mut db: duckdb_database = ptr::null_mut();
56            let mut con: duckdb_connection = ptr::null_mut();
57            if duckdb_open(ptr::null_mut(), &mut db) != duckdb_state_DuckDBSuccess {
58                panic!("duckdb_open error")
59            }
60            if duckdb_connect(db, &mut con) != duckdb_state_DuckDBSuccess {
61                panic!("duckdb_connect error")
62            }
63            // create a table
64            let sql = CString::new("CREATE TABLE integers(i INTEGER, j INTEGER);").unwrap();
65            if duckdb_query(con, sql.as_ptr() as *const c_char, ptr::null_mut()) != duckdb_state_DuckDBSuccess {
66                panic!("CREATE TABLE error")
67            }
68
69            // insert three rows into the table
70            let sql = CString::new("INSERT INTO integers VALUES (3, 4), (5, 6), (7, NULL);").unwrap();
71            let mut result: duckdb_arrow = ptr::null_mut();
72            if duckdb_query_arrow(con, sql.as_ptr() as *const c_char, &mut result) != duckdb_state_DuckDBSuccess {
73                panic!("INSERT error")
74            }
75            assert_eq!(duckdb_arrow_rows_changed(result), 3);
76            duckdb_destroy_arrow(&mut result);
77
78            // query rows again
79            let mut result: duckdb_arrow = ptr::null_mut();
80            let sql = CString::new("select i, j from integers order by i desc").unwrap();
81            if duckdb_query_arrow(con, sql.as_ptr() as *const c_char, &mut result) != duckdb_state_DuckDBSuccess {
82                panic!("SELECT error")
83            }
84            assert_eq!(duckdb_arrow_row_count(result), 3);
85            assert_eq!(duckdb_arrow_column_count(result), 2);
86
87            let mut arrays = FFI_ArrowArray::empty();
88            let mut schema = FFI_ArrowSchema::empty();
89            if duckdb_query_arrow_schema(
90                result,
91                &mut std::ptr::addr_of_mut!(schema) as *mut _ as *mut duckdb_arrow_schema,
92            ) != duckdb_state_DuckDBSuccess
93            {
94                panic!("SELECT error")
95            }
96            if duckdb_query_arrow_array(
97                result,
98                &mut std::ptr::addr_of_mut!(arrays) as *mut _ as *mut duckdb_arrow_array,
99            ) != duckdb_state_DuckDBSuccess
100            {
101                panic!("SELECT error")
102            }
103            let arrow_array = ArrowArray::new(arrays, schema);
104            let array_data = ArrayData::try_from(arrow_array).expect("ok");
105            let struct_array = StructArray::from(array_data);
106            assert_eq!(struct_array.len(), 3);
107            assert_eq!(struct_array.columns().len(), 2);
108            assert_eq!(struct_array.column(0).data_type(), &DataType::Int32);
109            assert_eq!(struct_array.column(1).data_type(), &DataType::Int32);
110            let arr_i = struct_array.column(0).as_any().downcast_ref::<Int32Array>().unwrap();
111            assert_eq!(arr_i.value(0), 7);
112            assert_eq!(arr_i.value(1), 5);
113            assert_eq!(arr_i.value(2), 3);
114            let arr_j = struct_array.column(1).as_any().downcast_ref::<Int32Array>().unwrap();
115            assert!(arr_j.is_null(0));
116            assert_eq!(arr_j.value(1), 6);
117            assert_eq!(arr_j.value(2), 4);
118
119            let mut arrays: duckdb_arrow_array = ptr::null_mut();
120            if duckdb_query_arrow_array(result, &mut arrays) != duckdb_state_DuckDBSuccess {
121                panic!("SELECT error")
122            }
123            assert!(arrays.is_null());
124            duckdb_destroy_arrow(&mut result);
125            duckdb_disconnect(&mut con);
126            duckdb_close(&mut db);
127        }
128    }
129
130    #[test]
131    fn basic_api_usage() {
132        unsafe {
133            // open db
134            let mut db: duckdb_database = ptr::null_mut();
135            let mut con: duckdb_connection = ptr::null_mut();
136            if duckdb_open(ptr::null_mut(), &mut db) != duckdb_state_DuckDBSuccess {
137                panic!("duckdb_open error")
138            }
139            if duckdb_connect(db, &mut con) != duckdb_state_DuckDBSuccess {
140                panic!("duckdb_connect error")
141            }
142            // create a table
143            let sql = CString::new("CREATE TABLE integers(i INTEGER, j INTEGER);").unwrap();
144            if duckdb_query(con, sql.as_ptr() as *const c_char, ptr::null_mut()) != duckdb_state_DuckDBSuccess {
145                panic!("CREATE TABLE error")
146            }
147            // insert three rows into the table
148            let sql = CString::new("INSERT INTO integers VALUES (3, 4), (5, 6), (7, NULL);").unwrap();
149            if duckdb_query(con, sql.as_ptr() as *const c_char, ptr::null_mut()) != duckdb_state_DuckDBSuccess {
150                panic!("INSERT error")
151            }
152            // query rows again
153            let mut result: duckdb_result = mem::zeroed();
154            let sql = CString::new("select * from integers").unwrap();
155            if duckdb_query(con, sql.as_ptr() as *const c_char, &mut result) != duckdb_state_DuckDBSuccess {
156                panic!(
157                    "SELECT error: {}",
158                    CStr::from_ptr(duckdb_result_error(&mut result)).to_string_lossy()
159                )
160            }
161            assert_eq!(duckdb_row_count(&mut result), 3);
162            assert_eq!(duckdb_column_count(&mut result), 2);
163            print_int_result(result);
164            duckdb_destroy_result(&mut result);
165
166            // test prepare
167            let mut stmt: duckdb_prepared_statement = ptr::null_mut();
168            let sql = CString::new("select * from integers where i>?").unwrap();
169            if duckdb_prepare(con, sql.as_ptr() as *const c_char, &mut stmt) != duckdb_state_DuckDBSuccess {
170                panic!("Prepare error");
171            }
172            if duckdb_bind_int32(stmt, 1, 4) != duckdb_state_DuckDBSuccess {
173                panic!("Bind params error");
174            }
175            if duckdb_execute_prepared(stmt, &mut result) != duckdb_state_DuckDBSuccess {
176                panic!("Execute prepared error");
177            }
178            assert_eq!(duckdb_row_count(&mut result), 2);
179            assert_eq!(duckdb_column_count(&mut result), 2);
180            print_int_result(result);
181            duckdb_destroy_result(&mut result);
182
183            // test bind params again
184            if duckdb_bind_int32(stmt, 1, 5) != duckdb_state_DuckDBSuccess {
185                panic!("Bind params error");
186            }
187            if duckdb_execute_prepared(stmt, &mut result) != duckdb_state_DuckDBSuccess {
188                panic!("Execute prepared error");
189            }
190            assert_eq!(duckdb_row_count(&mut result), 1);
191            assert_eq!(duckdb_column_count(&mut result), 2);
192            print_int_result(result);
193            duckdb_destroy_result(&mut result);
194            duckdb_destroy_prepare(&mut stmt);
195
196            // clean up
197            duckdb_disconnect(&mut con);
198            duckdb_close(&mut db);
199        }
200    }
201}