You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

database.go 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package setting
  4. import (
  5. "errors"
  6. "fmt"
  7. "net"
  8. "net/url"
  9. "os"
  10. "path/filepath"
  11. "strings"
  12. "time"
  13. )
  14. var (
  15. // SupportedDatabaseTypes includes all XORM supported databases type, sqlite3 maybe added by `database_sqlite3.go`
  16. SupportedDatabaseTypes = []string{"mysql", "postgres", "mssql"}
  17. // DatabaseTypeNames contains the friendly names for all database types
  18. DatabaseTypeNames = map[string]string{"mysql": "MySQL", "postgres": "PostgreSQL", "mssql": "MSSQL", "sqlite3": "SQLite3"}
  19. // EnableSQLite3 use SQLite3, set by build flag
  20. EnableSQLite3 bool
  21. // Database holds the database settings
  22. Database = struct {
  23. Type DatabaseType
  24. Host string
  25. Name string
  26. User string
  27. Passwd string
  28. Schema string
  29. SSLMode string
  30. Path string
  31. LogSQL bool
  32. MysqlCharset string
  33. CharsetCollation string
  34. Timeout int // seconds
  35. SQLiteJournalMode string
  36. DBConnectRetries int
  37. DBConnectBackoff time.Duration
  38. MaxIdleConns int
  39. MaxOpenConns int
  40. ConnMaxLifetime time.Duration
  41. IterateBufferSize int
  42. AutoMigration bool
  43. SlowQueryThreshold time.Duration
  44. }{
  45. Timeout: 500,
  46. IterateBufferSize: 50,
  47. }
  48. )
  49. // LoadDBSetting loads the database settings
  50. func LoadDBSetting() {
  51. loadDBSetting(CfgProvider)
  52. }
  53. func loadDBSetting(rootCfg ConfigProvider) {
  54. sec := rootCfg.Section("database")
  55. Database.Type = DatabaseType(sec.Key("DB_TYPE").String())
  56. Database.Host = sec.Key("HOST").String()
  57. Database.Name = sec.Key("NAME").String()
  58. Database.User = sec.Key("USER").String()
  59. if len(Database.Passwd) == 0 {
  60. Database.Passwd = sec.Key("PASSWD").String()
  61. }
  62. Database.Schema = sec.Key("SCHEMA").String()
  63. Database.SSLMode = sec.Key("SSL_MODE").MustString("disable")
  64. Database.CharsetCollation = sec.Key("CHARSET_COLLATION").String()
  65. Database.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "gitea.db"))
  66. Database.Timeout = sec.Key("SQLITE_TIMEOUT").MustInt(500)
  67. Database.SQLiteJournalMode = sec.Key("SQLITE_JOURNAL_MODE").MustString("")
  68. Database.MaxIdleConns = sec.Key("MAX_IDLE_CONNS").MustInt(2)
  69. if Database.Type.IsMySQL() {
  70. Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFETIME").MustDuration(3 * time.Second)
  71. } else {
  72. Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFETIME").MustDuration(0)
  73. }
  74. Database.MaxOpenConns = sec.Key("MAX_OPEN_CONNS").MustInt(0)
  75. Database.IterateBufferSize = sec.Key("ITERATE_BUFFER_SIZE").MustInt(50)
  76. Database.LogSQL = sec.Key("LOG_SQL").MustBool(false)
  77. Database.DBConnectRetries = sec.Key("DB_RETRIES").MustInt(10)
  78. Database.DBConnectBackoff = sec.Key("DB_RETRY_BACKOFF").MustDuration(3 * time.Second)
  79. Database.AutoMigration = sec.Key("AUTO_MIGRATION").MustBool(true)
  80. Database.SlowQueryThreshold = sec.Key("SLOW_QUERY_THRESHOLD").MustDuration(5 * time.Second)
  81. }
  82. // DBConnStr returns database connection string
  83. func DBConnStr() (string, error) {
  84. var connStr string
  85. paramSep := "?"
  86. if strings.Contains(Database.Name, paramSep) {
  87. paramSep = "&"
  88. }
  89. switch Database.Type {
  90. case "mysql":
  91. connType := "tcp"
  92. if len(Database.Host) > 0 && Database.Host[0] == '/' { // looks like a unix socket
  93. connType = "unix"
  94. }
  95. tls := Database.SSLMode
  96. if tls == "disable" { // allow (Postgres-inspired) default value to work in MySQL
  97. tls = "false"
  98. }
  99. connStr = fmt.Sprintf("%s:%s@%s(%s)/%s%sparseTime=true&tls=%s",
  100. Database.User, Database.Passwd, connType, Database.Host, Database.Name, paramSep, tls)
  101. case "postgres":
  102. connStr = getPostgreSQLConnectionString(Database.Host, Database.User, Database.Passwd, Database.Name, Database.SSLMode)
  103. case "mssql":
  104. host, port := ParseMSSQLHostPort(Database.Host)
  105. connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, Database.Name, Database.User, Database.Passwd)
  106. case "sqlite3":
  107. if !EnableSQLite3 {
  108. return "", errors.New("this Gitea binary was not built with SQLite3 support")
  109. }
  110. if err := os.MkdirAll(filepath.Dir(Database.Path), os.ModePerm); err != nil {
  111. return "", fmt.Errorf("Failed to create directories: %w", err)
  112. }
  113. journalMode := ""
  114. if Database.SQLiteJournalMode != "" {
  115. journalMode = "&_journal_mode=" + Database.SQLiteJournalMode
  116. }
  117. connStr = fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d&_txlock=immediate%s",
  118. Database.Path, Database.Timeout, journalMode)
  119. default:
  120. return "", fmt.Errorf("unknown database type: %s", Database.Type)
  121. }
  122. return connStr, nil
  123. }
  124. // parsePostgreSQLHostPort parses given input in various forms defined in
  125. // https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
  126. // and returns proper host and port number.
  127. func parsePostgreSQLHostPort(info string) (host, port string) {
  128. if h, p, err := net.SplitHostPort(info); err == nil {
  129. host, port = h, p
  130. } else {
  131. // treat the "info" as "host", if it's an IPv6 address, remove the wrapper
  132. host = info
  133. if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
  134. host = host[1 : len(host)-1]
  135. }
  136. }
  137. // set fallback values
  138. if host == "" {
  139. host = "127.0.0.1"
  140. }
  141. if port == "" {
  142. port = "5432"
  143. }
  144. return host, port
  145. }
  146. func getPostgreSQLConnectionString(dbHost, dbUser, dbPasswd, dbName, dbsslMode string) (connStr string) {
  147. dbName, dbParam, _ := strings.Cut(dbName, "?")
  148. host, port := parsePostgreSQLHostPort(dbHost)
  149. connURL := url.URL{
  150. Scheme: "postgres",
  151. User: url.UserPassword(dbUser, dbPasswd),
  152. Host: net.JoinHostPort(host, port),
  153. Path: dbName,
  154. OmitHost: false,
  155. RawQuery: dbParam,
  156. }
  157. query := connURL.Query()
  158. if strings.HasPrefix(host, "/") { // looks like a unix socket
  159. query.Add("host", host)
  160. connURL.Host = ":" + port
  161. }
  162. query.Set("sslmode", dbsslMode)
  163. connURL.RawQuery = query.Encode()
  164. return connURL.String()
  165. }
  166. // ParseMSSQLHostPort splits the host into host and port
  167. func ParseMSSQLHostPort(info string) (string, string) {
  168. // the default port "0" might be related to MSSQL's dynamic port, maybe it should be double-confirmed in the future
  169. host, port := "127.0.0.1", "0"
  170. if strings.Contains(info, ":") {
  171. host = strings.Split(info, ":")[0]
  172. port = strings.Split(info, ":")[1]
  173. } else if strings.Contains(info, ",") {
  174. host = strings.Split(info, ",")[0]
  175. port = strings.TrimSpace(strings.Split(info, ",")[1])
  176. } else if len(info) > 0 {
  177. host = info
  178. }
  179. if host == "" {
  180. host = "127.0.0.1"
  181. }
  182. if port == "" {
  183. port = "0"
  184. }
  185. return host, port
  186. }
  187. type DatabaseType string
  188. func (t DatabaseType) String() string {
  189. return string(t)
  190. }
  191. func (t DatabaseType) IsSQLite3() bool {
  192. return t == "sqlite3"
  193. }
  194. func (t DatabaseType) IsMySQL() bool {
  195. return t == "mysql"
  196. }
  197. func (t DatabaseType) IsMSSQL() bool {
  198. return t == "mssql"
  199. }
  200. func (t DatabaseType) IsPostgreSQL() bool {
  201. return t == "postgres"
  202. }