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 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 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 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 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 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 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 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 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 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 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 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 duckdb_disconnect(&mut con);
200 duckdb_close(&mut db);
201 }
202 }
203}