Skip to main content

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