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