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