@@ -15,7 +15,7 @@ use tokio::net::TcpStream;
1515use tokio_rustls:: rustls:: { OwnedTrustAnchor , RootCertStore } ;
1616use tokio_rustls:: { client:: TlsStream , TlsConnector } ;
1717
18- use crate :: config:: { get_config, Address , User } ;
18+ use crate :: config:: { get_config, get_prepared_statements_cache_size , Address , User } ;
1919use crate :: constants:: * ;
2020use crate :: dns_cache:: { AddrSet , CACHED_RESOLVER } ;
2121use crate :: errors:: { Error , ServerIdentifier } ;
@@ -914,12 +914,16 @@ impl Server {
914914 Ok ( bytes)
915915 }
916916
917+ /// Add the prepared statement to being tracked by this server.
918+ /// The client is processing data that will create a prepared statement on this server.
917919 pub fn will_prepare ( & mut self , name : & str ) {
918920 debug ! ( "Will prepare `{}`" , name) ;
919921
920922 self . prepared_statements . insert ( name. to_string ( ) ) ;
923+ self . stats . prepared_cache_add ( ) ;
921924 }
922925
926+ /// Check if we should prepare a statement on the server.
923927 pub fn should_prepare ( & self , name : & str ) -> bool {
924928 let should_prepare = !self . prepared_statements . contains ( name) ;
925929
@@ -934,6 +938,7 @@ impl Server {
934938 should_prepare
935939 }
936940
941+ /// Create a prepared statement on the server.
937942 pub async fn prepare ( & mut self , parse : & Parse ) -> Result < ( ) , Error > {
938943 debug ! ( "Preparing `{}`" , parse. name) ;
939944
@@ -942,15 +947,82 @@ impl Server {
942947 self . send ( & flush ( ) ) . await ?;
943948
944949 // Read and discard ParseComplete (B)
945- let _ = read_message ( & mut self . stream ) . await ?;
950+ match read_message ( & mut self . stream ) . await {
951+ Ok ( _) => ( ) ,
952+ Err ( err) => {
953+ self . bad = true ;
954+ return Err ( err) ;
955+ }
956+ }
946957
947958 self . prepared_statements . insert ( parse. name . to_string ( ) ) ;
959+ self . stats . prepared_cache_add ( ) ;
948960
949961 debug ! ( "Prepared `{}`" , parse. name) ;
950962
951963 Ok ( ( ) )
952964 }
953965
966+ /// Maintain adequate cache size on the server.
967+ pub async fn maintain_cache ( & mut self ) -> Result < ( ) , Error > {
968+ debug ! ( "Cache maintenance run" ) ;
969+
970+ let max_cache_size = get_prepared_statements_cache_size ( ) ;
971+ let mut names = Vec :: new ( ) ;
972+
973+ while self . prepared_statements . len ( ) >= max_cache_size {
974+ // The prepared statmeents are alphanumerically sorted by the BTree.
975+ // FIFO.
976+ if let Some ( name) = self . prepared_statements . pop_last ( ) {
977+ names. push ( name) ;
978+ }
979+ }
980+
981+ self . deallocate ( names) . await ?;
982+
983+ Ok ( ( ) )
984+ }
985+
986+ /// Remove the prepared statement from being tracked by this server.
987+ /// The client is processing data that will cause the server to close the prepared statement.
988+ pub fn will_close ( & mut self , name : & str ) {
989+ debug ! ( "Will close `{}`" , name) ;
990+
991+ self . prepared_statements . remove ( name) ;
992+ }
993+
994+ /// Close a prepared statement on the server.
995+ pub async fn deallocate ( & mut self , names : Vec < String > ) -> Result < ( ) , Error > {
996+ for name in & names {
997+ debug ! ( "Deallocating prepared statement `{}`" , name) ;
998+
999+ let close = Close :: new ( name) ;
1000+ let bytes: BytesMut = close. try_into ( ) ?;
1001+
1002+ self . send ( & bytes) . await ?;
1003+ }
1004+
1005+ self . send ( & flush ( ) ) . await ?;
1006+
1007+ // Read and discard CloseComplete (3)
1008+ for name in & names {
1009+ match read_message ( & mut self . stream ) . await {
1010+ Ok ( _) => {
1011+ self . prepared_statements . remove ( name) ;
1012+ self . stats . prepared_cache_remove ( ) ;
1013+ debug ! ( "Closed `{}`" , name) ;
1014+ }
1015+
1016+ Err ( err) => {
1017+ self . bad = true ;
1018+ return Err ( err) ;
1019+ }
1020+ } ;
1021+ }
1022+
1023+ Ok ( ( ) )
1024+ }
1025+
9541026 /// If the server is still inside a transaction.
9551027 /// If the client disconnects while the server is in a transaction, we will clean it up.
9561028 pub fn in_transaction ( & self ) -> bool {
0 commit comments