/episode21/vendor/github.com/gobuffalo/pop/connection_details.go

https://github.com/arschles/go-in-5-minutes · Go · 142 lines · 113 code · 11 blank · 18 comment · 25 complexity · 0038c4c2d1b6334d7ff5db6ded31ff3c MD5 · raw file

  1. package pop
  2. import (
  3. "fmt"
  4. "net/url"
  5. "regexp"
  6. "strconv"
  7. "strings"
  8. "time"
  9. _mysql "github.com/go-sql-driver/mysql"
  10. "github.com/markbates/going/defaults"
  11. "github.com/pkg/errors"
  12. )
  13. // ConnectionDetails stores the data needed to connect to a datasource
  14. type ConnectionDetails struct {
  15. // Example: "postgres" or "sqlite3" or "mysql"
  16. Dialect string
  17. // The name of your database. Example: "foo_development"
  18. Database string
  19. // The host of your database. Example: "127.0.0.1"
  20. Host string
  21. // The port of your database. Example: 1234
  22. // Will default to the "default" port for each dialect.
  23. Port string
  24. // The username of the database user. Example: "root"
  25. User string
  26. // The password of the database user. Example: "password"
  27. Password string
  28. // Instead of specifying each individual piece of the
  29. // connection you can instead just specify the URL of the
  30. // database. Example: "postgres://postgres:postgres@localhost:5432/pop_test?sslmode=disable"
  31. URL string
  32. // Defaults to 0 "unlimited". See https://golang.org/pkg/database/sql/#DB.SetMaxOpenConns
  33. Pool int
  34. Options map[string]string
  35. }
  36. var dialectX = regexp.MustCompile(`\s+:\/\/`)
  37. // Finalize cleans up the connection details by normalizing names,
  38. // filling in default values, etc...
  39. func (cd *ConnectionDetails) Finalize() error {
  40. if cd.URL != "" {
  41. ul := cd.URL
  42. if cd.Dialect != "" {
  43. if !dialectX.MatchString(ul) {
  44. ul = cd.Dialect + "://" + ul
  45. }
  46. }
  47. cd.Database = cd.URL
  48. if !strings.HasPrefix(cd.Dialect, "sqlite") {
  49. u, err := url.Parse(ul)
  50. if err != nil {
  51. return errors.Wrapf(err, "couldn't parse %s", ul)
  52. }
  53. cd.Dialect = u.Scheme
  54. cd.Database = u.Path
  55. hp := strings.Split(u.Host, ":")
  56. cd.Host = hp[0]
  57. if len(hp) > 1 {
  58. cd.Port = hp[1]
  59. }
  60. if u.User != nil {
  61. cd.User = u.User.Username()
  62. cd.Password, _ = u.User.Password()
  63. }
  64. }
  65. }
  66. switch strings.ToLower(cd.Dialect) {
  67. case "postgres", "postgresql", "pg":
  68. cd.Dialect = "postgres"
  69. cd.Port = defaults.String(cd.Port, "5432")
  70. cd.Database = strings.TrimPrefix(cd.Database, "/")
  71. case "cockroach", "cockroachdb", "crdb":
  72. cd.Dialect = "cockroach"
  73. cd.Port = defaults.String(cd.Port, "26257")
  74. cd.Database = strings.TrimPrefix(cd.Database, "/")
  75. case "mysql":
  76. // parse and verify whether URL is supported by underlying driver or not.
  77. if cd.URL != "" {
  78. cfg, err := _mysql.ParseDSN(strings.TrimPrefix(cd.URL, "mysql://"))
  79. if err != nil {
  80. return errors.Errorf("The URL is not supported by MySQL driver.")
  81. }
  82. cd.User = cfg.User
  83. cd.Password = cfg.Passwd
  84. cd.Database = cfg.DBName
  85. addr := strings.TrimSuffix(strings.TrimPrefix(cfg.Addr, "("), ")")
  86. if cfg.Net == "unix" {
  87. cd.Port = "socket"
  88. cd.Host = addr
  89. } else {
  90. tmp := strings.Split(addr, ":")
  91. switch len(tmp) {
  92. case 1:
  93. cd.Host = tmp[0]
  94. cd.Port = "3306"
  95. case 2:
  96. cd.Host = tmp[0]
  97. cd.Port = tmp[1]
  98. }
  99. }
  100. } else {
  101. cd.Port = defaults.String(cd.Port, "3306")
  102. cd.Database = strings.TrimPrefix(cd.Database, "/")
  103. }
  104. case "sqlite", "sqlite3":
  105. cd.Dialect = "sqlite3"
  106. default:
  107. return errors.Errorf("Unknown dialect %s!", cd.Dialect)
  108. }
  109. return nil
  110. }
  111. // Parse is deprecated! Please use `ConnectionDetails.Finalize()` instead!
  112. func (cd *ConnectionDetails) Parse(port string) error {
  113. fmt.Println("[POP] ConnectionDetails#Parse(port string) has been deprecated!")
  114. return cd.Finalize()
  115. }
  116. // RetrySleep returns the amount of time to wait between two connection retries
  117. func (cd *ConnectionDetails) RetrySleep() time.Duration {
  118. d, err := time.ParseDuration(defaults.String(cd.Options["retry_sleep"], "1ms"))
  119. if err != nil {
  120. return 1 * time.Millisecond
  121. }
  122. return d
  123. }
  124. // RetryLimit returns the maximum number of accepted connection retries
  125. func (cd *ConnectionDetails) RetryLimit() int {
  126. i, err := strconv.Atoi(defaults.String(cd.Options["retry_limit"], "1000"))
  127. if err != nil {
  128. return 100
  129. }
  130. return i
  131. }