@@ -28,6 +28,7 @@ const Packets = require('../packets/index.js');
2828const Commands = require ( '../commands/index.js' ) ;
2929const ConnectionConfig = require ( '../connection_config.js' ) ;
3030const CharsetToEncoding = require ( '../constants/charset_encodings.js' ) ;
31+ const { ER_UNKNOWN_STMT_HANDLER } = require ( '../constants/errors.js' ) ;
3132const {
3233 traceCallback,
3334 tracePromise,
@@ -814,13 +815,45 @@ class BaseConnection extends EventEmitter {
814815 this . addCommand ( executeCommand ) ;
815816 } ;
816817
818+ // We need to intercept and retry prepareAndExecute if we had a stale prepared statement in the cache that the server already released
819+ const key = BaseConnection . statementKey ( options ) ;
820+ const cacheHasKey = this . _statements . has ( key ) ;
821+
817822 if ( executeCommand . onResult ) {
818823 // Callback mode: traceCallback wraps the callback with tracing lifecycle, or calls through directly when no subscribers are registered
819824 const origExecCb = executeCommand . onResult ;
820825 traceCallback (
821826 executeChannel ,
822827 ( wrappedCb ) => {
823- executeCommand . onResult = wrappedCb ;
828+ if ( cacheHasKey ) {
829+ executeCommand . onResult = ( err , ...rest ) => {
830+ if ( err && err . errno === ER_UNKNOWN_STMT_HANDLER ) {
831+ const origEmit = executeCommand . emit . bind ( executeCommand ) ;
832+ executeCommand . emit = ( eventName , ...rest ) => {
833+ if ( eventName === 'end' ) {
834+ // Intercept the 'end' event that will be emitted after this 'error' event is emitted
835+ executeCommand . emit = origEmit ;
836+ return false ;
837+ }
838+
839+ return origEmit ( eventName , ...rest ) ;
840+ } ;
841+
842+ // Listeners may have been added to this execute command, so we re-use it
843+ executeCommand . next = null ;
844+
845+ this . _statements . delete ( key ) ;
846+ executeCommand . onResult = wrappedCb ;
847+ prepareAndExecute ( wrappedCb ) ;
848+ return ;
849+ }
850+
851+ wrappedCb ( err , ...rest ) ;
852+ } ;
853+ } else {
854+ executeCommand . onResult = wrappedCb ;
855+ }
856+
824857 prepareAndExecute ( wrappedCb ) ;
825858 } ,
826859 0 ,
@@ -837,7 +870,43 @@ class BaseConnection extends EventEmitter {
837870 null ,
838871 origExecCb
839872 ) ;
840- } else if ( shouldTrace ( executeChannel ) ) {
873+
874+ return executeCommand ;
875+ }
876+
877+ if ( cacheHasKey ) {
878+ const origEmit = executeCommand . emit . bind ( executeCommand ) ;
879+ executeCommand . emit = ( eventName , firstArg , ...rest ) => {
880+ if (
881+ eventName === 'error' &&
882+ firstArg &&
883+ firstArg . errno === ER_UNKNOWN_STMT_HANDLER
884+ ) {
885+ executeCommand . emit = ( eventName , ...rest ) => {
886+ if ( eventName === 'end' ) {
887+ // Intercept the 'end' event that will be emitted after this 'error' event is emitted
888+ executeCommand . emit = origEmit ;
889+ return false ;
890+ }
891+
892+ return origEmit ( eventName , ...rest ) ;
893+ } ;
894+
895+ // Listeners may have been added to this execute command, so we re-use it
896+ executeCommand . next = null ;
897+
898+ this . _statements . delete ( key ) ;
899+ prepareAndExecute ( ( err ) => {
900+ executeCommand . emit ( 'error' , err ) ;
901+ } ) ;
902+ return false ;
903+ }
904+
905+ return origEmit ( eventName , firstArg , ...rest ) ;
906+ } ;
907+ }
908+
909+ if ( shouldTrace ( executeChannel ) ) {
841910 // Event-emitter mode: tracePromise wraps the async lifecycle
842911 tracePromise (
843912 executeChannel ,
0 commit comments