@@ -497,10 +497,49 @@ func (h *Handler) Progress(w http.ResponseWriter, r *http.Request) {
497497 })
498498}
499499
500+ // findMySQLBinlog locates the mysqlbinlog binary by checking PATH first, then
501+ // querying MySQL's basedir, and finally checking common installation paths.
502+ func findMySQLBinlog (ctx context.Context , conn * connector.MySQLConnector ) (string , error ) {
503+ // 1. Try PATH first.
504+ if path , err := exec .LookPath ("mysqlbinlog" ); err == nil {
505+ return path , nil
506+ }
507+
508+ // 2. Try MySQL basedir (SHOW VARIABLES LIKE 'basedir').
509+ basedir , err := conn .GetBasedir (ctx )
510+ if err == nil && basedir != "" {
511+ for _ , name := range []string {"mysqlbinlog" , "mysqlbinlog.exe" } {
512+ candidate := filepath .Join (basedir , "bin" , name )
513+ if fi , statErr := os .Stat (candidate ); statErr == nil && fi .Mode ().IsRegular () {
514+ abs , _ := filepath .Abs (candidate )
515+ return abs , nil
516+ }
517+ }
518+ }
519+
520+ // 3. Common installation paths.
521+ commonPaths := []string {
522+ "/usr/bin/mysqlbinlog" ,
523+ "/usr/local/mysql/bin/mysqlbinlog" ,
524+ "/opt/homebrew/bin/mysqlbinlog" ,
525+ "C:\\ Program Files\\ MySQL\\ MySQL Server 8.0\\ bin\\ mysqlbinlog.exe" ,
526+ "C:\\ Program Files\\ MySQL\\ MySQL Server 8.4\\ bin\\ mysqlbinlog.exe" ,
527+ "C:\\ Program Files\\ MySQL\\ MySQL Server 9.0\\ bin\\ mysqlbinlog.exe" ,
528+ }
529+ for _ , p := range commonPaths {
530+ if fi , err := os .Stat (p ); err == nil && fi .Mode ().IsRegular () {
531+ abs , _ := filepath .Abs (p )
532+ return abs , nil
533+ }
534+ }
535+
536+ return "" , fmt .Errorf ("mysqlbinlog not found in $PATH, MySQL basedir, or common installation paths; install mysql-client or add mysqlbinlog to your PATH" )
537+ }
538+
500539// parseBinlogRemote uses mysqlbinlog --read-from-remote-server to parse binlog
501540// files from a remote MySQL server. This is the fallback when binlog files are
502541// not accessible on the local filesystem.
503- func (h * Handler ) parseBinlogRemote (cfg connector.ConnConfig , binlogNames []string , targetTable string , recoveryTime time.Time ) (* connector.ParseResult , error ) {
542+ func (h * Handler ) parseBinlogRemote (mysqlbinlogPath string , cfg connector.ConnConfig , binlogNames []string , targetTable string , recoveryTime time.Time ) (* connector.ParseResult , error ) {
504543 parts := strings .SplitN (targetTable , "." , 2 )
505544 if len (parts ) != 2 {
506545 return nil , fmt .Errorf ("invalid target table %q: expected schema.table format" , targetTable )
@@ -519,8 +558,8 @@ func (h *Handler) parseBinlogRemote(cfg connector.ConnConfig, binlogNames []stri
519558 "--protocol=TCP" ,
520559 }
521560 args = append (args , binlogNames ... )
522-
523- cmd := exec .Command ("mysqlbinlog" , args ... )
561+
562+ cmd := exec .Command (mysqlbinlogPath , args ... )
524563 stdout , err := cmd .StdoutPipe ()
525564 if err != nil {
526565 return nil , fmt .Errorf ("mysqlbinlog pipe: %w" , err )
@@ -740,7 +779,7 @@ func (h *Handler) runOperation(op *Operation, operator string) {
740779 op .PreflightRes = & PreflightResult {
741780 CheckedAt : time .Now (),
742781 BinlogFiles : binlogNames ,
743- EarliestTime : time .Now (). Add ( - 24 * time . Hour ) ,
782+ EarliestTime : time.Time {} ,
744783 EstimatedSize : totalSize ,
745784 }
746785 _ = h .opStore .Update (op )
@@ -758,7 +797,12 @@ func (h *Handler) runOperation(op *Operation, operator string) {
758797 if len (paths ) > 0 {
759798 if _ , statErr := os .Stat (paths [0 ]); os .IsNotExist (statErr ) {
760799 log .Printf ("pitr: binlog files not accessible locally, trying mysqlbinlog remote for op %s" , op .ID )
761- parseRes , err = h .parseBinlogRemote (connCfg , binlogNames , op .TargetTable , op .RecoveryTime )
800+ mysqlbinlogPath , findErr := findMySQLBinlog (ctx , conn )
801+ if findErr != nil {
802+ h .failOperation (op , "parse binlogs (remote): %v" , findErr )
803+ return
804+ }
805+ parseRes , err = h .parseBinlogRemote (mysqlbinlogPath , connCfg , binlogNames , op .TargetTable , op .RecoveryTime )
762806 if err != nil {
763807 h .failOperation (op , "parse binlogs (remote): %v" , err )
764808 return
0 commit comments