dsn.go 3.2 KB

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