@@ -349,6 +349,33 @@ impl Statement<'_> {
349349 self . query ( params) ?. get_expected_row ( ) . and_then ( f)
350350 }
351351
352+ /// Convenience method to execute a query that is expected to return exactly
353+ /// one row.
354+ ///
355+ /// Returns `Err(QueryReturnedMoreThanOneRow)` if the query returns more than one row.
356+ ///
357+ /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the
358+ /// query truly is optional, you can call
359+ /// [`.optional()`](crate::OptionalExt::optional) on the result of
360+ /// this to get a `Result<Option<T>>` (requires that the trait
361+ /// `duckdb::OptionalExt` is imported).
362+ ///
363+ /// # Failure
364+ ///
365+ /// Will return `Err` if the underlying DuckDB call fails.
366+ pub fn query_one < T , P , F > ( & mut self , params : P , f : F ) -> Result < T >
367+ where
368+ P : Params ,
369+ F : FnOnce ( & Row < ' _ > ) -> Result < T > ,
370+ {
371+ let mut rows = self . query ( params) ?;
372+ let row = rows. get_expected_row ( ) . and_then ( f) ?;
373+ if rows. next ( ) ?. is_some ( ) {
374+ return Err ( Error :: QueryReturnedMoreThanOneRow ) ;
375+ }
376+ Ok ( row)
377+ }
378+
352379 /// Return the row count
353380 #[ inline]
354381 pub fn row_count ( & self ) -> usize {
@@ -635,9 +662,8 @@ mod test {
635662
636663 let mut stmt = db. prepare ( "SELECT id FROM test where name = ?" ) ?;
637664 {
638- let mut rows = stmt. query ( [ & "one" ] ) ?;
639- let id: Result < i32 > = rows. next ( ) ?. unwrap ( ) . get ( 0 ) ;
640- assert_eq ! ( Ok ( 1 ) , id) ;
665+ let id: i32 = stmt. query_one ( [ & "one" ] , |r| r. get ( 0 ) ) ?;
666+ assert_eq ! ( id, 1 ) ;
641667 }
642668 Ok ( ( ) )
643669 }
@@ -825,6 +851,71 @@ mod test {
825851 Ok ( ( ) )
826852 }
827853
854+ #[ test]
855+ fn test_query_one ( ) -> Result < ( ) > {
856+ let db = Connection :: open_in_memory ( ) ?;
857+ let sql = "BEGIN;
858+ CREATE TABLE foo(x INTEGER, y INTEGER);
859+ INSERT INTO foo VALUES(1, 3);
860+ INSERT INTO foo VALUES(2, 4);
861+ END;" ;
862+ db. execute_batch ( sql) ?;
863+
864+ // Exactly one row
865+ let y: i32 = db
866+ . prepare ( "SELECT y FROM foo WHERE x = ?" ) ?
867+ . query_one ( [ 1 ] , |r| r. get ( 0 ) ) ?;
868+ assert_eq ! ( y, 3 ) ;
869+
870+ // No rows
871+ let res: Result < i32 > = db
872+ . prepare ( "SELECT y FROM foo WHERE x = ?" ) ?
873+ . query_one ( [ 99 ] , |r| r. get ( 0 ) ) ;
874+ assert_eq ! ( res. unwrap_err( ) , Error :: QueryReturnedNoRows ) ;
875+
876+ // Multiple rows
877+ let res: Result < i32 > = db. prepare ( "SELECT y FROM foo" ) ?. query_one ( [ ] , |r| r. get ( 0 ) ) ;
878+ assert_eq ! ( res. unwrap_err( ) , Error :: QueryReturnedMoreThanOneRow ) ;
879+
880+ Ok ( ( ) )
881+ }
882+
883+ #[ test]
884+ fn test_query_one_optional ( ) -> Result < ( ) > {
885+ use crate :: OptionalExt ;
886+
887+ let db = Connection :: open_in_memory ( ) ?;
888+ let sql = "BEGIN;
889+ CREATE TABLE foo(x INTEGER, y INTEGER);
890+ INSERT INTO foo VALUES(1, 3);
891+ INSERT INTO foo VALUES(2, 4);
892+ END;" ;
893+ db. execute_batch ( sql) ?;
894+
895+ // Exactly one row
896+ let y: Option < i32 > = db
897+ . prepare ( "SELECT y FROM foo WHERE x = ?" ) ?
898+ . query_one ( [ 1 ] , |r| r. get ( 0 ) )
899+ . optional ( ) ?;
900+ assert_eq ! ( y, Some ( 3 ) ) ;
901+
902+ // No rows
903+ let y: Option < i32 > = db
904+ . prepare ( "SELECT y FROM foo WHERE x = ?" ) ?
905+ . query_one ( [ 99 ] , |r| r. get ( 0 ) )
906+ . optional ( ) ?;
907+ assert_eq ! ( y, None ) ;
908+
909+ // Multiple rows - should still return error (not converted by optional)
910+ let res = db
911+ . prepare ( "SELECT y FROM foo" ) ?
912+ . query_one ( [ ] , |r| r. get :: < _ , i32 > ( 0 ) )
913+ . optional ( ) ;
914+ assert_eq ! ( res. unwrap_err( ) , Error :: QueryReturnedMoreThanOneRow ) ;
915+
916+ Ok ( ( ) )
917+ }
918+
828919 #[ test]
829920 fn test_query_by_column_name ( ) -> Result < ( ) > {
830921 let db = Connection :: open_in_memory ( ) ?;
0 commit comments