dsn.go 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. package dbutil
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/cockroachdb/errors"
  6. "github.com/glebarez/sqlite"
  7. "gorm.io/driver/mysql"
  8. "gorm.io/driver/postgres"
  9. "gorm.io/gorm"
  10. "gogs.io/gogs/internal/conf"
  11. )
  12. // ParsePostgreSQLHostPort parses given input in various forms defined in
  13. // https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
  14. // and returns proper host and port number.
  15. func ParsePostgreSQLHostPort(info string) (host, port string) {
  16. host, port = "127.0.0.1", "5432"
  17. if strings.Contains(info, ":") && !strings.HasSuffix(info, "]") {
  18. idx := strings.LastIndex(info, ":")
  19. host = info[:idx]
  20. port = info[idx+1:]
  21. } else if len(info) > 0 {
  22. host = info
  23. }
  24. return host, port
  25. }
  26. // NewDSN takes given database options and returns parsed DSN.
  27. func NewDSN(opts conf.DatabaseOpts) (dsn string, err error) {
  28. // In case the database name contains "?" with some parameters
  29. concate := "?"
  30. if strings.Contains(opts.Name, concate) {
  31. concate = "&"
  32. }
  33. switch opts.Type {
  34. case "mysql":
  35. if opts.Host[0] == '/' { // Looks like a unix socket
  36. dsn = fmt.Sprintf("%s:%s@unix(%s)/%s%scharset=utf8mb4&parseTime=true",
  37. opts.User, opts.Password, opts.Host, opts.Name, concate)
  38. } else {
  39. dsn = fmt.Sprintf("%s:%s@tcp(%s)/%s%scharset=utf8mb4&parseTime=true",
  40. opts.User, opts.Password, opts.Host, opts.Name, concate)
  41. }
  42. case "postgres":
  43. host, port := ParsePostgreSQLHostPort(opts.Host)
  44. dsn = fmt.Sprintf("user='%s' password='%s' host='%s' port='%s' dbname='%s' sslmode='%s' search_path='%s' application_name='gogs'",
  45. opts.User, opts.Password, host, port, opts.Name, opts.SSLMode, opts.Schema)
  46. case "sqlite3":
  47. dsn = "file:" + opts.Path + "?cache=shared&mode=rwc"
  48. default:
  49. return "", errors.Errorf("unrecognized dialect: %s", opts.Type)
  50. }
  51. return dsn, nil
  52. }
  53. // OpenDB opens a new database connection encapsulated as gorm.DB using given
  54. // database options and GORM config.
  55. func OpenDB(opts conf.DatabaseOpts, cfg *gorm.Config) (*gorm.DB, error) {
  56. dsn, err := NewDSN(opts)
  57. if err != nil {
  58. return nil, errors.Wrap(err, "parse DSN")
  59. }
  60. var dialector gorm.Dialector
  61. switch opts.Type {
  62. case "mysql":
  63. dialector = mysql.Open(dsn)
  64. case "postgres":
  65. dialector = postgres.Open(dsn)
  66. case "sqlite3":
  67. dialector = sqlite.Open(dsn)
  68. default:
  69. panic("unreachable")
  70. }
  71. return gorm.Open(dialector, cfg)
  72. }