github.com/eatigo/migrate@v3.0.2-0.20210729130915-7610befb1b6b+incompatible/migrate.go (about) 1 // Package migrate reads migrations from sources and runs them against databases. 2 // Sources are defined by the `source.Driver` and databases by the `database.Driver` 3 // interface. The driver interfaces are kept "dump", all migration logic is kept 4 // in this package. 5 package migrate 6 7 import ( 8 "fmt" 9 "os" 10 "sync" 11 "time" 12 13 "github.com/eatigo/migrate/database" 14 "github.com/eatigo/migrate/source" 15 ) 16 17 // DefaultPrefetchMigrations sets the number of migrations to pre-read 18 // from the source. This is helpful if the source is remote, but has little 19 // effect for a local source (i.e. file system). 20 // Please note that this setting has a major impact on the memory usage, 21 // since each pre-read migration is buffered in memory. See DefaultBufferSize. 22 var DefaultPrefetchMigrations = uint(10) 23 24 // DefaultLockTimeout sets the max time a database driver has to acquire a lock. 25 var DefaultLockTimeout = 15 * time.Second 26 27 var ( 28 ErrNoChange = fmt.Errorf("no change") 29 ErrNilVersion = fmt.Errorf("no migration") 30 ErrLocked = fmt.Errorf("database locked") 31 ErrLockTimeout = fmt.Errorf("timeout: can't acquire database lock") 32 ) 33 34 // ErrShortLimit is an error returned when not enough migrations 35 // can be returned by a source for a given limit. 36 type ErrShortLimit struct { 37 Short uint 38 } 39 40 // Error implements the error interface. 41 func (e ErrShortLimit) Error() string { 42 return fmt.Sprintf("limit %v short", e.Short) 43 } 44 45 type ErrDirty struct { 46 Version int 47 } 48 49 func (e ErrDirty) Error() string { 50 return fmt.Sprintf("Dirty database version %v. Fix and force version.", e.Version) 51 } 52 53 type Migrate struct { 54 sourceName string 55 sourceDrv source.Driver 56 databaseName string 57 databaseDrv database.Driver 58 59 // Log accepts a Logger interface 60 Log Logger 61 62 // GracefulStop accepts `true` and will stop executing migrations 63 // as soon as possible at a safe break point, so that the database 64 // is not corrupted. 65 GracefulStop chan bool 66 isGracefulStop bool 67 68 isLockedMu *sync.Mutex 69 isLocked bool 70 71 // PrefetchMigrations defaults to DefaultPrefetchMigrations, 72 // but can be set per Migrate instance. 73 PrefetchMigrations uint 74 75 // LockTimeout defaults to DefaultLockTimeout, 76 // but can be set per Migrate instance. 77 LockTimeout time.Duration 78 } 79 80 // New returns a new Migrate instance from a source URL and a database URL. 81 // The URL scheme is defined by each driver. 82 func New(sourceUrl, databaseUrl string) (*Migrate, error) { 83 m := newCommon() 84 85 if sourceUrl != "" { 86 sourceName, err := schemeFromUrl(sourceUrl) 87 if err != nil { 88 return nil, err 89 } 90 m.sourceName = sourceName 91 } 92 databaseName, err := schemeFromUrl(databaseUrl) 93 if err != nil { 94 return nil, err 95 } 96 m.databaseName = databaseName 97 98 if sourceUrl != "" { 99 sourceDrv, err := source.Open(sourceUrl) 100 if err != nil { 101 return nil, err 102 } 103 m.sourceDrv = sourceDrv 104 } 105 106 databaseDrv, err := database.Open(databaseUrl) 107 if err != nil { 108 return nil, err 109 } 110 m.databaseDrv = databaseDrv 111 112 return m, nil 113 } 114 115 // NewWithDatabaseInstance returns a new Migrate instance from a source URL 116 // and an existing database instance. The source URL scheme is defined by each driver. 117 // Use any string that can serve as an identifier during logging as databaseName. 118 // You are responsible for closing the underlying database client if necessary. 119 func NewWithDatabaseInstance(sourceUrl string, databaseName string, databaseInstance database.Driver) (*Migrate, error) { 120 m := newCommon() 121 122 sourceName, err := schemeFromUrl(sourceUrl) 123 if err != nil { 124 return nil, err 125 } 126 m.sourceName = sourceName 127 128 m.databaseName = databaseName 129 130 sourceDrv, err := source.Open(sourceUrl) 131 if err != nil { 132 return nil, err 133 } 134 m.sourceDrv = sourceDrv 135 136 m.databaseDrv = databaseInstance 137 138 return m, nil 139 } 140 141 // NewWithSourceInstance returns a new Migrate instance from an existing source instance 142 // and a database URL. The database URL scheme is defined by each driver. 143 // Use any string that can serve as an identifier during logging as sourceName. 144 // You are responsible for closing the underlying source client if necessary. 145 func NewWithSourceInstance(sourceName string, sourceInstance source.Driver, databaseUrl string) (*Migrate, error) { 146 m := newCommon() 147 148 databaseName, err := schemeFromUrl(databaseUrl) 149 if err != nil { 150 return nil, err 151 } 152 m.databaseName = databaseName 153 154 m.sourceName = sourceName 155 156 databaseDrv, err := database.Open(databaseUrl) 157 if err != nil { 158 return nil, err 159 } 160 m.databaseDrv = databaseDrv 161 162 m.sourceDrv = sourceInstance 163 164 return m, nil 165 } 166 167 // NewWithInstance returns a new Migrate instance from an existing source and 168 // database instance. Use any string that can serve as an identifier during logging 169 // as sourceName and databaseName. You are responsible for closing down 170 // the underlying source and database client if necessary. 171 func NewWithInstance(sourceName string, sourceInstance source.Driver, databaseName string, databaseInstance database.Driver) (*Migrate, error) { 172 m := newCommon() 173 174 m.sourceName = sourceName 175 m.databaseName = databaseName 176 177 m.sourceDrv = sourceInstance 178 m.databaseDrv = databaseInstance 179 180 return m, nil 181 } 182 183 func newCommon() *Migrate { 184 return &Migrate{ 185 GracefulStop: make(chan bool, 1), 186 PrefetchMigrations: DefaultPrefetchMigrations, 187 LockTimeout: DefaultLockTimeout, 188 isLockedMu: &sync.Mutex{}, 189 } 190 } 191 192 // Close closes the the source and the database. 193 func (m *Migrate) Close() (source error, database error) { 194 databaseSrvClose := make(chan error) 195 sourceSrvClose := make(chan error) 196 197 m.logVerbosePrintf("Closing source and database\n") 198 199 go func() { 200 databaseSrvClose <- m.databaseDrv.Close() 201 }() 202 203 go func() { 204 if m.sourceDrv != nil { 205 sourceSrvClose <- m.sourceDrv.Close() 206 } else { 207 sourceSrvClose <- nil 208 } 209 }() 210 211 return <-sourceSrvClose, <-databaseSrvClose 212 } 213 214 // Migrate looks at the currently active migration version, 215 // then migrates either up or down to the specified version. 216 func (m *Migrate) Migrate(version uint) error { 217 if err := m.lock(); err != nil { 218 return err 219 } 220 221 curVersion, dirty, err := m.databaseDrv.Version() 222 if err != nil { 223 return m.unlockErr(err) 224 } 225 226 if dirty { 227 return m.unlockErr(ErrDirty{curVersion}) 228 } 229 230 ret := make(chan interface{}, m.PrefetchMigrations) 231 go m.read(curVersion, int(version), ret) 232 233 return m.unlockErr(m.runMigrations(ret)) 234 } 235 236 // Steps looks at the currently active migration version. 237 // It will migrate up if n > 0, and down if n < 0. 238 func (m *Migrate) Steps(n int) error { 239 if n == 0 { 240 return ErrNoChange 241 } 242 243 if err := m.lock(); err != nil { 244 return err 245 } 246 247 curVersion, dirty, err := m.databaseDrv.Version() 248 if err != nil { 249 return m.unlockErr(err) 250 } 251 252 if dirty { 253 return m.unlockErr(ErrDirty{curVersion}) 254 } 255 256 ret := make(chan interface{}, m.PrefetchMigrations) 257 258 if n > 0 { 259 go m.readUp(curVersion, n, ret) 260 } else { 261 go m.readDown(curVersion, -n, ret) 262 } 263 264 return m.unlockErr(m.runMigrations(ret)) 265 } 266 267 // Up looks at the currently active migration version 268 // and will migrate all the way up (applying all up migrations). 269 func (m *Migrate) Up() error { 270 if err := m.lock(); err != nil { 271 return err 272 } 273 274 curVersion, dirty, err := m.databaseDrv.Version() 275 if err != nil { 276 return m.unlockErr(err) 277 } 278 279 if dirty { 280 return m.unlockErr(ErrDirty{curVersion}) 281 } 282 283 ret := make(chan interface{}, m.PrefetchMigrations) 284 285 go m.readUp(curVersion, -1, ret) 286 return m.unlockErr(m.runMigrations(ret)) 287 } 288 289 // Down looks at the currently active migration version 290 // and will migrate all the way down (applying all down migrations). 291 func (m *Migrate) Down() error { 292 if err := m.lock(); err != nil { 293 return err 294 } 295 296 curVersion, dirty, err := m.databaseDrv.Version() 297 if err != nil { 298 return m.unlockErr(err) 299 } 300 301 if dirty { 302 return m.unlockErr(ErrDirty{curVersion}) 303 } 304 305 ret := make(chan interface{}, m.PrefetchMigrations) 306 go m.readDown(curVersion, -1, ret) 307 return m.unlockErr(m.runMigrations(ret)) 308 } 309 310 // Drop deletes everything in the database. 311 func (m *Migrate) Drop() error { 312 if err := m.lock(); err != nil { 313 return err 314 } 315 if err := m.databaseDrv.Drop(); err != nil { 316 return m.unlockErr(err) 317 } 318 return m.unlock() 319 } 320 321 // Run runs any migration provided by you against the database. 322 // It does not check any currently active version in database. 323 // Usually you don't need this function at all. Use Migrate, 324 // Steps, Up or Down instead. 325 func (m *Migrate) Run(migration ...*Migration) error { 326 if len(migration) == 0 { 327 return ErrNoChange 328 } 329 330 if err := m.lock(); err != nil { 331 return err 332 } 333 334 curVersion, dirty, err := m.databaseDrv.Version() 335 if err != nil { 336 return m.unlockErr(err) 337 } 338 339 if dirty { 340 return m.unlockErr(ErrDirty{curVersion}) 341 } 342 343 ret := make(chan interface{}, m.PrefetchMigrations) 344 345 go func() { 346 defer close(ret) 347 for _, migr := range migration { 348 if m.PrefetchMigrations > 0 && migr.Body != nil { 349 m.logVerbosePrintf("Start buffering %v\n", migr.LogString()) 350 } else { 351 m.logVerbosePrintf("Scheduled %v\n", migr.LogString()) 352 } 353 354 ret <- migr 355 go migr.Buffer() 356 } 357 }() 358 359 return m.unlockErr(m.runMigrations(ret)) 360 } 361 362 // Force sets a migration version. 363 // It does not check any currently active version in database. 364 // It resets the dirty state to false. 365 func (m *Migrate) Force(version int) error { 366 if version < -1 { 367 panic("version must be >= -1") 368 } 369 370 if err := m.lock(); err != nil { 371 return err 372 } 373 374 if err := m.databaseDrv.SetVersion(version, false); err != nil { 375 return m.unlockErr(err) 376 } 377 378 return m.unlock() 379 } 380 381 // Version returns the currently active migration version. 382 // If no migration has been applied, yet, it will return ErrNilVersion. 383 func (m *Migrate) Version() (version uint, dirty bool, err error) { 384 v, d, err := m.databaseDrv.Version() 385 if err != nil { 386 return 0, false, err 387 } 388 389 if v == database.NilVersion { 390 return 0, false, ErrNilVersion 391 } 392 393 return suint(v), d, nil 394 } 395 396 // read reads either up or down migrations from source `from` to `to`. 397 // Each migration is then written to the ret channel. 398 // If an error occurs during reading, that error is written to the ret channel, too. 399 // Once read is done reading it will close the ret channel. 400 func (m *Migrate) read(from int, to int, ret chan<- interface{}) { 401 defer close(ret) 402 403 // check if from version exists 404 if from >= 0 { 405 if m.versionExists(suint(from)) != nil { 406 ret <- os.ErrNotExist 407 return 408 } 409 } 410 411 // check if to version exists 412 if to >= 0 { 413 if m.versionExists(suint(to)) != nil { 414 ret <- os.ErrNotExist 415 return 416 } 417 } 418 419 // no change? 420 if from == to { 421 ret <- ErrNoChange 422 return 423 } 424 425 if from < to { 426 // it's going up 427 // apply first migration if from is nil version 428 if from == -1 { 429 firstVersion, err := m.sourceDrv.First() 430 if err != nil { 431 ret <- err 432 return 433 } 434 435 migr, err := m.newMigration(firstVersion, int(firstVersion)) 436 if err != nil { 437 ret <- err 438 return 439 } 440 441 ret <- migr 442 go migr.Buffer() 443 from = int(firstVersion) 444 } 445 446 // run until we reach target ... 447 for from < to { 448 if m.stop() { 449 return 450 } 451 452 next, err := m.sourceDrv.Next(suint(from)) 453 if err != nil { 454 ret <- err 455 return 456 } 457 458 migr, err := m.newMigration(next, int(next)) 459 if err != nil { 460 ret <- err 461 return 462 } 463 464 ret <- migr 465 go migr.Buffer() 466 from = int(next) 467 } 468 469 } else { 470 // it's going down 471 // run until we reach target ... 472 for from > to && from >= 0 { 473 if m.stop() { 474 return 475 } 476 477 prev, err := m.sourceDrv.Prev(suint(from)) 478 if os.IsNotExist(err) && to == -1 { 479 // apply nil migration 480 migr, err := m.newMigration(suint(from), -1) 481 if err != nil { 482 ret <- err 483 return 484 } 485 ret <- migr 486 go migr.Buffer() 487 return 488 489 } else if err != nil { 490 ret <- err 491 return 492 } 493 494 migr, err := m.newMigration(suint(from), int(prev)) 495 if err != nil { 496 ret <- err 497 return 498 } 499 500 ret <- migr 501 go migr.Buffer() 502 from = int(prev) 503 } 504 } 505 } 506 507 // readUp reads up migrations from `from` limitted by `limit`. 508 // limit can be -1, implying no limit and reading until there are no more migrations. 509 // Each migration is then written to the ret channel. 510 // If an error occurs during reading, that error is written to the ret channel, too. 511 // Once readUp is done reading it will close the ret channel. 512 func (m *Migrate) readUp(from int, limit int, ret chan<- interface{}) { 513 defer close(ret) 514 515 // check if from version exists 516 if from >= 0 { 517 if m.versionExists(suint(from)) != nil { 518 ret <- os.ErrNotExist 519 return 520 } 521 } 522 523 if limit == 0 { 524 ret <- ErrNoChange 525 return 526 } 527 528 count := 0 529 for count < limit || limit == -1 { 530 if m.stop() { 531 return 532 } 533 534 // apply first migration if from is nil version 535 if from == -1 { 536 firstVersion, err := m.sourceDrv.First() 537 if err != nil { 538 ret <- err 539 return 540 } 541 542 migr, err := m.newMigration(firstVersion, int(firstVersion)) 543 if err != nil { 544 ret <- err 545 return 546 } 547 548 ret <- migr 549 go migr.Buffer() 550 from = int(firstVersion) 551 count++ 552 continue 553 } 554 555 // apply next migration 556 next, err := m.sourceDrv.Next(suint(from)) 557 if os.IsNotExist(err) { 558 // no limit, but no migrations applied? 559 if limit == -1 && count == 0 { 560 ret <- ErrNoChange 561 return 562 } 563 564 // no limit, reached end 565 if limit == -1 { 566 return 567 } 568 569 // reached end, and didn't apply any migrations 570 if limit > 0 && count == 0 { 571 ret <- os.ErrNotExist 572 return 573 } 574 575 // applied less migrations than limit? 576 if count < limit { 577 ret <- ErrShortLimit{suint(limit - count)} 578 return 579 } 580 } 581 if err != nil { 582 ret <- err 583 return 584 } 585 586 migr, err := m.newMigration(next, int(next)) 587 if err != nil { 588 ret <- err 589 return 590 } 591 592 ret <- migr 593 go migr.Buffer() 594 from = int(next) 595 count++ 596 } 597 } 598 599 // readDown reads down migrations from `from` limitted by `limit`. 600 // limit can be -1, implying no limit and reading until there are no more migrations. 601 // Each migration is then written to the ret channel. 602 // If an error occurs during reading, that error is written to the ret channel, too. 603 // Once readDown is done reading it will close the ret channel. 604 func (m *Migrate) readDown(from int, limit int, ret chan<- interface{}) { 605 defer close(ret) 606 607 // check if from version exists 608 if from >= 0 { 609 if m.versionExists(suint(from)) != nil { 610 ret <- os.ErrNotExist 611 return 612 } 613 } 614 615 if limit == 0 { 616 ret <- ErrNoChange 617 return 618 } 619 620 // no change if already at nil version 621 if from == -1 && limit == -1 { 622 ret <- ErrNoChange 623 return 624 } 625 626 // can't go over limit if already at nil version 627 if from == -1 && limit > 0 { 628 ret <- os.ErrNotExist 629 return 630 } 631 632 count := 0 633 for count < limit || limit == -1 { 634 if m.stop() { 635 return 636 } 637 638 prev, err := m.sourceDrv.Prev(suint(from)) 639 if os.IsNotExist(err) { 640 // no limit or haven't reached limit, apply "first" migration 641 if limit == -1 || limit-count > 0 { 642 firstVersion, err := m.sourceDrv.First() 643 if err != nil { 644 ret <- err 645 return 646 } 647 648 migr, err := m.newMigration(firstVersion, -1) 649 if err != nil { 650 ret <- err 651 return 652 } 653 ret <- migr 654 go migr.Buffer() 655 count++ 656 } 657 658 if count < limit { 659 ret <- ErrShortLimit{suint(limit - count)} 660 } 661 return 662 } 663 if err != nil { 664 ret <- err 665 return 666 } 667 668 migr, err := m.newMigration(suint(from), int(prev)) 669 if err != nil { 670 ret <- err 671 return 672 } 673 674 ret <- migr 675 go migr.Buffer() 676 from = int(prev) 677 count++ 678 } 679 } 680 681 // runMigrations reads *Migration and error from a channel. Any other type 682 // sent on this channel will result in a panic. Each migration is then 683 // proxied to the database driver and run against the database. 684 // Before running a newly received migration it will check if it's supposed 685 // to stop execution because it might have received a stop signal on the 686 // GracefulStop channel. 687 func (m *Migrate) runMigrations(ret <-chan interface{}) error { 688 for r := range ret { 689 690 if m.stop() { 691 return nil 692 } 693 694 switch r.(type) { 695 case error: 696 return r.(error) 697 698 case *Migration: 699 migr := r.(*Migration) 700 701 // set version with dirty state 702 if err := m.databaseDrv.SetVersion(migr.TargetVersion, true); err != nil { 703 return err 704 } 705 706 if migr.Body != nil { 707 m.logVerbosePrintf("Read and execute %v\n", migr.LogString()) 708 if err := m.databaseDrv.Run(migr.BufferedBody); err != nil { 709 return err 710 } 711 } 712 713 // set clean state 714 if err := m.databaseDrv.SetVersion(migr.TargetVersion, false); err != nil { 715 return err 716 } 717 718 endTime := time.Now() 719 readTime := migr.FinishedReading.Sub(migr.StartedBuffering) 720 runTime := endTime.Sub(migr.FinishedReading) 721 722 // log either verbose or normal 723 if m.Log != nil { 724 if m.Log.Verbose() { 725 m.logPrintf("Finished %v (read %v, ran %v)\n", migr.LogString(), readTime, runTime) 726 } else { 727 m.logPrintf("%v (%v)\n", migr.LogString(), readTime+runTime) 728 } 729 } 730 731 default: 732 panic("unknown type") 733 } 734 } 735 return nil 736 } 737 738 // versionExists checks the source if either the up or down migration for 739 // the specified migration version exists. 740 func (m *Migrate) versionExists(version uint) error { 741 // try up migration first 742 up, _, err := m.sourceDrv.ReadUp(version) 743 if err == nil { 744 defer up.Close() 745 } 746 if os.IsExist(err) { 747 return nil 748 } else if !os.IsNotExist(err) { 749 return err 750 } 751 752 // then try down migration 753 down, _, err := m.sourceDrv.ReadDown(version) 754 if err == nil { 755 defer down.Close() 756 } 757 if os.IsExist(err) { 758 return nil 759 } else if !os.IsNotExist(err) { 760 return err 761 } 762 763 return os.ErrNotExist 764 } 765 766 // stop returns true if no more migrations should be run against the database 767 // because a stop signal was received on the GracefulStop channel. 768 // Calls are cheap and this function is not blocking. 769 func (m *Migrate) stop() bool { 770 if m.isGracefulStop { 771 return true 772 } 773 774 select { 775 case <-m.GracefulStop: 776 m.isGracefulStop = true 777 return true 778 779 default: 780 return false 781 } 782 } 783 784 // newMigration is a helper func that returns a *Migration for the 785 // specified version and targetVersion. 786 func (m *Migrate) newMigration(version uint, targetVersion int) (*Migration, error) { 787 var migr *Migration 788 789 if targetVersion >= int(version) { 790 r, identifier, err := m.sourceDrv.ReadUp(version) 791 if os.IsNotExist(err) { 792 // create "empty" migration 793 migr, err = NewMigration(nil, "", version, targetVersion) 794 if err != nil { 795 return nil, err 796 } 797 798 } else if err != nil { 799 return nil, err 800 801 } else { 802 // create migration from up source 803 migr, err = NewMigration(r, identifier, version, targetVersion) 804 if err != nil { 805 return nil, err 806 } 807 } 808 809 } else { 810 r, identifier, err := m.sourceDrv.ReadDown(version) 811 if os.IsNotExist(err) { 812 // create "empty" migration 813 migr, err = NewMigration(nil, "", version, targetVersion) 814 if err != nil { 815 return nil, err 816 } 817 818 } else if err != nil { 819 return nil, err 820 821 } else { 822 // create migration from down source 823 migr, err = NewMigration(r, identifier, version, targetVersion) 824 if err != nil { 825 return nil, err 826 } 827 } 828 } 829 830 if m.PrefetchMigrations > 0 && migr.Body != nil { 831 m.logVerbosePrintf("Start buffering %v\n", migr.LogString()) 832 } else { 833 m.logVerbosePrintf("Scheduled %v\n", migr.LogString()) 834 } 835 836 return migr, nil 837 } 838 839 // lock is a thread safe helper function to lock the database. 840 // It should be called as late as possible when running migrations. 841 func (m *Migrate) lock() error { 842 m.isLockedMu.Lock() 843 defer m.isLockedMu.Unlock() 844 845 if m.isLocked { 846 return ErrLocked 847 } 848 849 // create done channel, used in the timeout goroutine 850 done := make(chan bool, 1) 851 defer func() { 852 done <- true 853 }() 854 855 // use errchan to signal error back to this context 856 errchan := make(chan error, 2) 857 858 // start timeout goroutine 859 timeout := time.After(m.LockTimeout) 860 go func() { 861 for { 862 select { 863 case <-done: 864 return 865 case <-timeout: 866 errchan <- ErrLockTimeout 867 return 868 } 869 } 870 }() 871 872 // now try to acquire the lock 873 go func() { 874 if err := m.databaseDrv.Lock(); err != nil { 875 errchan <- err 876 } else { 877 errchan <- nil 878 } 879 return 880 }() 881 882 // wait until we either recieve ErrLockTimeout or error from Lock operation 883 err := <-errchan 884 if err == nil { 885 m.isLocked = true 886 } 887 return err 888 } 889 890 // unlock is a thread safe helper function to unlock the database. 891 // It should be called as early as possible when no more migrations are 892 // expected to be executed. 893 func (m *Migrate) unlock() error { 894 m.isLockedMu.Lock() 895 defer m.isLockedMu.Unlock() 896 897 if err := m.databaseDrv.Unlock(); err != nil { 898 // BUG: Can potentially create a deadlock. Add a timeout. 899 return err 900 } 901 902 m.isLocked = false 903 return nil 904 } 905 906 // unlockErr calls unlock and returns a combined error 907 // if a prevErr is not nil. 908 func (m *Migrate) unlockErr(prevErr error) error { 909 if err := m.unlock(); err != nil { 910 return NewMultiError(prevErr, err) 911 } 912 return prevErr 913 } 914 915 // logPrintf writes to m.Log if not nil 916 func (m *Migrate) logPrintf(format string, v ...interface{}) { 917 if m.Log != nil { 918 m.Log.Printf(format, v...) 919 } 920 } 921 922 // logVerbosePrintf writes to m.Log if not nil. Use for verbose logging output. 923 func (m *Migrate) logVerbosePrintf(format string, v ...interface{}) { 924 if m.Log != nil && m.Log.Verbose() { 925 m.Log.Printf(format, v...) 926 } 927 }