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