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