2019-08-24 11:24:45 +02:00
|
|
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
2022-11-27 19:20:29 +01:00
|
|
|
// SPDX-License-Identifier: MIT
|
2019-08-24 11:24:45 +02:00
|
|
|
|
|
|
|
package setting
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"time"
|
2022-06-13 14:55:08 +02:00
|
|
|
|
|
|
|
"code.gitea.io/gitea/modules/log"
|
2019-08-24 11:24:45 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2021-12-07 06:44:08 +01:00
|
|
|
// SupportedDatabaseTypes includes all XORM supported databases type, sqlite3 maybe added by `database_sqlite3.go`
|
|
|
|
SupportedDatabaseTypes = []string{"mysql", "postgres", "mssql"}
|
|
|
|
// DatabaseTypeNames contains the friendly names for all database types
|
|
|
|
DatabaseTypeNames = map[string]string{"mysql": "MySQL", "postgres": "PostgreSQL", "mssql": "MSSQL", "sqlite3": "SQLite3"}
|
2019-08-24 11:24:45 +02:00
|
|
|
|
|
|
|
// EnableSQLite3 use SQLite3, set by build flag
|
|
|
|
EnableSQLite3 bool
|
|
|
|
|
|
|
|
// Database holds the database settings
|
|
|
|
Database = struct {
|
2023-03-07 11:51:06 +01:00
|
|
|
Type DatabaseType
|
2019-08-24 11:24:45 +02:00
|
|
|
Host string
|
|
|
|
Name string
|
|
|
|
User string
|
|
|
|
Passwd string
|
2020-01-20 16:45:14 +01:00
|
|
|
Schema string
|
2019-08-24 11:24:45 +02:00
|
|
|
SSLMode string
|
|
|
|
Path string
|
|
|
|
LogSQL bool
|
|
|
|
Charset string
|
|
|
|
Timeout int // seconds
|
2022-07-30 21:57:41 +02:00
|
|
|
SQLiteJournalMode string
|
2019-08-24 11:24:45 +02:00
|
|
|
DBConnectRetries int
|
|
|
|
DBConnectBackoff time.Duration
|
|
|
|
MaxIdleConns int
|
2019-10-21 23:20:47 +02:00
|
|
|
MaxOpenConns int
|
2019-08-24 11:24:45 +02:00
|
|
|
ConnMaxLifetime time.Duration
|
|
|
|
IterateBufferSize int
|
2022-12-07 16:58:31 +01:00
|
|
|
AutoMigration bool
|
2019-08-24 11:24:45 +02:00
|
|
|
}{
|
2020-10-14 15:07:51 +02:00
|
|
|
Timeout: 500,
|
|
|
|
IterateBufferSize: 50,
|
2019-08-24 11:24:45 +02:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2023-02-19 17:12:01 +01:00
|
|
|
// LoadDBSetting loads the database settings
|
|
|
|
func LoadDBSetting() {
|
|
|
|
sec := CfgProvider.Section("database")
|
2023-03-07 11:51:06 +01:00
|
|
|
Database.Type = DatabaseType(sec.Key("DB_TYPE").String())
|
2020-08-22 14:56:33 +02:00
|
|
|
defaultCharset := "utf8"
|
2020-12-03 01:39:48 +01:00
|
|
|
|
2023-03-07 11:51:06 +01:00
|
|
|
if Database.Type.IsMySQL() {
|
2020-08-22 14:56:33 +02:00
|
|
|
defaultCharset = "utf8mb4"
|
2019-08-24 11:24:45 +02:00
|
|
|
}
|
2023-03-07 11:51:06 +01:00
|
|
|
|
2019-08-24 11:24:45 +02:00
|
|
|
Database.Host = sec.Key("HOST").String()
|
|
|
|
Database.Name = sec.Key("NAME").String()
|
|
|
|
Database.User = sec.Key("USER").String()
|
|
|
|
if len(Database.Passwd) == 0 {
|
|
|
|
Database.Passwd = sec.Key("PASSWD").String()
|
|
|
|
}
|
2020-01-20 16:45:14 +01:00
|
|
|
Database.Schema = sec.Key("SCHEMA").String()
|
2019-08-24 11:24:45 +02:00
|
|
|
Database.SSLMode = sec.Key("SSL_MODE").MustString("disable")
|
2020-08-22 14:56:33 +02:00
|
|
|
Database.Charset = sec.Key("CHARSET").In(defaultCharset, []string{"utf8", "utf8mb4"})
|
2023-03-07 11:51:06 +01:00
|
|
|
if Database.Type.IsMySQL() && defaultCharset != "utf8mb4" {
|
2022-06-13 14:55:08 +02:00
|
|
|
log.Error("Deprecated database mysql charset utf8 support, please use utf8mb4 or convert utf8 to utf8mb4.")
|
|
|
|
}
|
|
|
|
|
2019-08-24 11:24:45 +02:00
|
|
|
Database.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "gitea.db"))
|
|
|
|
Database.Timeout = sec.Key("SQLITE_TIMEOUT").MustInt(500)
|
2022-07-30 21:57:41 +02:00
|
|
|
Database.SQLiteJournalMode = sec.Key("SQLITE_JOURNAL_MODE").MustString("")
|
|
|
|
|
2019-10-21 23:20:47 +02:00
|
|
|
Database.MaxIdleConns = sec.Key("MAX_IDLE_CONNS").MustInt(2)
|
2023-03-07 11:51:06 +01:00
|
|
|
if Database.Type.IsMySQL() {
|
2021-11-17 11:59:23 +01:00
|
|
|
Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFETIME").MustDuration(3 * time.Second)
|
2019-10-21 23:20:47 +02:00
|
|
|
} else {
|
2021-11-17 11:59:23 +01:00
|
|
|
Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFETIME").MustDuration(0)
|
2019-10-21 23:20:47 +02:00
|
|
|
}
|
|
|
|
Database.MaxOpenConns = sec.Key("MAX_OPEN_CONNS").MustInt(0)
|
2019-08-24 11:24:45 +02:00
|
|
|
|
|
|
|
Database.IterateBufferSize = sec.Key("ITERATE_BUFFER_SIZE").MustInt(50)
|
|
|
|
Database.LogSQL = sec.Key("LOG_SQL").MustBool(true)
|
|
|
|
Database.DBConnectRetries = sec.Key("DB_RETRIES").MustInt(10)
|
|
|
|
Database.DBConnectBackoff = sec.Key("DB_RETRY_BACKOFF").MustDuration(3 * time.Second)
|
2022-12-07 16:58:31 +01:00
|
|
|
Database.AutoMigration = sec.Key("AUTO_MIGRATION").MustBool(true)
|
2019-08-24 11:24:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// DBConnStr returns database connection string
|
|
|
|
func DBConnStr() (string, error) {
|
2022-06-20 12:02:49 +02:00
|
|
|
var connStr string
|
2022-01-20 18:46:10 +01:00
|
|
|
Param := "?"
|
2019-08-24 11:24:45 +02:00
|
|
|
if strings.Contains(Database.Name, Param) {
|
|
|
|
Param = "&"
|
|
|
|
}
|
|
|
|
switch Database.Type {
|
|
|
|
case "mysql":
|
|
|
|
connType := "tcp"
|
2020-06-11 18:47:55 +02:00
|
|
|
if len(Database.Host) > 0 && Database.Host[0] == '/' { // looks like a unix socket
|
2019-08-24 11:24:45 +02:00
|
|
|
connType = "unix"
|
|
|
|
}
|
|
|
|
tls := Database.SSLMode
|
|
|
|
if tls == "disable" { // allow (Postgres-inspired) default value to work in MySQL
|
|
|
|
tls = "false"
|
|
|
|
}
|
|
|
|
connStr = fmt.Sprintf("%s:%s@%s(%s)/%s%scharset=%s&parseTime=true&tls=%s",
|
|
|
|
Database.User, Database.Passwd, connType, Database.Host, Database.Name, Param, Database.Charset, tls)
|
|
|
|
case "postgres":
|
|
|
|
connStr = getPostgreSQLConnectionString(Database.Host, Database.User, Database.Passwd, Database.Name, Param, Database.SSLMode)
|
|
|
|
case "mssql":
|
|
|
|
host, port := ParseMSSQLHostPort(Database.Host)
|
|
|
|
connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, Database.Name, Database.User, Database.Passwd)
|
|
|
|
case "sqlite3":
|
|
|
|
if !EnableSQLite3 {
|
|
|
|
return "", errors.New("this binary version does not build support for SQLite3")
|
|
|
|
}
|
|
|
|
if err := os.MkdirAll(path.Dir(Database.Path), os.ModePerm); err != nil {
|
2022-10-24 21:29:17 +02:00
|
|
|
return "", fmt.Errorf("Failed to create directories: %w", err)
|
2019-08-24 11:24:45 +02:00
|
|
|
}
|
2022-07-30 21:57:41 +02:00
|
|
|
journalMode := ""
|
|
|
|
if Database.SQLiteJournalMode != "" {
|
|
|
|
journalMode = "&_journal_mode=" + Database.SQLiteJournalMode
|
|
|
|
}
|
|
|
|
connStr = fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d&_txlock=immediate%s",
|
|
|
|
Database.Path, Database.Timeout, journalMode)
|
2019-08-24 11:24:45 +02:00
|
|
|
default:
|
|
|
|
return "", fmt.Errorf("Unknown database type: %s", Database.Type)
|
|
|
|
}
|
|
|
|
|
|
|
|
return connStr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// parsePostgreSQLHostPort parses given input in various forms defined in
|
|
|
|
// https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
|
|
|
|
// and returns proper host and port number.
|
|
|
|
func parsePostgreSQLHostPort(info string) (string, string) {
|
|
|
|
host, port := "127.0.0.1", "5432"
|
|
|
|
if strings.Contains(info, ":") && !strings.HasSuffix(info, "]") {
|
|
|
|
idx := strings.LastIndex(info, ":")
|
|
|
|
host = info[:idx]
|
|
|
|
port = info[idx+1:]
|
|
|
|
} else if len(info) > 0 {
|
|
|
|
host = info
|
|
|
|
}
|
2022-07-13 07:33:31 +02:00
|
|
|
if host == "" {
|
|
|
|
host = "127.0.0.1"
|
|
|
|
}
|
|
|
|
if port == "" {
|
|
|
|
port = "5432"
|
|
|
|
}
|
2019-08-24 11:24:45 +02:00
|
|
|
return host, port
|
|
|
|
}
|
|
|
|
|
|
|
|
func getPostgreSQLConnectionString(dbHost, dbUser, dbPasswd, dbName, dbParam, dbsslMode string) (connStr string) {
|
|
|
|
host, port := parsePostgreSQLHostPort(dbHost)
|
|
|
|
if host[0] == '/' { // looks like a unix socket
|
|
|
|
connStr = fmt.Sprintf("postgres://%s:%s@:%s/%s%ssslmode=%s&host=%s",
|
|
|
|
url.PathEscape(dbUser), url.PathEscape(dbPasswd), port, dbName, dbParam, dbsslMode, host)
|
|
|
|
} else {
|
|
|
|
connStr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s%ssslmode=%s",
|
|
|
|
url.PathEscape(dbUser), url.PathEscape(dbPasswd), host, port, dbName, dbParam, dbsslMode)
|
|
|
|
}
|
2022-06-20 12:02:49 +02:00
|
|
|
return connStr
|
2019-08-24 11:24:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// ParseMSSQLHostPort splits the host into host and port
|
|
|
|
func ParseMSSQLHostPort(info string) (string, string) {
|
2022-07-13 07:33:31 +02:00
|
|
|
// the default port "0" might be related to MSSQL's dynamic port, maybe it should be double-confirmed in the future
|
2020-05-29 05:59:59 +02:00
|
|
|
host, port := "127.0.0.1", "0"
|
2019-08-24 11:24:45 +02:00
|
|
|
if strings.Contains(info, ":") {
|
|
|
|
host = strings.Split(info, ":")[0]
|
|
|
|
port = strings.Split(info, ":")[1]
|
|
|
|
} else if strings.Contains(info, ",") {
|
|
|
|
host = strings.Split(info, ",")[0]
|
|
|
|
port = strings.TrimSpace(strings.Split(info, ",")[1])
|
|
|
|
} else if len(info) > 0 {
|
|
|
|
host = info
|
|
|
|
}
|
2022-07-13 07:33:31 +02:00
|
|
|
if host == "" {
|
|
|
|
host = "127.0.0.1"
|
|
|
|
}
|
|
|
|
if port == "" {
|
|
|
|
port = "0"
|
|
|
|
}
|
2019-08-24 11:24:45 +02:00
|
|
|
return host, port
|
|
|
|
}
|
2023-03-07 11:51:06 +01:00
|
|
|
|
|
|
|
type DatabaseType string
|
|
|
|
|
|
|
|
func (t DatabaseType) String() string {
|
|
|
|
return string(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t DatabaseType) IsSQLite3() bool {
|
|
|
|
return t == "sqlite3"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t DatabaseType) IsMySQL() bool {
|
|
|
|
return t == "mysql"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t DatabaseType) IsMSSQL() bool {
|
|
|
|
return t == "mssql"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t DatabaseType) IsPostgreSQL() bool {
|
|
|
|
return t == "postgres"
|
|
|
|
}
|