@@ -69,6 +69,7 @@ pub struct SessionResidueState {
6969 may_have_prepared_statement : bool ,
7070 may_have_user_variable : bool ,
7171 may_have_session_setting : bool ,
72+ may_have_statement_diagnostics : bool ,
7273 may_have_next_transaction_mode_override : bool ,
7374 may_have_transaction_mode_override : bool ,
7475 may_have_untracked_session_state : bool ,
@@ -512,6 +513,7 @@ impl SessionResidueState {
512513 may_have_prepared_statement : effects. creates_prepared_statement ,
513514 may_have_user_variable : effects. sets_user_variable ,
514515 may_have_session_setting : effects. sets_session_setting ,
516+ may_have_statement_diagnostics : effects. sets_statement_diagnostics ,
515517 may_have_next_transaction_mode_override : effects. sets_next_transaction_mode_override ,
516518 may_have_transaction_mode_override : effects. sets_transaction_mode_override ,
517519 may_have_untracked_session_state : effects. may_leave_unknown_state ,
@@ -527,6 +529,8 @@ impl SessionResidueState {
527529 may_have_user_variable : self . may_have_user_variable || other. may_have_user_variable ,
528530 may_have_session_setting : self . may_have_session_setting
529531 || other. may_have_session_setting ,
532+ may_have_statement_diagnostics : self . may_have_statement_diagnostics
533+ || other. may_have_statement_diagnostics ,
530534 may_have_next_transaction_mode_override : self . may_have_next_transaction_mode_override
531535 || other. may_have_next_transaction_mode_override ,
532536 may_have_transaction_mode_override : self . may_have_transaction_mode_override
@@ -540,14 +544,22 @@ impl SessionResidueState {
540544 if effects. clears_all_session_residue {
541545 return Self :: default ( ) ;
542546 }
543- let state = if effects. consumes_next_transaction_mode_override {
547+ let state = if effects. clears_statement_diagnostics {
544548 Self {
545- may_have_next_transaction_mode_override : false ,
549+ may_have_statement_diagnostics : false ,
546550 ..self
547551 }
548552 } else {
549553 self
550554 } ;
555+ let state = if effects. consumes_next_transaction_mode_override {
556+ Self {
557+ may_have_next_transaction_mode_override : false ,
558+ ..state
559+ }
560+ } else {
561+ state
562+ } ;
551563 state. merged_with ( Self :: from_effects ( effects) )
552564 }
553565
@@ -556,6 +568,7 @@ impl SessionResidueState {
556568 || self . may_have_prepared_statement
557569 || self . may_have_user_variable
558570 || self . may_have_session_setting
571+ || self . may_have_statement_diagnostics
559572 || self . may_have_untracked_session_state
560573 }
561574
@@ -616,6 +629,13 @@ impl SessionResidueState {
616629 ..self
617630 }
618631 }
632+
633+ fn with_statement_diagnostics_cleared ( self ) -> Self {
634+ Self {
635+ may_have_statement_diagnostics : false ,
636+ ..self
637+ }
638+ }
619639}
620640
621641pub ( crate ) fn mysql_statement_can_cleanup_retained_session_for_preflight (
@@ -900,6 +920,8 @@ struct StatementSessionResidueEffects {
900920 creates_prepared_statement : bool ,
901921 sets_user_variable : bool ,
902922 sets_session_setting : bool ,
923+ sets_statement_diagnostics : bool ,
924+ clears_statement_diagnostics : bool ,
903925 sets_next_transaction_mode_override : bool ,
904926 sets_transaction_mode_override : bool ,
905927 consumes_next_transaction_mode_override : bool ,
@@ -913,10 +935,19 @@ impl StatementSessionResidueEffects {
913935 || self . creates_prepared_statement
914936 || self . sets_user_variable
915937 || self . sets_session_setting
938+ || self . sets_statement_diagnostics
916939 || self . sets_next_transaction_mode_override
917940 || self . sets_transaction_mode_override
918941 || self . may_leave_unknown_state
919942 }
943+
944+ fn without_statement_diagnostics ( self ) -> Self {
945+ Self {
946+ sets_statement_diagnostics : false ,
947+ clears_statement_diagnostics : false ,
948+ ..self
949+ }
950+ }
920951}
921952
922953#[ derive( Clone , Copy , Debug , Default , PartialEq , Eq ) ]
@@ -983,6 +1014,10 @@ impl StatementSessionEffects {
9831014 self . session_residue . may_leave_session_residue ( )
9841015 }
9851016
1017+ pub ( crate ) fn preserves_statement_diagnostics ( self ) -> bool {
1018+ self . session_residue . sets_statement_diagnostics
1019+ }
1020+
9861021 pub ( crate ) fn has_implicit_commit ( self ) -> bool {
9871022 self . transaction . has_implicit_commit
9881023 }
@@ -1425,7 +1460,10 @@ impl MySqlBatchSessionEffects {
14251460 } else {
14261461 self . session_residue_state = self
14271462 . session_residue_state
1428- . merged_with ( SessionResidueState :: from_effects ( effects. session_residue ) ) ;
1463+ . with_statement_diagnostics_cleared ( )
1464+ . merged_with ( SessionResidueState :: from_effects (
1465+ effects. session_residue . without_statement_diagnostics ( ) ,
1466+ ) ) ;
14291467 }
14301468
14311469 if cleanup_effects_confirmed && effects. releases_table_lock ( ) {
@@ -1973,7 +2011,11 @@ pub(crate) fn retained_session_state_after_statement(
19732011 } else {
19742012 prior_state. session_residue_state ( )
19752013 } ;
1976- prior_residue. merged_with ( SessionResidueState :: from_effects ( effects. session_residue ) )
2014+ prior_residue
2015+ . with_statement_diagnostics_cleared ( )
2016+ . merged_with ( SessionResidueState :: from_effects (
2017+ effects. session_residue . without_statement_diagnostics ( ) ,
2018+ ) )
19772019 } else if effects. releases_physical_session ( ) {
19782020 SessionResidueState :: default ( )
19792021 } else {
@@ -4256,6 +4298,14 @@ fn mysql_session_residue_effects_for_analysis(
42564298
42574299 let leading_keyword = analysis. leading_keyword ( ) ;
42584300 let mut effects = StatementSessionResidueEffects :: default ( ) ;
4301+ let sets_found_rows = matches ! ( leading_keyword, Some ( "SELECT" | "WITH" ) )
4302+ && analysis
4303+ . words ( )
4304+ . iter ( )
4305+ . any ( |word| word == "SQL_CALC_FOUND_ROWS" ) ;
4306+ effects. sets_statement_diagnostics =
4307+ sets_found_rows || analysis. classify_for_db_type ( db_type) == SqlKind :: Dml ;
4308+ effects. clears_statement_diagnostics = !effects. sets_statement_diagnostics ;
42594309 if mysql_statement_assigns_user_variable ( sql, analysis) {
42604310 effects. sets_user_variable = true ;
42614311 }
@@ -5661,6 +5711,56 @@ mod tests {
56615711 assert ! ( !plain_hint. may_leave_untracked_session_state) ;
56625712 }
56635713
5714+ #[ test]
5715+ fn mysql_statement_diagnostics_are_retained_until_the_next_select ( ) {
5716+ for db_type in [ DatabaseType :: MySQL , DatabaseType :: MariaDB ] {
5717+ let post_processor = statement_session_post_processor_for ( db_type) ;
5718+ for sql in [
5719+ "UPDATE t SET value = value + 1" ,
5720+ "SELECT SQL_CALC_FOUND_ROWS id FROM t LIMIT 1" ,
5721+ ] {
5722+ let effects = post_processor. effects_for_sql ( sql) ;
5723+ assert ! (
5724+ effects. preserves_statement_diagnostics( ) ,
5725+ "{db_type} should preserve diagnostics after `{sql}`"
5726+ ) ;
5727+ let retained = retained_session_state_after_statement (
5728+ post_processor,
5729+ RetainedSessionState :: default ( ) ,
5730+ effects,
5731+ false ,
5732+ false ,
5733+ false ,
5734+ false ,
5735+ ) ;
5736+ assert ! (
5737+ retained. may_have_untracked_session_state( ) ,
5738+ "{db_type} should retain the physical session after `{sql}`"
5739+ ) ;
5740+
5741+ let next_select = post_processor. effects_for_sql ( "SELECT ROW_COUNT()" ) ;
5742+ assert ! ( !next_select. preserves_statement_diagnostics( ) ) ;
5743+ let cleared = retained_session_state_after_statement (
5744+ post_processor,
5745+ retained,
5746+ next_select,
5747+ false ,
5748+ false ,
5749+ false ,
5750+ false ,
5751+ ) ;
5752+ assert ! (
5753+ !cleared. may_have_untracked_session_state( ) ,
5754+ "{db_type} should release consumed diagnostics after the next SELECT"
5755+ ) ;
5756+ }
5757+
5758+ assert ! ( !post_processor
5759+ . effects_for_sql( "SELECT 'SQL_CALC_FOUND_ROWS' AS note" )
5760+ . preserves_statement_diagnostics( ) ) ;
5761+ }
5762+ }
5763+
56645764 #[ test]
56655765 fn mysql_with_select_session_side_effects_are_tracked ( ) {
56665766 let assignment_hint = mysql_session_state_hint_for_sql (
0 commit comments