1use std::{convert, ffi::c_void, fmt, mem, os::raw::c_char, ptr, str};
2
3use arrow::{array::StructArray, datatypes::SchemaRef};
4
5use super::{AndThenRows, Connection, Error, MappedRows, Params, RawStatement, Result, Row, Rows, ValueRef, ffi};
6use crate::core::LogicalTypeId;
7#[cfg(feature = "polars")]
8use crate::polars_dataframe::Polars;
9use crate::{
10 arrow_batch::{Arrow, ArrowStream},
11 error::result_from_duckdb_prepare,
12 types::{
13 ToSql, ToSqlOutput, binding_unsupported_value, to_duckdb_hugeint, to_duckdb_uhugeint, value_ref_from_value,
14 },
15};
16#[cfg(feature = "polars")]
17use polars_core::utils::arrow as polars_arrow;
18
19pub struct Statement<'conn> {
29 conn: &'conn Connection,
30 pub(crate) stmt: RawStatement,
31}
32
33impl Statement<'_> {
34 #[inline]
76 pub fn execute<P: Params>(&mut self, params: P) -> Result<usize> {
77 params.__bind_in(self)?;
78 self.execute_with_bound_parameters()
79 }
80
81 #[inline]
95 pub fn insert<P: Params>(&mut self, params: P) -> Result<()> {
96 let changes = self.execute(params)?;
97 match changes {
98 1 => Ok(()),
99 _ => Err(Error::StatementChangedRows(changes)),
100 }
101 }
102
103 #[inline]
125 pub fn query_arrow<P: Params>(&mut self, params: P) -> Result<Arrow<'_>> {
126 self.execute(params)?;
127 Ok(Arrow::new(self))
128 }
129
130 #[inline]
155 pub fn stream_arrow<P: Params>(&mut self, params: P) -> Result<ArrowStream<'_>> {
156 params.__bind_in(self)?;
157 self.stmt.execute_streaming()?;
158 Ok(ArrowStream::new(self))
159 }
160
161 #[cfg(feature = "polars")]
199 #[inline]
200 pub fn query_polars<P: Params>(&mut self, params: P) -> Result<Polars<'_>> {
201 self.execute(params)?;
202 Ok(Polars::new(self))
203 }
204
205 #[inline]
265 pub fn query<P: Params>(&mut self, params: P) -> Result<Rows<'_>> {
266 self.execute(params)?;
267 Ok(Rows::new(self))
268 }
269
270 pub fn query_map<T, P, F>(&mut self, params: P, f: F) -> Result<MappedRows<'_, F>>
301 where
302 P: Params,
303 F: FnMut(&Row<'_>) -> Result<T>,
304 {
305 self.query(params).map(|rows| rows.mapped(f))
306 }
307
308 #[inline]
337 pub fn query_and_then<T, E, P, F>(&mut self, params: P, f: F) -> Result<AndThenRows<'_, F>>
338 where
339 P: Params,
340 E: convert::From<Error>,
341 F: FnMut(&Row<'_>) -> Result<T, E>,
342 {
343 self.query(params).map(|rows| rows.and_then(f))
344 }
345
346 #[inline]
349 pub fn exists<P: Params>(&mut self, params: P) -> Result<bool> {
350 let mut rows = self.query(params)?;
351 let exists = rows.next()?.is_some();
352 Ok(exists)
353 }
354
355 pub fn query_row<T, P, F>(&mut self, params: P, f: F) -> Result<T>
371 where
372 P: Params,
373 F: FnOnce(&Row<'_>) -> Result<T>,
374 {
375 self.query(params)?.get_expected_row().and_then(f)
376 }
377
378 pub fn query_one<T, P, F>(&mut self, params: P, f: F) -> Result<T>
393 where
394 P: Params,
395 F: FnOnce(&Row<'_>) -> Result<T>,
396 {
397 let mut rows = self.query(params)?;
398 let row = rows.get_expected_row().and_then(f)?;
399 if rows.next()?.is_some() {
400 return Err(Error::QueryReturnedMoreThanOneRow);
401 }
402 Ok(row)
403 }
404
405 #[inline]
407 pub fn row_count(&self) -> usize {
408 self.stmt.row_count()
409 }
410
411 #[inline]
416 pub fn step(&self) -> Result<Option<StructArray>> {
417 self.stmt.step()
418 }
419
420 #[cfg(feature = "polars")]
421 #[inline]
422 pub(crate) fn step_polars(&self) -> Result<Option<polars_arrow::array::StructArray>> {
423 self.stmt.step_polars()
424 }
425
426 #[inline]
427 pub(crate) fn bind_parameters<P>(&mut self, params: P) -> Result<()>
428 where
429 P: IntoIterator,
430 P::Item: ToSql,
431 {
432 let result = self.try_bind_parameters(params);
433 if result.is_err() {
434 let _ = self.stmt.clear_bindings();
435 }
436 result
437 }
438
439 fn try_bind_parameters<P>(&mut self, params: P) -> Result<()>
440 where
441 P: IntoIterator,
442 P::Item: ToSql,
443 {
444 let expected = self.stmt.bind_parameter_count();
445 let mut index = 0;
446 for p in params.into_iter() {
447 index += 1; if index > expected {
449 break;
450 }
451 self.bind_parameter(&p, index)?;
452 }
453 if index != expected {
454 Err(Error::InvalidParameterCount(index, expected))
455 } else {
456 Ok(())
457 }
458 }
459
460 #[inline]
462 pub fn parameter_count(&self) -> usize {
463 self.stmt.bind_parameter_count()
464 }
465
466 #[inline]
495 pub fn parameter_name(&self, idx: usize) -> Result<String> {
496 self.stmt.parameter_name(idx)
497 }
498
499 #[inline]
537 pub fn raw_bind_parameter<T: ToSql>(&mut self, one_based_col_index: usize, param: T) -> Result<()> {
538 self.bind_parameter(¶m, one_based_col_index)
541 }
542
543 #[inline]
558 pub fn raw_execute(&mut self) -> Result<usize> {
559 self.execute_with_bound_parameters()
560 }
561
562 #[inline]
575 pub fn raw_query(&self) -> Rows<'_> {
576 Rows::new(self)
577 }
578
579 #[inline]
584 pub fn schema(&self) -> SchemaRef {
585 self.stmt.schema()
586 }
587
588 #[inline]
589 pub(crate) fn result_column_logical_id(&self, idx: usize) -> Option<LogicalTypeId> {
590 self.stmt.result_column_logical_id(idx)
591 }
592
593 fn bind_parameter<P: ?Sized + ToSql>(&self, param: &P, col: usize) -> Result<()> {
595 let value = param.to_sql()?;
596
597 let ptr = unsafe { self.stmt.ptr() };
598 let value = match value {
599 ToSqlOutput::Borrowed(v) => v,
600 ToSqlOutput::Owned(ref v) => value_ref_from_value(v, binding_unsupported_value)?,
601 };
602 let rc = match value {
604 ValueRef::Null => unsafe { ffi::duckdb_bind_null(ptr, col as u64) },
605 ValueRef::Boolean(i) => unsafe { ffi::duckdb_bind_boolean(ptr, col as u64, i) },
606 ValueRef::TinyInt(i) => unsafe { ffi::duckdb_bind_int8(ptr, col as u64, i) },
607 ValueRef::SmallInt(i) => unsafe { ffi::duckdb_bind_int16(ptr, col as u64, i) },
608 ValueRef::Int(i) => unsafe { ffi::duckdb_bind_int32(ptr, col as u64, i) },
609 ValueRef::BigInt(i) => unsafe { ffi::duckdb_bind_int64(ptr, col as u64, i) },
610 ValueRef::HugeInt(i) => unsafe { ffi::duckdb_bind_hugeint(ptr, col as u64, to_duckdb_hugeint(i)) },
611 ValueRef::UHugeInt(i) => unsafe { ffi::duckdb_bind_uhugeint(ptr, col as u64, to_duckdb_uhugeint(i)) },
612 ValueRef::UTinyInt(i) => unsafe { ffi::duckdb_bind_uint8(ptr, col as u64, i) },
613 ValueRef::USmallInt(i) => unsafe { ffi::duckdb_bind_uint16(ptr, col as u64, i) },
614 ValueRef::UInt(i) => unsafe { ffi::duckdb_bind_uint32(ptr, col as u64, i) },
615 ValueRef::UBigInt(i) => unsafe { ffi::duckdb_bind_uint64(ptr, col as u64, i) },
616 ValueRef::Float(r) => unsafe { ffi::duckdb_bind_float(ptr, col as u64, r) },
617 ValueRef::Double(r) => unsafe { ffi::duckdb_bind_double(ptr, col as u64, r) },
618 ValueRef::Text(s) => unsafe {
619 ffi::duckdb_bind_varchar_length(ptr, col as u64, s.as_ptr() as *const c_char, s.len() as u64)
620 },
621 ValueRef::Blob(b) | ValueRef::Geometry(b) => unsafe {
622 ffi::duckdb_bind_blob(ptr, col as u64, b.as_ptr() as *const c_void, b.len() as u64)
623 },
624 ValueRef::Timestamp(u, i) => unsafe {
625 ffi::duckdb_bind_timestamp(ptr, col as u64, ffi::duckdb_timestamp { micros: u.to_micros(i) })
626 },
627 ValueRef::Interval { months, days, nanos } => unsafe {
628 let micros = nanos / 1_000;
629 ffi::duckdb_bind_interval(ptr, col as u64, ffi::duckdb_interval { months, days, micros })
630 },
631 ValueRef::Date32(days) => unsafe { ffi::duckdb_bind_date(ptr, col as u64, ffi::duckdb_date { days }) },
632 ValueRef::Time64(u, i) => unsafe {
633 ffi::duckdb_bind_time(ptr, col as u64, ffi::duckdb_time { micros: u.to_micros(i) })
634 },
635 ValueRef::Decimal(d) => unsafe {
636 let decimal = crate::types::to_duckdb_decimal(d);
637 ffi::duckdb_bind_decimal(ptr, col as u64, decimal)
638 },
639 _ => {
640 return Err(Error::ToSqlConversionFailure(
641 binding_unsupported_value(value.data_type()).into(),
642 ));
643 }
644 };
645 result_from_duckdb_prepare(rc, ptr)
646 }
647
648 #[inline]
649 fn execute_with_bound_parameters(&mut self) -> Result<usize> {
650 self.stmt.execute()
651 }
652
653 #[inline]
657 pub(crate) unsafe fn into_raw(mut self) -> RawStatement {
658 let mut stmt = unsafe { RawStatement::new(ptr::null_mut()) };
659 mem::swap(&mut stmt, &mut self.stmt);
660 stmt
661 }
662}
663
664impl fmt::Debug for Statement<'_> {
665 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
666 let sql = if self.stmt.is_null() {
667 Ok("")
668 } else {
669 str::from_utf8(self.stmt.sql().unwrap().to_bytes())
670 };
671 f.debug_struct("Statement")
672 .field("conn", self.conn)
673 .field("stmt", &self.stmt)
674 .field("sql", &sql)
675 .finish()
676 }
677}
678
679impl Statement<'_> {
680 #[inline]
681 pub(super) fn new(conn: &Connection, stmt: RawStatement) -> Statement<'_> {
682 Statement { conn, stmt }
683 }
684}
685
686#[cfg(test)]
687mod test {
688 use arrow::{
689 array::ListArray,
690 datatypes::{DataType, Int32Type, Schema, SchemaRef},
691 record_batch::RecordBatch,
692 };
693
694 use crate::{
695 Connection, Error, Result, Statement,
696 core::LogicalTypeId,
697 params_from_iter,
698 types::{Decimal, ListType, ToSql, ToSqlOutput, Type, ValueRef},
699 };
700
701 struct BorrowedList(ListArray);
702 impl BorrowedList {
703 fn new() -> Self {
704 Self(ListArray::from_iter_primitive::<Int32Type, _, _>(vec![Some(vec![
705 Some(1),
706 Some(2),
707 ])]))
708 }
709 }
710 impl ToSql for BorrowedList {
711 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
712 Ok(ToSqlOutput::Borrowed(ValueRef::List(ListType::Regular(&self.0), 0)))
713 }
714 }
715
716 fn assert_binding_list_error(err: Error) {
717 match err {
718 Error::ToSqlConversionFailure(e) => {
719 assert!(
720 e.to_string().contains("binding List parameters is not yet supported"),
721 "unexpected message: {e}"
722 );
723 }
724 other => panic!("expected ToSqlConversionFailure, got {other:?}"),
725 }
726 }
727
728 fn assert_variant_decode_error(err: Error, expected_idx: usize) {
729 match err {
730 Error::FromSqlConversionFailure(idx, Type::Variant, e) => {
731 assert_eq!(idx, expected_idx);
732 assert!(
733 e.to_string().contains("decoding Variant columns is not supported"),
734 "unexpected message: {e}"
735 );
736 }
737 other => panic!("expected FromSqlConversionFailure for Variant, got {other:?}"),
738 }
739 }
740
741 #[test]
742 fn test_execute() -> Result<()> {
743 let db = Connection::open_in_memory()?;
744 db.execute_batch("CREATE TABLE foo(x INTEGER)")?;
745
746 assert_eq!(db.execute("INSERT INTO foo(x) VALUES (?)", [&2i32])?, 1);
747 assert_eq!(db.execute("INSERT INTO foo(x) VALUES (?)", [&3i32])?, 1);
748
749 assert_eq!(
751 5i32,
752 db.query_row::<i32, _, _>("SELECT SUM(x) FROM foo WHERE x > ?", [&0i32], |r| r.get(0))?
753 );
754 assert_eq!(
755 3i32,
756 db.query_row::<i32, _, _>("SELECT SUM(x) FROM foo WHERE x > ?", [&2i32], |r| r.get(0))?
757 );
758 Ok(())
759 }
760
761 #[test]
762 fn test_stmt_execute() -> Result<()> {
763 let db = Connection::open_in_memory()?;
764 let sql = r#"
765 CREATE SEQUENCE seq;
766 CREATE TABLE test (id INTEGER DEFAULT NEXTVAL('seq'), name TEXT NOT NULL, flag INTEGER);
767 "#;
768 db.execute_batch(sql)?;
769
770 let mut stmt = db.prepare("INSERT INTO test (name) VALUES (?)")?;
771 stmt.execute([&"one"])?;
772
773 let mut stmt = db.prepare("SELECT COUNT(*) FROM test WHERE name = ?")?;
774 assert_eq!(1i32, stmt.query_row::<i32, _, _>([&"one"], |r| r.get(0))?);
775 Ok(())
776 }
777
778 #[test]
779 fn test_query() -> Result<()> {
780 let db = Connection::open_in_memory()?;
781 let sql = r#"
782 CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
783 INSERT INTO test(id, name) VALUES (1, 'one');
784 "#;
785 db.execute_batch(sql)?;
786
787 let mut stmt = db.prepare("SELECT id FROM test where name = ?")?;
788 {
789 let id: i32 = stmt.query_one([&"one"], |r| r.get(0))?;
790 assert_eq!(id, 1);
791 }
792 Ok(())
793 }
794
795 #[test]
796 fn test_bind_u128_as_uhugeint() -> Result<()> {
797 const U128_MAX_SQL: &str = "340282366920938463463374607431768211455";
798
799 let db = Connection::open_in_memory()?;
800 let max = u128::MAX;
801
802 let typ: String = db.query_row("SELECT typeof(?)", [&max], |row| row.get(0))?;
803 assert_eq!(typ, "UHUGEINT");
804
805 let equals_max: bool = db.query_row(&format!("SELECT ? = ({U128_MAX_SQL})::UHUGEINT"), [&max], |row| {
806 row.get(0)
807 })?;
808 assert!(equals_max);
809
810 Ok(())
811 }
812
813 #[test]
814 fn test_query_and_then() -> Result<()> {
815 let db = Connection::open_in_memory()?;
816 let sql = r#"
817 CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
818 INSERT INTO test(id, name) VALUES (1, 'one');
819 INSERT INTO test(id, name) VALUES (2, 'one');
820 "#;
821 db.execute_batch(sql)?;
822
823 let mut stmt = db.prepare("SELECT id FROM test where name = ? ORDER BY id ASC")?;
824 let mut rows = stmt.query_and_then([&"one"], |row| {
825 let id: i32 = row.get(0)?;
826 if id == 1 {
827 Ok(id)
828 } else {
829 Err(Error::ExecuteReturnedResults)
830 }
831 })?;
832
833 let doubled_id: i32 = rows.next().unwrap()?;
835 assert_eq!(1, doubled_id);
836
837 #[allow(clippy::match_wild_err_arm)]
839 match rows.next().unwrap() {
840 Ok(_) => panic!("invalid Ok"),
841 Err(Error::ExecuteReturnedResults) => (),
842 Err(_) => panic!("invalid Err"),
843 }
844 Ok(())
845 }
846
847 #[test]
848 fn test_unbound_parameters_are_error() -> Result<()> {
849 let db = Connection::open_in_memory()?;
850 let sql = "CREATE TABLE test (x TEXT, y TEXT)";
851 db.execute_batch(sql)?;
852
853 let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (?, ?)")?;
854 assert!(stmt.execute([&"one"]).is_err());
855 Ok(())
856 }
857
858 #[test]
859 fn test_insert_empty_text_is_none() -> Result<()> {
860 let db = Connection::open_in_memory()?;
861 let sql = "CREATE TABLE test (x TEXT, y TEXT)";
862 db.execute_batch(sql)?;
863
864 let mut stmt = db.prepare("INSERT INTO test (x) VALUES (?)")?;
865 stmt.execute([&"one"])?;
866
867 let result: Option<String> = db.query_row("SELECT y FROM test WHERE x = 'one'", [], |row| row.get(0))?;
868 assert!(result.is_none());
869 Ok(())
870 }
871
872 #[test]
873 fn test_raw_binding() -> Result<()> {
874 let db = Connection::open_in_memory()?;
875 db.execute_batch("CREATE TABLE test (name TEXT, value INTEGER)")?;
876 {
877 let mut stmt = db.prepare("INSERT INTO test (name, value) VALUES (?, ?)")?;
878
879 stmt.raw_bind_parameter(2, 50i32)?;
880 stmt.raw_bind_parameter(1, "example")?;
881 let n = stmt.raw_execute()?;
882 assert_eq!(n, 1);
883 }
884
885 {
886 let mut stmt = db.prepare("SELECT name, value FROM test WHERE value = ?")?;
887 stmt.raw_bind_parameter(1, 50)?;
888 stmt.raw_execute()?;
889 let mut rows = stmt.raw_query();
890 {
891 let row = rows.next()?.unwrap();
892 let name: String = row.get(0)?;
893 assert_eq!(name, "example");
894 let value: i32 = row.get(1)?;
895 assert_eq!(value, 50);
896 }
897 assert!(rows.next()?.is_none());
898 }
899
900 {
901 let db = Connection::open_in_memory()?;
902 db.execute_batch("CREATE TABLE test (name TEXT, value UINTEGER)")?;
903 let mut stmt = db.prepare("INSERT INTO test(name, value) VALUES (?, ?)")?;
904 stmt.raw_bind_parameter(1, "negative")?;
905 stmt.raw_bind_parameter(2, u32::MAX)?;
906 let n = stmt.raw_execute()?;
907 assert_eq!(n, 1);
908 assert_eq!(
909 u32::MAX,
910 db.query_row::<u32, _, _>("SELECT value FROM test", [], |r| r.get(0))?
911 );
912 }
913
914 {
915 let db = Connection::open_in_memory()?;
916 db.execute_batch("CREATE TABLE test (name TEXT, value UBIGINT)")?;
917 let mut stmt = db.prepare("INSERT INTO test(name, value) VALUES (?, ?)")?;
918 stmt.raw_bind_parameter(1, "negative")?;
919 stmt.raw_bind_parameter(2, u64::MAX)?;
920 let n = stmt.raw_execute()?;
921 assert_eq!(n, 1);
922 assert_eq!(
923 u64::MAX,
924 db.query_row::<u64, _, _>("SELECT value FROM test", [], |r| r.get(0))?
925 );
926 }
927
928 Ok(())
929 }
930
931 #[test]
932 #[cfg_attr(windows, ignore = "Windows doesn't allow concurrent writes to a file")]
933 fn test_insert_duplicate() -> Result<()> {
934 let db = Connection::open_in_memory()?;
935 db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)")?;
936 let mut stmt = db.prepare("INSERT INTO foo (x) VALUES (?)")?;
937 stmt.insert([1i32])?;
939 stmt.insert([2i32])?;
940 assert!(stmt.insert([1i32]).is_err());
941 let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4")?;
942 match multi.insert([]).unwrap_err() {
943 Error::StatementChangedRows(2) => (),
944 err => panic!("Unexpected error {err}"),
945 }
946 Ok(())
947 }
948
949 #[test]
950 fn test_insert_different_tables() -> Result<()> {
951 let db = Connection::open_in_memory()?;
953 db.execute_batch(
954 r"
955 CREATE TABLE foo(x INTEGER);
956 CREATE TABLE bar(x INTEGER);
957 ",
958 )?;
959
960 db.prepare("INSERT INTO foo VALUES (10)")?.insert([])?;
961 db.prepare("INSERT INTO bar VALUES (10)")?.insert([])?;
962 Ok(())
963 }
964
965 #[test]
969 fn test_insert_with_returning_clause() -> Result<()> {
970 let db = Connection::open_in_memory()?;
971 db.execute_batch(
972 "CREATE SEQUENCE location_id_seq START WITH 1 INCREMENT BY 1;
973 CREATE TABLE location (
974 id INTEGER PRIMARY KEY DEFAULT nextval('location_id_seq'),
975 name TEXT NOT NULL
976 )",
977 )?;
978
979 let changes = db.execute("INSERT INTO location (name) VALUES (?)", ["test1"])?;
981 assert_eq!(changes, 1);
982
983 let changes = db.execute("INSERT INTO location (name) VALUES (?) RETURNING id", ["test2"])?;
985 assert_eq!(changes, 0);
986
987 let count: i64 = db.query_row("SELECT COUNT(*) FROM location", [], |r| r.get(0))?;
989 assert_eq!(count, 2);
990
991 let mut stmt = db.prepare("INSERT INTO location (name) VALUES (?)")?;
993 stmt.insert(["test3"])?;
994
995 let mut stmt = db.prepare("INSERT INTO location (name) VALUES (?) RETURNING id")?;
997 let result = stmt.insert(["test4"]);
998 assert!(matches!(result, Err(Error::StatementChangedRows(0))));
999
1000 let count: i64 = db.query_row("SELECT COUNT(*) FROM location", [], |r| r.get(0))?;
1002 assert_eq!(count, 4);
1003
1004 let id: i64 = db.query_row("INSERT INTO location (name) VALUES (?) RETURNING id", ["test5"], |r| {
1006 r.get(0)
1007 })?;
1008 assert_eq!(id, 5);
1009
1010 let mut stmt = db.prepare("INSERT INTO location (name) VALUES (?) RETURNING id")?;
1012 let ids: Vec<i64> = stmt
1013 .query_map(["test6"], |row| row.get(0))?
1014 .collect::<Result<Vec<_>>>()?;
1015 assert_eq!(ids.len(), 1);
1016 assert_eq!(ids[0], 6);
1017
1018 let id: i64 = db
1020 .prepare("INSERT INTO location (name) VALUES (?) RETURNING id")?
1021 .query_one(["test7"], |r| r.get(0))?;
1022 assert_eq!(id, 7);
1023
1024 let (id, name): (i64, String) = db.query_row(
1026 "INSERT INTO location (name) VALUES (?) RETURNING id, name",
1027 ["test8"],
1028 |r| Ok((r.get(0)?, r.get(1)?)),
1029 )?;
1030 assert_eq!(id, 8);
1031 assert_eq!(name, "test8");
1032
1033 Ok(())
1034 }
1035
1036 #[test]
1037 fn test_exists() -> Result<()> {
1038 let db = Connection::open_in_memory()?;
1039 let sql = "BEGIN;
1040 CREATE TABLE foo(x INTEGER);
1041 INSERT INTO foo VALUES(1);
1042 INSERT INTO foo VALUES(2);
1043 END;";
1044 db.execute_batch(sql)?;
1045 let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?")?;
1046 assert!(stmt.exists([1i32])?);
1047 assert!(stmt.exists([2i32])?);
1048 assert!(!stmt.exists([0i32])?);
1049 Ok(())
1050 }
1051
1052 #[test]
1053 fn test_query_row() -> Result<()> {
1054 let db = Connection::open_in_memory()?;
1055 let sql = "BEGIN;
1056 CREATE TABLE foo(x INTEGER, y INTEGER);
1057 INSERT INTO foo VALUES(1, 3);
1058 INSERT INTO foo VALUES(2, 4);
1059 END;";
1060 db.execute_batch(sql)?;
1061 let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?")?;
1062 let y: Result<i32> = stmt.query_row([1i32], |r| r.get(0));
1063 assert_eq!(3i32, y?);
1064 Ok(())
1065 }
1066
1067 #[test]
1068 fn test_query_one() -> Result<()> {
1069 let db = Connection::open_in_memory()?;
1070 let sql = "BEGIN;
1071 CREATE TABLE foo(x INTEGER, y INTEGER);
1072 INSERT INTO foo VALUES(1, 3);
1073 INSERT INTO foo VALUES(2, 4);
1074 END;";
1075 db.execute_batch(sql)?;
1076
1077 let y: i32 = db
1079 .prepare("SELECT y FROM foo WHERE x = ?")?
1080 .query_one([1], |r| r.get(0))?;
1081 assert_eq!(y, 3);
1082
1083 let res: Result<i32> = db
1085 .prepare("SELECT y FROM foo WHERE x = ?")?
1086 .query_one([99], |r| r.get(0));
1087 assert_eq!(res.unwrap_err(), Error::QueryReturnedNoRows);
1088
1089 let res: Result<i32> = db.prepare("SELECT y FROM foo")?.query_one([], |r| r.get(0));
1091 assert_eq!(res.unwrap_err(), Error::QueryReturnedMoreThanOneRow);
1092
1093 Ok(())
1094 }
1095
1096 #[test]
1097 fn test_query_one_optional() -> Result<()> {
1098 use crate::OptionalExt;
1099
1100 let db = Connection::open_in_memory()?;
1101 let sql = "BEGIN;
1102 CREATE TABLE foo(x INTEGER, y INTEGER);
1103 INSERT INTO foo VALUES(1, 3);
1104 INSERT INTO foo VALUES(2, 4);
1105 END;";
1106 db.execute_batch(sql)?;
1107
1108 let y: Option<i32> = db
1110 .prepare("SELECT y FROM foo WHERE x = ?")?
1111 .query_one([1], |r| r.get(0))
1112 .optional()?;
1113 assert_eq!(y, Some(3));
1114
1115 let y: Option<i32> = db
1117 .prepare("SELECT y FROM foo WHERE x = ?")?
1118 .query_one([99], |r| r.get(0))
1119 .optional()?;
1120 assert_eq!(y, None);
1121
1122 let res = db
1124 .prepare("SELECT y FROM foo")?
1125 .query_one([], |r| r.get::<_, i32>(0))
1126 .optional();
1127 assert_eq!(res.unwrap_err(), Error::QueryReturnedMoreThanOneRow);
1128
1129 Ok(())
1130 }
1131
1132 #[test]
1133 fn test_query_by_column_name() -> Result<()> {
1134 let db = Connection::open_in_memory()?;
1135 let sql = "BEGIN;
1136 CREATE TABLE foo(x INTEGER, y INTEGER);
1137 INSERT INTO foo VALUES(1, 3);
1138 END;";
1139 db.execute_batch(sql)?;
1140 let mut stmt = db.prepare("SELECT y FROM foo")?;
1141 let y: Result<i64> = stmt.query_row([], |r| r.get("y"));
1142 assert_eq!(3i64, y?);
1143 Ok(())
1144 }
1145
1146 #[test]
1147 fn test_get_schema_of_executed_result() -> Result<()> {
1148 use arrow::datatypes::{DataType, Field, Schema};
1149 let db = Connection::open_in_memory()?;
1150 let sql = "BEGIN;
1151 CREATE TABLE foo(x STRING, y INTEGER);
1152 INSERT INTO foo VALUES('hello', 3);
1153 END;";
1154 db.execute_batch(sql)?;
1155 let mut stmt = db.prepare("SELECT x, y FROM foo")?;
1156 let _ = stmt.execute([]);
1157 let schema = stmt.schema();
1158 assert_eq!(
1159 *schema,
1160 Schema::new(vec![
1161 Field::new("x", DataType::Utf8, true),
1162 Field::new("y", DataType::Int32, true)
1163 ])
1164 );
1165 Ok(())
1166 }
1167
1168 #[test]
1169 #[should_panic(expected = "The statement was not executed yet")]
1170 fn test_unexecuted_schema_panics() {
1171 let db = Connection::open_in_memory().unwrap();
1172 let sql = "BEGIN;
1173 CREATE TABLE foo(x STRING, y INTEGER);
1174 INSERT INTO foo VALUES('hello', 3);
1175 END;";
1176 db.execute_batch(sql).unwrap();
1177 let stmt = db.prepare("SELECT x, y FROM foo").unwrap();
1178 let _ = stmt.schema();
1179 }
1180
1181 #[test]
1182 fn test_query_by_column_name_ignore_case() -> Result<()> {
1183 let db = Connection::open_in_memory()?;
1184 let sql = "BEGIN;
1185 CREATE TABLE foo(x INTEGER, y INTEGER);
1186 INSERT INTO foo VALUES(1, 3);
1187 END;";
1188 db.execute_batch(sql)?;
1189 let mut stmt = db.prepare("SELECT y as Y FROM foo")?;
1190 let y: Result<i64> = stmt.query_row([], |r| r.get("y"));
1191 assert_eq!(3i64, y?);
1192 Ok(())
1193 }
1194
1195 #[test]
1196 fn test_bind_parameters() -> Result<()> {
1197 let db = Connection::open_in_memory()?;
1198 db.query_row("SELECT ?1, ?2, ?3", [&1u8 as &dyn ToSql, &"one", &Some("one")], |row| {
1200 row.get::<_, u8>(0)
1201 })?;
1202 let data = vec![1, 2, 3];
1204 db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| row.get::<_, u8>(0))?;
1205 db.query_row("SELECT ?1, ?2, ?3", params_from_iter(data.as_slice()), |row| {
1206 row.get::<_, u8>(0)
1207 })?;
1208 db.query_row("SELECT ?1, ?2, ?3", params_from_iter(data), |row| row.get::<_, u8>(0))?;
1209
1210 let data: std::collections::BTreeSet<String> =
1211 ["one", "two", "three"].iter().map(|s| (*s).to_string()).collect();
1212 db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| {
1213 row.get::<_, String>(0)
1214 })?;
1215
1216 let data = [0; 3];
1217 db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| row.get::<_, u8>(0))?;
1218 db.query_row("SELECT ?1, ?2, ?3", params_from_iter(data.iter()), |row| {
1219 row.get::<_, u8>(0)
1220 })?;
1221 Ok(())
1222 }
1223
1224 #[test]
1225 fn test_named_parameters() -> Result<()> {
1226 use std::collections::HashMap;
1227
1228 let named_params = HashMap::from([("foo", 42), ("bar", 23)]);
1229
1230 let db = Connection::open_in_memory()?;
1231 let sql = r#"SELECT $foo > $bar"#;
1232 let result: bool = db.query_row(sql, &named_params, |row| row.get(0))?;
1233 assert!(result);
1234 Ok(())
1235 }
1236
1237 #[test]
1238 fn test_named_parameters_macro() -> Result<()> {
1239 let db = Connection::open_in_memory()?;
1240 let name = "alice";
1241 let params = crate::named_params! {
1242 "foo": 42,
1243 "name": name,
1244 };
1245 let result: bool = db.query_row("SELECT $foo > 40 AND $name = 'alice'", params, |row| row.get(0))?;
1246 assert!(result);
1247 Ok(())
1248 }
1249
1250 #[test]
1251 fn test_empty_named_parameters_macro() -> Result<()> {
1252 let db = Connection::open_in_memory()?;
1253 let result: i32 = db.query_row("SELECT 1", crate::named_params! {}, |row| row.get(0))?;
1254 assert_eq!(result, 1);
1255 Ok(())
1256 }
1257
1258 #[test]
1259 fn test_named_parameters_repeated_placeholder() -> Result<()> {
1260 use std::collections::HashMap;
1261
1262 let db = Connection::open_in_memory()?;
1263 let stmt = db.prepare("SELECT $foo + $foo")?;
1264 assert_eq!(stmt.parameter_count(), 1);
1265 assert_eq!(stmt.parameter_name(1)?, "foo");
1266
1267 let slice_result: i32 = db.query_row(
1268 "SELECT $foo + $foo",
1269 crate::named_params! {
1270 "foo": 21,
1271 },
1272 |row| row.get(0),
1273 )?;
1274 assert_eq!(slice_result, 42);
1275
1276 let named_params = HashMap::from([("foo", 21)]);
1277 let hashmap_result: i32 = db.query_row("SELECT $foo + $foo", &named_params, |row| row.get(0))?;
1278 assert_eq!(hashmap_result, 42);
1279
1280 Ok(())
1281 }
1282
1283 #[test]
1284 fn test_named_parameters_reject_extra_keys() -> Result<()> {
1285 use std::collections::HashMap;
1286
1287 let named_params = HashMap::from([("foo", 42), ("bar", 23), ("extra", 1)]);
1288
1289 let db = Connection::open_in_memory()?;
1290 let err = db
1291 .query_row("SELECT $foo > $bar", &named_params, |row| row.get::<_, bool>(0))
1292 .unwrap_err();
1293 assert_eq!(err, Error::InvalidParameterName("extra".to_string()));
1294 Ok(())
1295 }
1296
1297 #[test]
1298 fn test_named_parameters_reject_extra_hashmap_key_deterministically() -> Result<()> {
1299 use std::collections::HashMap;
1300
1301 let named_params = HashMap::from([("foo", 42), ("z_extra", 1), ("a_extra", 2)]);
1302
1303 let db = Connection::open_in_memory()?;
1304 let err = db
1305 .query_row("SELECT $foo", &named_params, |row| row.get::<_, i32>(0))
1306 .unwrap_err();
1307 assert_eq!(err, Error::InvalidParameterName("a_extra".to_string()));
1308 Ok(())
1309 }
1310
1311 #[test]
1312 fn test_named_parameters_reject_extra_slice_key() -> Result<()> {
1313 let db = Connection::open_in_memory()?;
1314 let bar = 23;
1315 let params = crate::named_params! {
1316 "foo": 42,
1317 "middle_extra": 0,
1318 "bar": bar,
1319 "first_extra": 1,
1320 "second_extra": 2,
1321 };
1322
1323 let err = db
1324 .query_row("SELECT $foo + $bar", params, |row| row.get::<_, i32>(0))
1325 .unwrap_err();
1326 assert_eq!(err, Error::InvalidParameterName("middle_extra".to_string()));
1327 Ok(())
1328 }
1329
1330 #[test]
1331 fn test_named_parameters_reject_missing_hashmap_key() -> Result<()> {
1332 use std::collections::HashMap;
1333
1334 let named_params = HashMap::from([("foo", 42)]);
1335
1336 let db = Connection::open_in_memory()?;
1337 let err = db
1338 .query_row("SELECT $foo > $bar", &named_params, |row| row.get::<_, bool>(0))
1339 .unwrap_err();
1340 assert_eq!(err, Error::InvalidParameterName("bar".to_string()));
1341 Ok(())
1342 }
1343
1344 #[test]
1345 fn test_named_parameters_reject_missing_slice_key() -> Result<()> {
1346 let db = Connection::open_in_memory()?;
1347 let params = crate::named_params! {
1348 "foo": 42,
1349 };
1350
1351 let err = db
1352 .query_row("SELECT $foo > $bar", params, |row| row.get::<_, bool>(0))
1353 .unwrap_err();
1354 assert_eq!(err, Error::InvalidParameterName("bar".to_string()));
1355 Ok(())
1356 }
1357
1358 #[test]
1359 fn test_named_parameters_reject_duplicate_slice_keys() -> Result<()> {
1360 let db = Connection::open_in_memory()?;
1361 let first = 42;
1362 let second = 23;
1363 let params = &[("foo", &first as &dyn ToSql), ("foo", &second as &dyn ToSql)] as &[(&str, &dyn ToSql)];
1364
1365 let err = db
1366 .query_row("SELECT $foo", params, |row| row.get::<_, i32>(0))
1367 .unwrap_err();
1368 assert_eq!(
1369 err,
1370 Error::InvalidParameterName("duplicate parameter name: foo".to_string())
1371 );
1372 Ok(())
1373 }
1374
1375 #[test]
1376 fn test_named_parameters_reject_positional_placeholders() -> Result<()> {
1377 use std::collections::HashMap;
1378
1379 let named_params = HashMap::from([("1", 42), ("2", 23)]);
1380
1381 let db = Connection::open_in_memory()?;
1382 let err = db
1383 .query_row("SELECT ? > ?", &named_params, |row| row.get::<_, bool>(0))
1384 .unwrap_err();
1385 assert_eq!(
1386 err,
1387 Error::InvalidParameterName("positional parameter 1 cannot be used with named parameters".to_string())
1388 );
1389 Ok(())
1390 }
1391
1392 #[test]
1393 fn test_named_parameters_reject_dollar_number_placeholders() -> Result<()> {
1394 use std::collections::HashMap;
1395
1396 let named_params = HashMap::from([("1", 42), ("2", 23)]);
1397 let db = Connection::open_in_memory()?;
1398
1399 let hashmap_err = db
1400 .query_row("SELECT $1 + $2", &named_params, |row| row.get::<_, i32>(0))
1401 .unwrap_err();
1402 assert_eq!(
1403 hashmap_err,
1404 Error::InvalidParameterName("positional parameter 1 cannot be used with named parameters".to_string())
1405 );
1406
1407 let slice_err = db
1408 .query_row(
1409 "SELECT $1 + $2",
1410 crate::named_params! {
1411 "1": 42,
1412 "2": 23,
1413 },
1414 |row| row.get::<_, i32>(0),
1415 )
1416 .unwrap_err();
1417 assert_eq!(
1418 slice_err,
1419 Error::InvalidParameterName("positional parameter 1 cannot be used with named parameters".to_string())
1420 );
1421 Ok(())
1422 }
1423
1424 #[test]
1425 fn test_named_parameters_reject_mixed_placeholders() -> Result<()> {
1426 let db = Connection::open_in_memory()?;
1427 let err = db.prepare("SELECT $foo + ?").unwrap_err();
1428 assert!(err.to_string().contains("Mixing named and positional parameters"));
1429 Ok(())
1430 }
1431
1432 #[test]
1433 fn test_named_parameters_bind_null_values() -> Result<()> {
1434 use std::collections::HashMap;
1435
1436 let db = Connection::open_in_memory()?;
1437 let named_params = HashMap::from([("x", None::<i32>)]);
1438
1439 let hashmap_result: bool = db.query_row("SELECT $x IS NULL", &named_params, |row| row.get(0))?;
1440 assert!(hashmap_result);
1441
1442 let slice_result: bool = db.query_row(
1443 "SELECT $x IS NULL",
1444 crate::named_params! {
1445 "x": Option::<i32>::None,
1446 },
1447 |row| row.get(0),
1448 )?;
1449 assert!(slice_result);
1450
1451 Ok(())
1452 }
1453
1454 #[test]
1455 fn test_named_parameters_string_keys_query_map() -> Result<()> {
1456 use std::collections::HashMap;
1457
1458 let db = Connection::open_in_memory()?;
1459 let params = HashMap::from([("min".to_string(), 2i64), ("max".to_string(), 3i64)]);
1460 let mut stmt = db.prepare("SELECT i FROM range(5) tbl(i) WHERE i BETWEEN $min AND $max ORDER BY i")?;
1461 let rows = stmt.query_map(¶ms, |row| row.get::<_, i64>(0))?;
1462 let values = rows.collect::<Result<Vec<_>>>()?;
1463
1464 assert_eq!(values, [2, 3]);
1465 Ok(())
1466 }
1467
1468 #[test]
1469 fn test_named_parameters_cow_keys_custom_hasher() -> Result<()> {
1470 use std::{
1471 borrow::Cow,
1472 collections::{HashMap, hash_map::DefaultHasher},
1473 hash::BuildHasherDefault,
1474 };
1475
1476 let db = Connection::open_in_memory()?;
1477 let mut params: HashMap<Cow<'static, str>, i64, BuildHasherDefault<DefaultHasher>> = HashMap::default();
1478 params.insert(Cow::Borrowed("min"), 2);
1479 params.insert(Cow::Owned("max".to_string()), 3);
1480
1481 let mut stmt = db.prepare("SELECT i FROM range(5) tbl(i) WHERE i BETWEEN $min AND $max ORDER BY i")?;
1482 let rows = stmt.query_map(¶ms, |row| row.get::<_, i64>(0))?;
1483 let values = rows.collect::<Result<Vec<_>>>()?;
1484
1485 assert_eq!(values, [2, 3]);
1486 Ok(())
1487 }
1488
1489 #[test]
1490 fn test_empty_stmt() -> Result<()> {
1491 let conn = Connection::open_in_memory()?;
1492 let stmt = conn.prepare("");
1493 assert!(stmt.is_err());
1494
1495 Ok(())
1496 }
1497
1498 #[test]
1499 fn test_comment_empty_stmt() -> Result<()> {
1500 let conn = Connection::open_in_memory()?;
1501 assert!(conn.prepare("/*SELECT 1;*/").is_err());
1502 Ok(())
1503 }
1504
1505 #[test]
1506 fn test_comment_and_sql_stmt() -> Result<()> {
1507 let conn = Connection::open_in_memory()?;
1508 let mut stmt = conn.prepare("/*...*/ SELECT 1;")?;
1509 stmt.execute([])?;
1510 assert_eq!(1, stmt.column_count());
1511 Ok(())
1512 }
1513
1514 #[test]
1515 fn test_nul_byte() -> Result<()> {
1516 let db = Connection::open_in_memory()?;
1517 let expected = "a\x00b";
1518 let actual: String = db.query_row("SELECT CAST(? AS VARCHAR)", [expected], |row| row.get(0))?;
1519 assert_eq!(expected, actual);
1520 Ok(())
1521 }
1522
1523 #[test]
1524 fn test_parameter_name() -> Result<()> {
1525 let db = Connection::open_in_memory()?;
1526
1527 {
1528 let stmt = db.prepare("SELECT $foo, $bar")?;
1529
1530 assert_eq!(stmt.parameter_count(), 2);
1531 assert_eq!(stmt.parameter_name(1)?, "foo");
1532 assert_eq!(stmt.parameter_name(2)?, "bar");
1533
1534 assert!(matches!(stmt.parameter_name(0), Err(Error::InvalidParameterIndex(0))));
1535 assert!(matches!(
1536 stmt.parameter_name(100),
1537 Err(Error::InvalidParameterIndex(100))
1538 ));
1539 }
1540
1541 {
1543 let stmt = db.prepare("SELECT ?, ?")?;
1544 assert_eq!(stmt.parameter_count(), 2);
1545 assert_eq!(stmt.parameter_name(1)?, "1");
1546 assert_eq!(stmt.parameter_name(2)?, "2");
1547 }
1548
1549 {
1551 let stmt = db.prepare("SELECT ?1, ?2")?;
1552 assert_eq!(stmt.parameter_count(), 2);
1553 assert_eq!(stmt.parameter_name(1)?, "1");
1554 assert_eq!(stmt.parameter_name(2)?, "2");
1555 }
1556
1557 Ok(())
1558 }
1559
1560 #[test]
1561 fn test_bind_named_parameters_manually() -> Result<()> {
1562 use std::collections::HashMap;
1563
1564 let db = Connection::open_in_memory()?;
1565 let mut stmt = db.prepare("SELECT $foo > $bar")?;
1566
1567 let mut params: HashMap<String, i32> = HashMap::new();
1568 params.insert("foo".to_string(), 42);
1569 params.insert("bar".to_string(), 23);
1570
1571 for idx in 1..=stmt.parameter_count() {
1572 let name = stmt.parameter_name(idx)?;
1573 if let Some(value) = params.get(&name) {
1574 stmt.raw_bind_parameter(idx, value)?;
1575 }
1576 }
1577
1578 stmt.raw_execute()?;
1579
1580 let mut rows = stmt.raw_query();
1581 let row = rows.next()?.unwrap();
1582 let result: bool = row.get(0)?;
1583 assert!(result);
1584
1585 Ok(())
1586 }
1587
1588 #[test]
1589 fn test_execute_streaming_error_message() -> Result<()> {
1590 let db = Connection::open_in_memory()?;
1591
1592 let mut stmt = db.prepare("SELECT CAST('not-a-number' AS INTEGER)")?;
1594 let result = stmt.stmt.execute_streaming();
1595
1596 assert!(result.is_err());
1597 let err = result.unwrap_err();
1598
1599 let error_string = format!("{}", err);
1600 assert!(
1601 error_string.contains("Conversion Error"),
1602 "Expected descriptive error, got: {}",
1603 error_string
1604 );
1605
1606 Ok(())
1607 }
1608
1609 #[test]
1610 fn test_step_after_streaming_materialized_result_fetches_data() -> Result<()> {
1611 use arrow::array::Int32Array;
1612
1613 let db = Connection::open_in_memory()?;
1614 db.execute_batch(
1615 "CREATE TABLE test_data(id INTEGER);
1616 INSERT INTO test_data VALUES (1), (2);
1617 CREATE MACRO test_func() AS TABLE SELECT * FROM test_data;",
1618 )?;
1619
1620 let mut stmt = db.prepare("CALL test_func()")?;
1621 stmt.stmt.execute_streaming()?;
1622
1623 let batch = stmt.stmt.step()?.expect("expected result batch");
1624 let values = batch.column(0).as_any().downcast_ref::<Int32Array>().unwrap();
1625 assert_eq!(1, values.value(0));
1626 Ok(())
1627 }
1628
1629 #[test]
1630 fn test_row_count_after_query_execution() -> Result<()> {
1631 let db = Connection::open_in_memory()?;
1632 let mut stmt = db.prepare("SELECT i FROM range(3000) AS t(i)")?;
1633 {
1634 let rows = stmt.query([])?;
1635 drop(rows);
1636 }
1637
1638 assert_eq!(3000, stmt.row_count());
1639 Ok(())
1640 }
1641
1642 #[test]
1643 fn test_stream_arrow_large_result() -> Result<()> {
1644 use arrow::array::Int64Array;
1645
1646 let db = Connection::open_in_memory()?;
1647 let mut stmt = db.prepare("SELECT i FROM range(3000) AS t(i) ORDER BY i")?;
1648 let mut stream = stmt.stream_arrow([])?;
1649 let batches = stream.by_ref().collect::<Vec<_>>();
1650
1651 assert!(batches.len() > 1);
1652 assert_eq!(3000, batches.iter().map(|batch| batch.num_rows()).sum::<usize>());
1653
1654 let values = batches
1655 .iter()
1656 .flat_map(|batch| {
1657 batch
1658 .column(0)
1659 .as_any()
1660 .downcast_ref::<Int64Array>()
1661 .unwrap()
1662 .iter()
1663 .map(Option::unwrap)
1664 })
1665 .collect::<Vec<_>>();
1666 assert_eq!(0, values[0]);
1667 assert_eq!(2048, values[2048]);
1668 assert_eq!(2999, values[2999]);
1669 assert!(stream.next().is_none());
1670 assert!(stream.next().is_none());
1671
1672 Ok(())
1673 }
1674
1675 #[test]
1676 fn test_stream_arrow_with_params() -> Result<()> {
1677 let db = Connection::open_in_memory()?;
1678 let mut stmt = db.prepare("SELECT i FROM range(3000) AS t(i) WHERE i < ? ORDER BY i")?;
1679 let batches = stmt.stream_arrow([2500_i64])?.collect::<Vec<_>>();
1680
1681 assert!(batches.len() > 1);
1682 assert_eq!(2500, batches.iter().map(|batch| batch.num_rows()).sum::<usize>());
1683 Ok(())
1684 }
1685
1686 const ARROW_EXTENSION_NAME_KEY: &str = "ARROW:extension:name";
1687 const UUID_EXTENSION_NAME: &str = "arrow.uuid";
1688 const UUID_BYTE_WIDTH: i32 = 16;
1689
1690 fn assert_default_uuid_arrow_schema(schema: &Schema) {
1691 let field = schema.field(0);
1692 assert_eq!(field.name(), "id");
1693 assert_eq!(field.data_type(), &DataType::Utf8);
1694 assert!(
1695 !field.metadata().contains_key(ARROW_EXTENSION_NAME_KEY),
1696 "default UUID Arrow export should not carry extension metadata"
1697 );
1698 }
1699
1700 fn assert_lossless_uuid_arrow_schema(schema: &Schema) {
1701 let field = schema.field(0);
1702 assert_eq!(field.name(), "id");
1703 assert_eq!(field.data_type(), &DataType::FixedSizeBinary(UUID_BYTE_WIDTH));
1704 assert_eq!(
1705 field.metadata().get(ARROW_EXTENSION_NAME_KEY).map(String::as_str),
1706 Some(UUID_EXTENSION_NAME)
1707 );
1708 }
1709
1710 fn check_uuid_arrow_schemas(query: impl Fn(&mut Statement<'_>) -> Result<(SchemaRef, RecordBatch)>) -> Result<()> {
1711 for lossless in [false, true] {
1712 let db = Connection::open_in_memory()?;
1713 if lossless {
1714 db.execute_batch("SET arrow_lossless_conversion = true")?;
1715 }
1716 let assert_schema: fn(&Schema) = if lossless {
1717 assert_lossless_uuid_arrow_schema
1718 } else {
1719 assert_default_uuid_arrow_schema
1720 };
1721
1722 let mut stmt = db.prepare("SELECT 'a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8'::UUID AS id")?;
1723 let (schema, batch) = query(&mut stmt)?;
1724 assert_schema(schema.as_ref());
1725 assert_schema(batch.schema().as_ref());
1726 }
1727
1728 Ok(())
1729 }
1730
1731 #[test]
1732 fn test_query_arrow_uuid_schema_with_and_without_lossless_conversion() -> Result<()> {
1733 check_uuid_arrow_schemas(|stmt| {
1734 let arrow = stmt.query_arrow([])?;
1735 let schema = arrow.get_schema();
1736 let batch = arrow.into_iter().next().expect("expected one batch");
1737 Ok((schema, batch))
1738 })
1739 }
1740
1741 #[test]
1742 fn test_stream_arrow_uuid_schema_with_and_without_lossless_conversion() -> Result<()> {
1743 check_uuid_arrow_schemas(|stmt| {
1744 let stream = stmt.stream_arrow([])?;
1745 let schema = stream.get_schema();
1746 let batch = stream.into_iter().next().expect("expected one batch");
1747 Ok((schema, batch))
1748 })
1749 }
1750
1751 #[test]
1752 fn test_streaming_step_surfaces_fetch_error_after_interrupt() -> Result<()> {
1753 let db = Connection::open_in_memory()?;
1754 let interrupt = db.interrupt_handle();
1755 let mut stmt = db.prepare("SELECT i FROM range(100000000) AS t(i)")?;
1756
1757 stmt.stmt.execute_streaming()?;
1758 assert!(stmt.stmt.step()?.is_some());
1759 interrupt.interrupt();
1760
1761 let err = stmt.stmt.step().unwrap_err();
1762 let message = err.to_string();
1763 assert!(
1764 message.contains("Interrupted"),
1765 "expected interrupted fetch error, got: {message}"
1766 );
1767 Ok(())
1768 }
1769
1770 #[test]
1771 fn test_stream_arrow_empty_result_reaches_clean_eof() -> Result<()> {
1772 let db = Connection::open_in_memory()?;
1773 let mut stmt = db.prepare("SELECT 1 AS x WHERE false")?;
1774 let mut stream = stmt.stream_arrow([])?;
1775
1776 let schema = stream.get_schema();
1777 assert_eq!(1, schema.fields().len());
1778 assert_eq!("x", schema.field(0).name());
1779 assert_eq!(&arrow::datatypes::DataType::Int32, schema.field(0).data_type());
1780 assert!(stream.next().is_none());
1781 assert!(stream.next().is_none());
1782 Ok(())
1783 }
1784
1785 #[test]
1786 fn test_stream_arrow_then_query_arrow_replaces_result() -> Result<()> {
1787 use arrow::array::Int64Array;
1788
1789 fn assert_range_batches(batches: &[arrow::record_batch::RecordBatch]) {
1790 assert!(batches.len() > 1);
1791 assert_eq!(3000, batches.iter().map(|batch| batch.num_rows()).sum::<usize>());
1792
1793 let values = batches
1794 .iter()
1795 .flat_map(|batch| {
1796 batch
1797 .column(0)
1798 .as_any()
1799 .downcast_ref::<Int64Array>()
1800 .unwrap()
1801 .iter()
1802 .map(Option::unwrap)
1803 })
1804 .collect::<Vec<_>>();
1805 assert_eq!(0, values[0]);
1806 assert_eq!(2048, values[2048]);
1807 assert_eq!(2999, values[2999]);
1808 }
1809
1810 let db = Connection::open_in_memory()?;
1811 let mut stmt = db.prepare("SELECT i FROM range(3000) AS t(i) ORDER BY i")?;
1812
1813 {
1814 let batches = stmt.stream_arrow([])?.collect::<Vec<_>>();
1815 assert_range_batches(&batches);
1816 }
1817
1818 {
1819 let batches = stmt.query_arrow([])?.collect::<Vec<_>>();
1820 assert_range_batches(&batches);
1821 }
1822
1823 {
1824 let batches = stmt.stream_arrow([])?.collect::<Vec<_>>();
1825 assert_range_batches(&batches);
1826 }
1827
1828 Ok(())
1829 }
1830
1831 #[test]
1832 fn test_bind_date32() -> Result<()> {
1833 use crate::types::Value;
1834
1835 let db = Connection::open_in_memory()?;
1836 let result: bool = db.query_row("SELECT ? = DATE '2022-05-18'", [Value::Date32(19130)], |row| row.get(0))?;
1838 assert!(result);
1839 Ok(())
1840 }
1841
1842 #[test]
1843 fn test_bind_time64() -> Result<()> {
1844 use crate::types::{TimeUnit, Value};
1845
1846 let db = Connection::open_in_memory()?;
1847 let micros = 45_045_123_456i64;
1849 let result: bool = db.query_row(
1850 "SELECT ? = TIME '12:30:45.123456'",
1851 [Value::Time64(TimeUnit::Microsecond, micros)],
1852 |row| row.get(0),
1853 )?;
1854 assert!(result);
1855 Ok(())
1856 }
1857
1858 #[test]
1859 fn test_execute_tuple() -> Result<()> {
1860 let db = Connection::open_in_memory()?;
1861 db.execute_batch("CREATE TABLE test (id INTEGER, name TEXT, score DOUBLE)")?;
1862
1863 let mut stmt = db.prepare("INSERT INTO test VALUES (?, ?, ?)")?;
1865 stmt.execute((1i32, "alice", 95.5f64))?;
1866 stmt.execute((2i32, "bob", 87.0f64))?;
1867
1868 let mut stmt = db.prepare("SELECT id, name, score FROM test ORDER BY id")?;
1869 let mut rows = stmt.query([])?;
1870
1871 let row = rows.next()?.unwrap();
1872 assert_eq!(row.get::<_, i32>(0)?, 1);
1873 assert_eq!(row.get::<_, String>(1)?, "alice");
1874 assert_eq!(row.get::<_, f64>(2)?, 95.5);
1875
1876 let row = rows.next()?.unwrap();
1877 assert_eq!(row.get::<_, i32>(0)?, 2);
1878 assert_eq!(row.get::<_, String>(1)?, "bob");
1879 assert_eq!(row.get::<_, f64>(2)?, 87.0);
1880
1881 Ok(())
1882 }
1883
1884 #[test]
1885 fn test_query_row_tuple() -> Result<()> {
1886 let db = Connection::open_in_memory()?;
1887 db.execute_batch("CREATE TABLE test (id INTEGER, name TEXT)")?;
1888 db.execute("INSERT INTO test VALUES (1, 'alice')", [])?;
1889
1890 let name: String = db.query_row("SELECT name FROM test WHERE id = ?", (1i32,), |r| r.get(0))?;
1891 assert_eq!(name, "alice");
1892
1893 Ok(())
1894 }
1895
1896 #[test]
1897 fn test_execute_tuple_single_element() -> Result<()> {
1898 let db = Connection::open_in_memory()?;
1899 db.execute_batch("CREATE TABLE test (id INTEGER)")?;
1900
1901 db.execute("INSERT INTO test VALUES (?)", (42i32,))?;
1902
1903 let val: i32 = db.query_row("SELECT id FROM test", [], |r| r.get(0))?;
1904 assert_eq!(val, 42);
1905 Ok(())
1906 }
1907
1908 #[test]
1909 fn test_execute_tuple_many_columns() -> Result<()> {
1910 let db = Connection::open_in_memory()?;
1911 db.execute_batch(
1912 "CREATE TABLE test (a INT, b TEXT, c DOUBLE, d INT, e TEXT, f DOUBLE, g INT, h TEXT, i DOUBLE, j INT, k TEXT, l DOUBLE, m INT, n TEXT, o DOUBLE, p INT)",
1913 )?;
1914
1915 db.execute(
1917 "INSERT INTO test VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
1918 (
1919 1i32, "a", 1.0f64, 2i32, "b", 2.0f64, 3i32, "c", 3.0f64, 4i32, "d", 4.0f64, 5i32, "e", 5.0f64, 6i32,
1920 ),
1921 )?;
1922
1923 let (a, p): (i32, i32) = db.query_row("SELECT a, p FROM test", [], |r| Ok((r.get(0)?, r.get(1)?)))?;
1924 assert_eq!(a, 1);
1925 assert_eq!(p, 6);
1926 Ok(())
1927 }
1928
1929 #[test]
1930 fn test_execute_tuple_with_option() -> Result<()> {
1931 let db = Connection::open_in_memory()?;
1932 db.execute_batch("CREATE TABLE test (id INTEGER, name TEXT)")?;
1933
1934 db.execute("INSERT INTO test VALUES (?, ?)", (1i32, None::<String>))?;
1935
1936 let name: Option<String> = db.query_row("SELECT name FROM test WHERE id = ?", (1i32,), |r| r.get(0))?;
1937 assert_eq!(name, None);
1938 Ok(())
1939 }
1940
1941 #[test]
1942 fn test_execute_empty_tuple() -> Result<()> {
1943 let db = Connection::open_in_memory()?;
1944 db.execute_batch("CREATE TABLE test (id INTEGER DEFAULT 1)")?;
1945
1946 db.execute("INSERT INTO test DEFAULT VALUES", ())?;
1947
1948 let val: i32 = db.query_row("SELECT id FROM test", (), |r| r.get(0))?;
1949 assert_eq!(val, 1);
1950 Ok(())
1951 }
1952
1953 #[test]
1954 fn test_with_decimal() -> Result<()> {
1955 let db = Connection::open_in_memory()?;
1956 db.execute_batch(
1957 "BEGIN; \
1958 CREATE TABLE foo(x DECIMAL(18, 4)); \
1959 CREATE TABLE bar(y DECIMAL(18, 2)); \
1960 COMMIT;",
1961 )?;
1962
1963 let value = Decimal::new(18, 4, 12345)?;
1965 db.execute("INSERT INTO foo(x) VALUES (?)", [&value])?;
1966 let row: Decimal = db.query_row("SELECT x FROM foo", [], |r| r.get::<_, Decimal>(0))?;
1967 assert_eq!(row, value);
1968
1969 let value = Decimal::new(18, 4, 12345)?;
1972 db.execute("INSERT INTO bar(y) VALUES (?)", [&value])?;
1973 let row: Decimal = db.query_row("SELECT y FROM bar", [], |r| r.get::<_, Decimal>(0))?;
1974 assert_eq!(row, Decimal::new(18, 2, 123)?);
1975
1976 Ok(())
1977 }
1978
1979 #[test]
1980 fn test_with_native_decimal() -> Result<()> {
1981 let db = Connection::open_in_memory()?;
1982 db.execute_batch("CREATE TABLE foo(x DECIMAL(38, 2));")?;
1983
1984 let value = Decimal::new(38, 2, 12_345_678_901_234_567_890_123_456_789_012)?;
1985 db.execute("INSERT INTO foo(x) VALUES (?)", [&value])?;
1986
1987 let row: Decimal = db.query_row("SELECT x FROM foo", [], |r| r.get(0))?;
1988 assert_eq!(row, value);
1989
1990 Ok(())
1991 }
1992
1993 #[test]
1994 fn test_decimal_from_sql_integer() -> Result<()> {
1995 let db = Connection::open_in_memory()?;
1996
1997 let row: Decimal = db.query_row("SELECT 42", [], |r| r.get(0))?;
1999 assert_eq!(row, Decimal::new(2, 0, 42)?);
2000
2001 let row: Decimal = db.query_row("SELECT 9999999999::BIGINT", [], |r| r.get(0))?;
2003 assert_eq!(row, Decimal::new(10, 0, 9999999999_i64 as i128)?);
2004
2005 let row: Decimal = db.query_row("SELECT 12345678901234567890::HUGEINT", [], |r| r.get(0))?;
2007 assert_eq!(row, Decimal::new(20, 0, 12345678901234567890)?);
2008
2009 Ok(())
2010 }
2011
2012 #[test]
2013 fn test_decimal_from_sql_float_targets_preserve_fraction() -> Result<()> {
2014 let db = Connection::open_in_memory()?;
2015
2016 let row: f64 = db.query_row("SELECT 123.45::DECIMAL(10,2)", [], |r| r.get(0))?;
2017 assert!((row - 123.45).abs() < f64::EPSILON);
2018
2019 let row: f32 = db.query_row("SELECT 0.4::DECIMAL(10,1)", [], |r| r.get(0))?;
2020 assert!((row - 0.4).abs() < f32::EPSILON);
2021
2022 let row: f32 = db.query_row("SELECT -0.4::DECIMAL(10,1)", [], |r| r.get(0))?;
2023 assert!((row + 0.4).abs() < f32::EPSILON);
2024
2025 let row: f64 = db.query_row("SELECT 1.5::DECIMAL(10,1)", [], |r| r.get(0))?;
2026 assert!((row - 1.5).abs() < f64::EPSILON);
2027
2028 let row: f64 = db.query_row("SELECT -1.5::DECIMAL(10,1)", [], |r| r.get(0))?;
2029 assert!((row + 1.5).abs() < f64::EPSILON);
2030
2031 Ok(())
2032 }
2033
2034 #[test]
2035 fn test_variant_column_logical_type_metadata() -> Result<()> {
2036 let db = Connection::open_in_memory()?;
2037 let stmt = db.prepare("SELECT {'a': 42}::VARIANT AS variant_col")?;
2038
2039 let logical_type = stmt.column_logical_type(0);
2040 assert_eq!(logical_type.id(), LogicalTypeId::Variant);
2041 assert_eq!(logical_type.raw_id(), crate::ffi::DUCKDB_TYPE_DUCKDB_TYPE_VARIANT);
2042 assert_eq!(logical_type.num_children(), 0);
2043 assert_eq!(format!("{logical_type:?}"), "Variant");
2044
2045 Ok(())
2046 }
2047
2048 #[test]
2049 fn test_geometry_column_logical_type_metadata() -> Result<()> {
2050 let db = Connection::open_in_memory()?;
2051 let stmt = db.prepare("SELECT NULL::GEOMETRY AS geom")?;
2052
2053 let logical_type = stmt.column_logical_type(0);
2054 assert_eq!(logical_type.id(), LogicalTypeId::Geometry);
2055 assert_eq!(logical_type.raw_id(), crate::ffi::DUCKDB_TYPE_DUCKDB_TYPE_GEOMETRY);
2056 assert_eq!(logical_type.geometry_crs(), None);
2057 assert_eq!(format!("{logical_type:?}"), "Geometry");
2058
2059 Ok(())
2060 }
2061
2062 #[test]
2063 fn test_geometry_column_logical_type_crs_metadata() -> Result<()> {
2064 let db = Connection::open_in_memory()?;
2065 let stmt = db.prepare("SELECT ST_SetCRS('POINT EMPTY'::GEOMETRY, 'EPSG:4326') AS geom")?;
2066
2067 let logical_type = stmt.column_logical_type(0);
2068 assert_eq!(logical_type.id(), LogicalTypeId::Geometry);
2069 assert_eq!(logical_type.geometry_crs(), Some("EPSG:4326".to_string()));
2070
2071 Ok(())
2072 }
2073
2074 #[test]
2075 #[should_panic(expected = "could not retrieve logical type for result column at index 1")]
2076 fn test_column_logical_type_bad_index_panics() {
2077 let db = Connection::open_in_memory().unwrap();
2078 let stmt = db.prepare("SELECT 1").unwrap();
2079
2080 let _ = stmt.column_logical_type(1);
2081 }
2082
2083 #[test]
2084 fn test_variant_result_decode_unsupported() -> Result<()> {
2085 let db = Connection::open_in_memory()?;
2086
2087 let err = match db
2088 .prepare("SELECT 1 AS id, {'a': 42}::VARIANT AS variant_col")?
2089 .query([])
2090 {
2091 Ok(_) => panic!("expected Variant query to fail"),
2092 Err(err) => err,
2093 };
2094
2095 assert_variant_decode_error(err, 1);
2096 Ok(())
2097 }
2098
2099 #[test]
2100 fn test_variant_streaming_result_decode_unsupported() -> Result<()> {
2101 let db = Connection::open_in_memory()?;
2102 let err = match db
2103 .prepare("SELECT 1 AS id, {'a': 42}::VARIANT AS variant_col")?
2104 .stream_arrow([])
2105 {
2106 Ok(_) => panic!("expected Variant streaming query to fail"),
2107 Err(err) => err,
2108 };
2109
2110 assert_variant_decode_error(err, 1);
2111 Ok(())
2112 }
2113
2114 #[test]
2115 fn test_variant_returning_execute_rejected_before_mutation() -> Result<()> {
2116 let db = Connection::open_in_memory()?;
2117 db.execute_batch("CREATE TABLE t(id INTEGER, v VARIANT);")?;
2118
2119 let err = match db.execute("INSERT INTO t VALUES (1, {'a': 42}::VARIANT) RETURNING v", []) {
2120 Ok(_) => panic!("expected Variant RETURNING execute to fail"),
2121 Err(err) => err,
2122 };
2123
2124 assert_variant_decode_error(err, 0);
2125 let count: i64 = db.query_row("SELECT COUNT(*) FROM t", [], |row| row.get(0))?;
2126 assert_eq!(0, count);
2127 Ok(())
2128 }
2129
2130 #[test]
2131 fn test_variant_returning_streaming_rejected_before_mutation() -> Result<()> {
2132 let db = Connection::open_in_memory()?;
2133 db.execute_batch("CREATE TABLE t(id INTEGER, v VARIANT);")?;
2134
2135 let err = match db
2136 .prepare("INSERT INTO t VALUES (1, {'a': 42}::VARIANT) RETURNING v")?
2137 .stream_arrow([])
2138 {
2139 Ok(_) => panic!("expected Variant RETURNING streaming execute to fail"),
2140 Err(err) => err,
2141 };
2142
2143 assert_variant_decode_error(err, 0);
2144 let count: i64 = db.query_row("SELECT COUNT(*) FROM t", [], |row| row.get(0))?;
2145 assert_eq!(0, count);
2146 Ok(())
2147 }
2148
2149 #[test]
2150 fn test_nested_variant_result_decode_unsupported() -> Result<()> {
2151 let db = Connection::open_in_memory()?;
2152
2153 let cases = [
2154 "SELECT [123::VARIANT] AS variant_list",
2155 "SELECT {'v': 123::VARIANT} AS variant_struct",
2156 "SELECT map(['v'], [123::VARIANT]) AS variant_map",
2157 ];
2158
2159 for sql in cases {
2160 let err = match db.prepare(sql)?.query([]) {
2161 Ok(_) => panic!("expected nested Variant query to fail for {sql}"),
2162 Err(err) => err,
2163 };
2164 assert_variant_decode_error(err, 0);
2165 }
2166
2167 Ok(())
2168 }
2169
2170 #[test]
2171 fn test_bind_variant_parameter_delegates_to_duckdb() -> Result<()> {
2172 let db = Connection::open_in_memory()?;
2173 db.execute("CREATE TABLE t (id INTEGER, variant_col VARIANT)", [])?;
2174
2175 db.execute("INSERT INTO t VALUES (?, ?)", crate::params![1, "hello"])?;
2176
2177 let count: i64 = db.query_row("SELECT COUNT(*) FROM t WHERE variant_col IS NOT NULL", [], |row| {
2178 row.get(0)
2179 })?;
2180 assert_eq!(count, 1);
2181 Ok(())
2182 }
2183
2184 #[test]
2186 fn test_bind_unsupported_container_type_returns_error() -> Result<()> {
2187 use crate::types::Value;
2188
2189 struct OwnedList;
2190 impl ToSql for OwnedList {
2191 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
2192 Ok(ToSqlOutput::Owned(Value::List(vec![Value::Int(1), Value::Int(2)])))
2193 }
2194 }
2195
2196 let db = Connection::open_in_memory()?;
2197 db.execute("CREATE TABLE t (id INTEGER, numbers INTEGER[])", [])?;
2198
2199 let list = OwnedList;
2200 let err = db
2201 .execute("INSERT INTO t VALUES (?, ?)", crate::params![1, list])
2202 .unwrap_err();
2203
2204 assert_binding_list_error(err);
2205
2206 let borrowed_list = BorrowedList::new();
2207 let err = db
2208 .execute("INSERT INTO t VALUES (?, ?)", crate::params![2, borrowed_list])
2209 .unwrap_err();
2210 assert_binding_list_error(err);
2211 Ok(())
2212 }
2213
2214 #[test]
2215 fn test_bind_error_clears_partial_parameter_state() -> Result<()> {
2216 let db = Connection::open_in_memory()?;
2217 db.execute("CREATE TABLE t (id INTEGER NOT NULL, name TEXT)", [])?;
2218
2219 let mut stmt = db.prepare("INSERT INTO t VALUES (?, ?)")?;
2220 let list = BorrowedList::new();
2221 let err = stmt.execute(crate::params![1, list]).unwrap_err();
2222 assert_binding_list_error(err);
2223
2224 stmt.raw_bind_parameter(2, "ok")?;
2225 assert!(stmt.raw_execute().is_err());
2226
2227 let count: i32 = db.query_row("SELECT COUNT(*) FROM t", [], |row| row.get(0))?;
2228 assert_eq!(count, 0);
2229
2230 stmt.raw_bind_parameter(1, 7)?;
2231 stmt.raw_bind_parameter(2, "ok")?;
2232 assert_eq!(stmt.raw_execute()?, 1);
2233
2234 let row = db.query_row("SELECT id, name FROM t", [], |row| {
2235 Ok((row.get::<_, i32>(0)?, row.get::<_, String>(1)?))
2236 })?;
2237 assert_eq!(row, (7, "ok".to_string()));
2238 Ok(())
2239 }
2240}