1use std::{
2 cell::OnceCell,
3 ffi::CStr,
4 ops::{Deref, DerefMut},
5 sync::Arc,
6};
7
8use crate::ffi::{
9 duckdb_column_count, duckdb_column_logical_type, duckdb_column_name, duckdb_destroy_result,
10 duckdb_result_return_type, duckdb_result_statement_type, duckdb_result_type, DUCKDB_TYPE,
11};
12
13use crate::{
14 error::{DuckDBConversionError, Error, Result},
15 ffi,
16 raw::row::DuckRow,
17 raw::statement::StatementType,
18 result_set::ResultSet,
19 types::{LogicalType, TypeInfo},
20};
21
22use super::data_chunk::DataChunk;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30#[non_exhaustive]
31pub enum ResultType {
32 QueryResult,
34 ChangedRows,
36 Nothing,
38 Invalid,
40 Unknown(duckdb_result_type),
42}
43
44impl ResultType {
45 #[must_use]
47 pub fn from_raw(raw: duckdb_result_type) -> ResultType {
48 use crate::ffi as f;
49 match raw {
50 f::duckdb_result_type_DUCKDB_RESULT_TYPE_QUERY_RESULT => ResultType::QueryResult,
51 f::duckdb_result_type_DUCKDB_RESULT_TYPE_CHANGED_ROWS => ResultType::ChangedRows,
52 f::duckdb_result_type_DUCKDB_RESULT_TYPE_NOTHING => ResultType::Nothing,
53 f::duckdb_result_type_DUCKDB_RESULT_TYPE_INVALID => ResultType::Invalid,
54 other => ResultType::Unknown(other),
55 }
56 }
57}
58
59pub struct DuckResult {
70 res: ffi::duckdb_result,
71 chunk: Option<DataChunk>,
72 column_names: OnceCell<Arc<[Box<str>]>>,
76 column_types: Box<[DUCKDB_TYPE]>,
79 column_schema: Arc<[TypeInfo]>,
83 statement_type: StatementType,
85 result_type: ResultType,
87 pub col_count: u64,
89 cache: Vec<DuckRow>,
94 cursor: usize,
96 exhausted: bool,
98 rewind_enabled: bool,
101 peeked: Option<DuckRow>,
104}
105
106impl DuckResult {
107 pub fn new(mut result: ffi::duckdb_result) -> DuckResult {
112 let mut res = DuckResult {
113 col_count: unsafe { duckdb_column_count(&mut result) },
117 statement_type: StatementType::from_raw(unsafe {
120 duckdb_result_statement_type(result)
121 }),
122 result_type: ResultType::from_raw(unsafe { duckdb_result_return_type(result) }),
124 res: result,
125 chunk: None,
126 column_names: OnceCell::new(),
127 column_types: Box::new([]),
128 column_schema: Arc::from([]),
129 cache: Vec::new(),
130 cursor: 0,
131 exhausted: false,
132 rewind_enabled: false,
133 peeked: None,
134 };
135 res.resolve_columns_name().expect("failed to resolve column names");
136 res.resolve_columns_types().expect("failed to resolve column types");
137 res.resolve_columns_schema();
138 res
139 }
140
141 #[inline]
148 fn resolve_columns_schema(&mut self) {
149 let mut schema = Vec::with_capacity(self.col_count as usize);
150 for i in 0..self.col_count {
151 let raw = unsafe { duckdb_column_logical_type(&mut self.res, i) };
154 let info = LogicalType::from_raw(raw)
155 .map(|lt| lt.describe())
156 .unwrap_or_else(|_| TypeInfo::Scalar(self.column_types[i as usize]));
159 schema.push(info);
160 }
161 self.column_schema = Arc::from(schema);
162 }
163
164 #[inline]
165 fn get_col_type(
167 &mut self,
168 col_index: u64,
169 ) -> DUCKDB_TYPE {
170 unsafe { ffi::duckdb_column_type(&mut self.res, col_index) }
172 }
173
174 #[inline]
175 fn resolve_columns_types(&mut self) -> Result<()> {
176 let mut col_types = Box::<[DUCKDB_TYPE]>::new_uninit_slice(self.col_count as usize);
178
179 for each in 0..self.col_count {
180 let temp_col_type = self.get_col_type(each);
183 unsafe {
186 col_types[each as usize].as_mut_ptr().write(temp_col_type);
187 }
188 }
189 self.column_types = unsafe { col_types.assume_init() };
191 Ok(())
192 }
193
194 #[inline]
195 fn resolve_columns_name(&mut self) -> Result<()> {
196 let names = (0..self.col_count)
197 .map(|i| {
198 let raw = unsafe { duckdb_column_name(&mut self.res, i) };
202 if raw.is_null() {
203 return Err(Error::InvalidColumnIndex(i as usize));
204 }
205 unsafe { CStr::from_ptr(raw) }
207 .to_str()
208 .map(|s| s.to_string().into_boxed_str())
209 .map_err(|e| {
210 Error::ConversionError(DuckDBConversionError::ConversionError(
211 e.to_string(),
212 ))
213 })
214 })
215 .collect::<Result<Vec<Box<str>>>>()?;
216
217 self.column_names
218 .set(Arc::from(names))
219 .map_err(|_| Error::UNKNOWN("column names already set".into()))
220 }
221
222 fn advance(&mut self) -> Option<()> {
227 loop {
228 if self.chunk.is_none() {
229 let next_chunk = DataChunk::from_result(self);
232 match next_chunk {
233 None => return None,
234 Some(Err(_)) => {
235 self.chunk = None;
236 return None;
237 },
238 Some(Ok(chunk)) => {
239 self.chunk = Some(chunk);
240 },
241 }
242 }
243 let the_chunk = self.chunk.as_mut().unwrap();
244 if the_chunk.row_count() == 0 {
246 self.chunk = None;
247 return None;
248 }
249 if the_chunk.next_row().is_some() {
251 let row_chunk = **the_chunk;
252 if row_chunk.is_null() {
253 panic!("Data chunk is null");
254 }
255 return Some(());
256 } else {
257 self.chunk = None;
258 }
260 }
261 }
262
263 fn pull_next(&mut self) -> Option<Result<DuckRow>> {
266 if self.advance().is_some() {
267 Some(self.current())
268 } else {
269 None
270 }
271 }
272}
273
274impl DuckResult {
276 pub fn current(&mut self) -> Result<DuckRow> {
282 let col_names = self.column_names.get().expect("column names resolved in new()").clone();
284 let chunk = self.chunk.as_mut().unwrap();
285 DuckRow::from_chunk(chunk, col_names, &self.column_types)
286 }
287
288 #[allow(unused)]
292 #[inline]
293 pub fn changes(&mut self) -> u64 {
294 unsafe { ffi::duckdb_rows_changed(&mut self.res) }
296 }
297
298 #[allow(unused)]
300 #[inline]
301 pub fn column_count(&self) -> u64 {
302 self.col_count
303 }
304
305 #[allow(unused)]
315 #[inline]
316 pub fn column_type(
317 &self,
318 col_index: usize,
319 ) -> Result<DUCKDB_TYPE> {
320 if col_index >= self.col_count as usize {
321 return Err(Error::InvalidColumnIndex(col_index));
322 }
323 Ok(self.column_types[col_index])
324 }
325
326 #[must_use]
328 #[inline]
329 pub fn column_schema(&self) -> &[TypeInfo] {
330 &self.column_schema
331 }
332
333 #[must_use]
335 #[inline]
336 pub fn statement_type(&self) -> StatementType {
337 self.statement_type
338 }
339
340 #[must_use]
342 #[inline]
343 pub fn result_type(&self) -> ResultType {
344 self.result_type
345 }
346
347 #[must_use]
352 #[inline]
353 pub(crate) fn column_schema_arc(&self) -> Arc<[TypeInfo]> {
354 Arc::clone(&self.column_schema)
355 }
356
357 #[allow(unused)]
363 #[inline]
364 pub fn column_logical_type(
365 &self,
366 col_index: usize,
367 ) -> Result<&TypeInfo> {
368 self.column_schema.get(col_index).ok_or(Error::InvalidColumnIndex(col_index))
369 }
370
371 #[allow(unused)]
377 #[inline]
378 pub fn column_name(
379 &self,
380 col_index: usize,
381 ) -> Result<&str> {
382 if col_index >= self.col_count as usize {
383 return Err(Error::InvalidColumnIndex(col_index));
384 }
385 Ok(&self.column_names.get().unwrap()[col_index])
386 }
387
388 #[allow(unused)]
390 #[inline]
391 pub fn column_names(&self) -> &[Box<str>] {
392 self.column_names.get().map(|v| v.as_ref()).unwrap_or(&[])
393 }
394
395 pub fn enable_rewind(&mut self) {
404 self.rewind_enabled = true;
405 }
406
407 #[allow(unused)]
410 #[inline]
411 pub fn column_idx(
412 &self,
413 col_name: &str,
414 ) -> Option<usize> {
415 self.column_names.get().unwrap().iter().position(|name| name.as_ref() == col_name)
416 }
417
418 pub fn exists(&mut self) -> Result<bool> {
425 if self.rewind_enabled && self.cursor < self.cache.len() {
426 return Ok(true);
427 }
428 if self.peeked.is_some() {
429 return Ok(true);
430 }
431 if self.exhausted {
432 return Ok(false);
433 }
434 match self.pull_next() {
435 Some(Ok(row)) => {
436 self.peeked = Some(row);
439 Ok(true)
440 },
441 Some(Err(e)) => Err(e),
442 None => {
443 self.exhausted = true;
444 Ok(false)
445 },
446 }
447 }
448
449 pub fn rewind(&mut self) {
455 self.cursor = 0;
456 }
457
458 pub fn materialize(mut self) -> Result<ResultSet> {
468 let changes = self.changes();
469 let column_names = self.column_names().to_vec().into_boxed_slice();
470 let column_schema = self.column_schema_arc();
472 let statement_type = self.statement_type();
473 let result_type = self.result_type();
474 let mut rows = Vec::new();
475 for row in self {
476 rows.push(row?);
477 }
478 Ok(ResultSet::new(rows, changes, column_names, column_schema, statement_type, result_type))
479 }
480}
481
482impl Iterator for DuckResult {
483 type Item = Result<DuckRow>;
484
485 fn next(&mut self) -> Option<Self::Item> {
486 if self.rewind_enabled && self.cursor < self.cache.len() {
487 let row = self.cache[self.cursor].clone();
488 self.cursor += 1;
489 return Some(Ok(row));
490 }
491 if let Some(row) = self.peeked.take() {
492 if self.rewind_enabled {
493 self.cache.push(row.clone());
494 self.cursor += 1;
495 }
496 return Some(Ok(row));
497 }
498 if self.exhausted {
499 return None;
500 }
501 match self.pull_next() {
502 Some(Ok(row)) => {
503 if self.rewind_enabled {
504 self.cache.push(row.clone());
505 self.cursor += 1;
506 }
507 Some(Ok(row))
508 },
509 Some(Err(e)) => {
510 self.exhausted = true;
511 Some(Err(e))
512 },
513 None => {
514 self.exhausted = true;
515 None
516 },
517 }
518 }
519
520 fn count(mut self) -> usize
529 where
530 Self: Sized,
531 {
532 let mut n = 0usize;
533 if self.rewind_enabled && self.cursor < self.cache.len() {
534 n += self.cache.len() - self.cursor;
535 self.cursor = self.cache.len();
536 }
537 if self.peeked.take().is_some() {
538 n += 1;
539 }
540 if self.exhausted {
541 return n;
542 }
543 while self.advance().is_some() {
544 n += 1;
545 }
546 n
547 }
548}
549
550impl Deref for DuckResult {
551 type Target = ffi::duckdb_result;
552
553 fn deref(&self) -> &Self::Target {
554 &self.res
555 }
556}
557impl DerefMut for DuckResult {
558 fn deref_mut(&mut self) -> &mut Self::Target {
559 &mut self.res
560 }
561}
562impl Drop for DuckResult {
563 fn drop(&mut self) {
564 unsafe {
567 duckdb_destroy_result(&mut self.res);
568 }
569 }
570}
571
572#[cfg(test)]
573#[allow(clippy::undocumented_unsafe_blocks)]
574mod tests {
575 use crate::{
576 config::Config,
577 error::Error,
578 ffi::{
579 DUCKDB_TYPE_DUCKDB_TYPE_DECIMAL, DUCKDB_TYPE_DUCKDB_TYPE_INTEGER,
580 DUCKDB_TYPE_DUCKDB_TYPE_VARCHAR,
581 },
582 helpers::path::path_to_cstring,
583 raw::connection::RawConnection,
584 raw::result::ResultType,
585 raw::statement::StatementType,
586 result_set::ResultSet,
587 types::value::DuckValue,
588 };
589
590 fn get_test_connection() -> RawConnection {
591 let c_path = path_to_cstring(":memory:".as_ref()).unwrap();
592 let config = Config::default().with("duckdb_api", "rust").unwrap();
593 RawConnection::open_with_flags(&c_path, config).unwrap()
594 }
595
596 #[test]
597 fn result_metadata_reports_columns_and_lookup_failures() {
598 let con = get_test_connection();
599 let mut stmt = con.prepare("SELECT 42::INTEGER AS id, 'duck'::VARCHAR AS label").unwrap();
600 let result = stmt.execute().unwrap();
601
602 assert_eq!(result.column_count(), 2);
603 assert_eq!(result.column_type(0), Ok(DUCKDB_TYPE_DUCKDB_TYPE_INTEGER));
604 assert_eq!(result.column_type(1), Ok(DUCKDB_TYPE_DUCKDB_TYPE_VARCHAR));
605 assert_eq!(result.column_name(0), Ok("id"));
606 assert_eq!(result.column_name(1), Ok("label"));
607 assert_eq!(
608 result.column_names().iter().map(AsRef::as_ref).collect::<Vec<_>>(),
609 ["id", "label"]
610 );
611 assert_eq!(result.column_idx("id"), Some(0));
612 assert_eq!(result.column_idx("label"), Some(1));
613 assert_eq!(result.column_idx("missing"), None);
614 assert_eq!(result.column_type(2), Err(Error::InvalidColumnIndex(2)));
615 assert_eq!(result.column_name(usize::MAX), Err(Error::InvalidColumnIndex(usize::MAX)));
616 }
617
618 #[test]
621 fn column_schema_preserves_decimal_precision_and_nesting() {
622 use crate::types::TypeInfo;
623 let con = get_test_connection();
624 let mut stmt = con
625 .prepare("SELECT CAST(1.5 AS DECIMAL(9,3)) AS d, [10, 20]::INTEGER[] AS xs")
626 .unwrap();
627 let result = stmt.execute().unwrap();
628
629 assert_eq!(result.column_type(0), Ok(DUCKDB_TYPE_DUCKDB_TYPE_DECIMAL));
631 assert_eq!(result.column_logical_type(0), Ok(&TypeInfo::Decimal { width: 9, scale: 3 }));
633 assert_eq!(
634 result.column_logical_type(1),
635 Ok(&TypeInfo::List(Box::new(TypeInfo::Scalar(DUCKDB_TYPE_DUCKDB_TYPE_INTEGER))))
636 );
637 assert_eq!(result.column_schema().len(), 2);
638 assert!(matches!(result.column_logical_type(2), Err(Error::InvalidColumnIndex(2))));
639 }
640
641 #[test]
643 fn materialized_result_carries_column_schema() {
644 use crate::types::TypeInfo;
645 let con = get_test_connection();
646 let set = con
647 .prepare("SELECT CAST(2.25 AS DECIMAL(6,2)) AS d")
648 .unwrap()
649 .execute()
650 .unwrap()
651 .materialize()
652 .unwrap();
653 assert_eq!(set.column_schema(), &[TypeInfo::Decimal { width: 6, scale: 2 }]);
654 }
655
656 #[test]
659 fn select_reports_query_result_classification() {
660 let mut con = get_test_connection();
661 let result = con.query("SELECT 1 AS a").unwrap();
662 assert_eq!(result.statement_type(), StatementType::Select);
663 assert_eq!(result.result_type(), ResultType::QueryResult);
664
665 let set = con.prepare("SELECT 1 AS a").unwrap().execute().unwrap().materialize().unwrap();
666 assert_eq!(set.statement_type(), StatementType::Select);
667 assert_eq!(set.result_type(), ResultType::QueryResult);
668 }
669
670 #[test]
673 fn insert_reports_changed_rows_classification() {
674 let mut con = get_test_connection();
675 con.query("CREATE TABLE t (id INTEGER)").unwrap();
676 let result = con.query("INSERT INTO t VALUES (1), (2)").unwrap();
677 assert_eq!(result.statement_type(), StatementType::Insert);
678 assert_eq!(result.result_type(), ResultType::ChangedRows);
679 }
680
681 #[test]
682 fn result_type_preserves_unknown_values() {
683 assert_eq!(ResultType::from_raw(9_999), ResultType::Unknown(9_999));
684 }
685
686 #[test]
687 fn materialize_select_preserves_rows_columns_and_values() {
688 let con = get_test_connection();
689 let mut stmt = con
690 .prepare("SELECT * FROM (VALUES (1, 'one'), (2, 'two')) AS t(id, label) ORDER BY id")
691 .unwrap();
692 let result = stmt.execute().unwrap().materialize().unwrap();
693
694 assert_eq!(result.len(), 2);
695 assert!(!result.is_empty());
696 assert_eq!(result.changes(), 0);
697 assert_eq!(
698 result.column_names().iter().map(AsRef::as_ref).collect::<Vec<_>>(),
699 ["id", "label"]
700 );
701 assert_eq!(result.rows()[0].get("id"), Some(&DuckValue::Int(1)));
702 assert_eq!(result.rows()[0].get("label"), Some(&DuckValue::Text("one".into())));
703 assert_eq!(result.rows()[1].get("id"), Some(&DuckValue::Int(2)));
704 assert_eq!(result.rows()[1].get("label"), Some(&DuckValue::Text("two".into())));
705 }
706
707 #[test]
708 fn materialize_empty_select_preserves_schema() {
709 let con = get_test_connection();
710 let mut stmt =
711 con.prepare("SELECT NULL::INTEGER AS id, NULL::VARCHAR AS label WHERE FALSE").unwrap();
712 let result = stmt.execute().unwrap().materialize().unwrap();
713
714 assert!(result.is_empty());
715 assert_eq!(result.len(), 0);
716 assert_eq!(result.changes(), 0);
717 assert_eq!(
718 result.column_names().iter().map(AsRef::as_ref).collect::<Vec<_>>(),
719 ["id", "label"]
720 );
721 assert!(result.first().is_none());
722 }
723
724 #[test]
725 fn materialize_dml_preserves_affected_row_count() {
726 let mut con = get_test_connection();
727 let _ = con.query("CREATE TABLE t (v INTEGER)").unwrap();
728 let result =
729 con.query("INSERT INTO t VALUES (1), (2), (3)").unwrap().materialize().unwrap();
730
731 assert_eq!(result.changes(), 3);
732 assert_eq!(result.len(), 1);
733 assert_eq!(result.first().unwrap().get_idx(0), Some(&DuckValue::BigInt(3)));
734 }
735
736 #[test]
737 fn result_set_is_send_and_sync() {
738 fn assert_send_sync<T: Send + Sync>() {}
739 assert_send_sync::<ResultSet>();
740 }
741
742 #[test]
745 fn forward_iteration_without_rewind_yields_all_rows() {
746 let mut con = get_test_connection();
747 con.query("CREATE TABLE t (v INTEGER)").unwrap();
748 con.query("INSERT INTO t VALUES (1), (2), (3)").unwrap();
749
750 let mut stmt = con.prepare("SELECT v FROM t ORDER BY v").unwrap();
751 let result = stmt.execute().unwrap();
752 let rows: Vec<_> = result.collect::<Result<_, _>>().unwrap();
753 assert_eq!(rows.len(), 3);
754 }
755
756 #[test]
759 fn exists_peeks_without_consuming() {
760 let mut con = get_test_connection();
761 con.query("CREATE TABLE t (v INTEGER)").unwrap();
762 con.query("INSERT INTO t VALUES (1), (2)").unwrap();
763
764 let mut stmt = con.prepare("SELECT v FROM t ORDER BY v").unwrap();
765 let mut result = stmt.execute().unwrap();
766
767 assert!(result.exists().unwrap());
768 assert!(result.exists().unwrap()); let rows: Vec<_> = result.collect::<Result<_, _>>().unwrap();
771 assert_eq!(rows.len(), 2, "the peeked row must still be yielded by next()");
772 }
773
774 #[test]
777 fn rewind_without_enable_is_a_no_op() {
778 let mut con = get_test_connection();
779 con.query("CREATE TABLE t (v INTEGER)").unwrap();
780 con.query("INSERT INTO t VALUES (1), (2), (3)").unwrap();
781
782 let mut stmt = con.prepare("SELECT v FROM t ORDER BY v").unwrap();
783 let mut result = stmt.execute().unwrap();
784
785 let first = result.next().unwrap().unwrap();
786 result.rewind();
787 let second = result.next().unwrap().unwrap();
789 assert_ne!(first.get("v"), second.get("v"));
790 }
791
792 #[test]
795 fn enable_rewind_then_rewind_replays_from_start() {
796 let mut con = get_test_connection();
797 con.query("CREATE TABLE t (v INTEGER)").unwrap();
798 con.query("INSERT INTO t VALUES (1), (2), (3)").unwrap();
799
800 let mut stmt = con.prepare("SELECT v FROM t ORDER BY v").unwrap();
801 let mut result = stmt.execute().unwrap();
802 result.enable_rewind();
803
804 let first_pass: Vec<_> = (&mut result).take(3).map(|r| r.unwrap()).collect();
805 assert_eq!(first_pass.len(), 3);
806
807 result.rewind();
808 let second_pass: Vec<_> = result.collect::<Result<_, _>>().unwrap();
809 assert_eq!(second_pass.len(), 3);
810 for (a, b) in first_pass.iter().zip(second_pass.iter()) {
811 assert_eq!(a.get("v"), b.get("v"));
812 }
813 }
814
815 #[test]
819 fn enable_rewind_after_exists_peek_still_caches_the_peeked_row() {
820 let mut con = get_test_connection();
821 con.query("CREATE TABLE t (v INTEGER)").unwrap();
822 con.query("INSERT INTO t VALUES (1), (2)").unwrap();
823
824 let mut stmt = con.prepare("SELECT v FROM t ORDER BY v").unwrap();
825 let mut result = stmt.execute().unwrap();
826
827 assert!(result.exists().unwrap()); result.enable_rewind();
829 let first = result.next().unwrap().unwrap(); result.rewind();
832 let replayed = result.next().unwrap().unwrap();
833 assert_eq!(first.get("v"), replayed.get("v"));
834 }
835
836 #[test]
839 fn count_matches_iteration_length_for_plain_forward_iteration() {
840 let mut con = get_test_connection();
841 con.query("CREATE TABLE t (v INTEGER)").unwrap();
842 con.query("INSERT INTO t VALUES (1), (2), (3)").unwrap();
843
844 let mut stmt = con.prepare("SELECT v FROM t").unwrap();
845 let result = stmt.execute().unwrap();
846 assert_eq!(result.count(), 3);
847 }
848
849 #[test]
853 fn count_matches_iteration_length_across_multiple_chunks() {
854 let mut con = get_test_connection();
855 con.query("CREATE TABLE t AS SELECT * FROM range(5000) t(v)").unwrap();
856
857 let mut stmt = con.prepare("SELECT v FROM t").unwrap();
858 let result = stmt.execute().unwrap();
859 assert_eq!(result.count(), 5000);
860 }
861
862 #[test]
865 fn count_after_exists_peek_includes_the_peeked_row() {
866 let mut con = get_test_connection();
867 con.query("CREATE TABLE t (v INTEGER)").unwrap();
868 con.query("INSERT INTO t VALUES (1), (2), (3)").unwrap();
869
870 let mut stmt = con.prepare("SELECT v FROM t").unwrap();
871 let mut result = stmt.execute().unwrap();
872 assert!(result.exists().unwrap());
873 assert_eq!(result.count(), 3);
874 }
875
876 #[test]
881 fn count_with_rewind_enabled_after_partial_consumption() {
882 let mut con = get_test_connection();
883 con.query("CREATE TABLE t (v INTEGER)").unwrap();
884 con.query("INSERT INTO t VALUES (1), (2), (3), (4), (5)").unwrap();
885
886 let mut stmt = con.prepare("SELECT v FROM t").unwrap();
887 let mut result = stmt.execute().unwrap();
888 result.enable_rewind();
889
890 let _ = result.next().unwrap().unwrap();
893 let _ = result.next().unwrap().unwrap();
894 result.rewind();
895
896 assert_eq!(result.count(), 5);
898 }
899}