@@ -83,6 +83,19 @@ impl Appender<'_> {
8383 result_from_duckdb_appender ( rc, & mut self . app )
8484 }
8585
86+ /// Append a DEFAULT value to the current row
87+ ///
88+ /// This is called internally when `AppendDefault` is used in `append_row()`.
89+ /// Users should use the `AppendDefault` marker type instead of calling this directly.
90+ #[ inline]
91+ fn append_default ( & mut self ) -> Result < ( ) > {
92+ let rc = unsafe { ffi:: duckdb_append_default ( self . app ) } ;
93+ if rc != 0 {
94+ return Err ( Error :: AppendError ) ;
95+ }
96+ Ok ( ( ) )
97+ }
98+
8699 #[ inline]
87100 pub ( crate ) fn bind_parameters < P > ( & mut self , params : P ) -> Result < ( ) >
88101 where
@@ -95,13 +108,14 @@ impl Appender<'_> {
95108 Ok ( ( ) )
96109 }
97110
98- fn bind_parameter < P : ?Sized + ToSql > ( & self , param : & P ) -> Result < ( ) > {
111+ fn bind_parameter < P : ?Sized + ToSql > ( & mut self , param : & P ) -> Result < ( ) > {
99112 let value = param. to_sql ( ) ?;
100113
101114 let ptr = self . app ;
102115 let value = match value {
103116 ToSqlOutput :: Borrowed ( v) => v,
104117 ToSqlOutput :: Owned ( ref v) => ValueRef :: from ( v) ,
118+ ToSqlOutput :: AppendDefault => return self . append_default ( ) ,
105119 } ;
106120 // NOTE: we ignore the return value here
107121 // because if anything failed, end_row will fail
@@ -189,7 +203,7 @@ impl fmt::Debug for Appender<'_> {
189203
190204#[ cfg( test) ]
191205mod test {
192- use crate :: { params, Connection , Error , Result } ;
206+ use crate :: { params, types :: AppendDefault , Connection , Error , Result } ;
193207
194208 #[ test]
195209 fn test_append_one_row ( ) -> Result < ( ) > {
@@ -389,4 +403,56 @@ mod test {
389403
390404 Ok ( ( ) )
391405 }
406+
407+ #[ test]
408+ fn test_append_default_with_const_default ( ) -> Result < ( ) > {
409+ let db = Connection :: open_in_memory ( ) ?;
410+ db. execute_batch (
411+ "CREATE TABLE test (
412+ id INTEGER,
413+ name VARCHAR,
414+ status VARCHAR DEFAULT 'active'
415+ )" ,
416+ ) ?;
417+
418+ {
419+ let mut app = db. appender ( "test" ) ?;
420+ app. append_row ( params ! [ 1 , "Alice" , AppendDefault ] ) ?;
421+ app. append_row ( params ! [ 2 , "Bob" , AppendDefault ] ) ?;
422+ app. append_row ( params ! [ 3 , AppendDefault , AppendDefault ] ) ?;
423+ app. append_row ( params ! [ 4 , None :: <String >, "inactive" ] ) ?;
424+ }
425+
426+ let rows: Vec < ( i32 , Option < String > , String ) > = db
427+ . prepare ( "SELECT id, name, status FROM test ORDER BY id" ) ?
428+ . query_map ( [ ] , |row| Ok ( ( row. get ( 0 ) ?, row. get ( 1 ) ?, row. get ( 2 ) ?) ) ) ?
429+ . collect :: < Result < Vec < _ > > > ( ) ?;
430+
431+ assert_eq ! ( rows. len( ) , 4 ) ;
432+ assert_eq ! ( rows[ 0 ] , ( 1 , Some ( "Alice" . to_string( ) ) , "active" . to_string( ) ) ) ;
433+ assert_eq ! ( rows[ 1 ] , ( 2 , Some ( "Bob" . to_string( ) ) , "active" . to_string( ) ) ) ;
434+ assert_eq ! ( rows[ 2 ] , ( 3 , None , "active" . to_string( ) ) ) ;
435+ assert_eq ! ( rows[ 3 ] , ( 4 , None , "inactive" . to_string( ) ) ) ;
436+
437+ Ok ( ( ) )
438+ }
439+
440+ #[ test]
441+ fn test_append_default_in_prepared_statement_fails ( ) -> Result < ( ) > {
442+ let db = Connection :: open_in_memory ( ) ?;
443+ db. execute_batch ( "CREATE TABLE test (id INTEGER, name VARCHAR DEFAULT 'test')" ) ?;
444+
445+ // AppendDefault should fail in prepared statements
446+ let mut stmt = db. prepare ( "INSERT INTO test VALUES (?, ?)" ) ?;
447+ let result = stmt. execute ( params ! [ 1 , AppendDefault ] ) ;
448+
449+ assert ! ( result. is_err( ) ) ;
450+ if let Err ( Error :: ToSqlConversionFailure ( e) ) = result {
451+ assert ! ( e. to_string( ) . contains( "only valid for Appender operations" ) ) ;
452+ } else {
453+ panic ! ( "Expected ToSqlConversionFailure error" ) ;
454+ }
455+
456+ Ok ( ( ) )
457+ }
392458}
0 commit comments