Skip to content

Commit d613139

Browse files
authored
fix: appender crash (#296)
* test: add test for appender errors * fix: handle errors from duckdb_appender_flush * fix: pass double ptr to result_from_duckdb_appender this ensures that the pointer is correctly cleared when errors occur
1 parent d110924 commit d613139

File tree

4 files changed

+31
-10
lines changed

4 files changed

+31
-10
lines changed

src/appender/arrow.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ impl Appender<'_> {
3939
record_batch_to_duckdb_data_chunk(&record_batch, &mut data_chunk).map_err(|_op| Error::AppendError)?;
4040

4141
let rc = unsafe { duckdb_append_data_chunk(self.app, data_chunk.get_ptr()) };
42-
result_from_duckdb_appender(rc, self.app)
42+
result_from_duckdb_appender(rc, &mut self.app)
4343
}
4444
}
4545

src/appender/mod.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ impl Appender<'_> {
6767
params.__bind_in(self)?;
6868
// NOTE: we only check end_row return value
6969
let rc = unsafe { ffi::duckdb_appender_end_row(self.app) };
70-
result_from_duckdb_appender(rc, self.app)
70+
result_from_duckdb_appender(rc, &mut self.app)
7171
}
7272

7373
#[inline]
@@ -142,17 +142,18 @@ impl Appender<'_> {
142142

143143
/// Flush data into DB
144144
#[inline]
145-
pub fn flush(&mut self) {
145+
pub fn flush(&mut self) -> Result<()> {
146146
unsafe {
147-
ffi::duckdb_appender_flush(self.app);
147+
let res = ffi::duckdb_appender_flush(self.app);
148+
result_from_duckdb_appender(res, &mut self.app)
148149
}
149150
}
150151
}
151152

152153
impl Drop for Appender<'_> {
153154
fn drop(&mut self) {
154155
if !self.app.is_null() {
155-
self.flush();
156+
let _ = self.flush(); // can't safely handle failures here
156157
unsafe {
157158
ffi::duckdb_appender_close(self.app);
158159
ffi::duckdb_appender_destroy(&mut self.app);
@@ -253,4 +254,24 @@ mod test {
253254
assert_eq!(val, (d.as_micros() as i32,));
254255
Ok(())
255256
}
257+
258+
#[test]
259+
fn test_appender_error() -> Result<(), crate::Error> {
260+
let conn = Connection::open_in_memory()?;
261+
conn.execute(
262+
r"CREATE TABLE foo (
263+
foobar TEXT,
264+
foobar_split TEXT[] AS (split(trim(foobar), ','))
265+
);",
266+
[],
267+
)?;
268+
let mut appender = conn.appender("foo")?;
269+
match appender.append_row(["foo"]) {
270+
Err(crate::Error::DuckDBFailure(.., Some(msg))) => {
271+
assert_eq!(msg, "Call to EndRow before all rows have been appended to!")
272+
}
273+
_ => panic!("expected error"),
274+
}
275+
Ok(())
276+
}
256277
}

src/error.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,17 +225,17 @@ fn error_from_duckdb_code(code: ffi::duckdb_state, message: Option<String>) -> R
225225

226226
#[cold]
227227
#[inline]
228-
pub fn result_from_duckdb_appender(code: ffi::duckdb_state, mut appender: ffi::duckdb_appender) -> Result<()> {
228+
pub fn result_from_duckdb_appender(code: ffi::duckdb_state, appender: *mut ffi::duckdb_appender) -> Result<()> {
229229
if code == ffi::DuckDBSuccess {
230230
return Ok(());
231231
}
232232
unsafe {
233-
let message = if appender.is_null() {
233+
let message = if (*appender).is_null() {
234234
Some("appender is null".to_string())
235235
} else {
236-
let c_err = ffi::duckdb_appender_error(appender);
236+
let c_err = ffi::duckdb_appender_error(*appender);
237237
let message = Some(CStr::from_ptr(c_err).to_string_lossy().to_string());
238-
ffi::duckdb_appender_destroy(&mut appender);
238+
ffi::duckdb_appender_destroy(appender);
239239
message
240240
};
241241
error_from_duckdb_code(code, message)

src/inner_connection.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ impl InnerConnection {
102102
&mut c_app,
103103
)
104104
};
105-
result_from_duckdb_appender(r, c_app)?;
105+
result_from_duckdb_appender(r, &mut c_app)?;
106106
Ok(Appender::new(conn, c_app))
107107
}
108108

0 commit comments

Comments
 (0)