2019-08-24 12:24:45 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package setting
import (
"errors"
"fmt"
"net/url"
"os"
"path"
"path/filepath"
"strings"
"time"
2022-06-13 15:55:08 +03:00
"code.gitea.io/gitea/modules/log"
2019-08-24 12:24:45 +03:00
)
var (
2021-12-07 08:44:08 +03: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 12:24:45 +03:00
// EnableSQLite3 use SQLite3, set by build flag
EnableSQLite3 bool
// Database holds the database settings
Database = struct {
Type string
Host string
Name string
User string
Passwd string
2020-01-20 18:45:14 +03:00
Schema string
2019-08-24 12:24:45 +03:00
SSLMode string
Path string
LogSQL bool
Charset string
Timeout int // seconds
UseSQLite3 bool
UseMySQL bool
UseMSSQL bool
UsePostgreSQL bool
DBConnectRetries int
DBConnectBackoff time . Duration
MaxIdleConns int
2019-10-22 00:20:47 +03:00
MaxOpenConns int
2019-08-24 12:24:45 +03:00
ConnMaxLifetime time . Duration
IterateBufferSize int
} {
2020-10-14 16:07:51 +03:00
Timeout : 500 ,
IterateBufferSize : 50 ,
2019-08-24 12:24:45 +03:00
}
)
// InitDBConfig loads the database settings
func InitDBConfig ( ) {
sec := Cfg . Section ( "database" )
Database . Type = sec . Key ( "DB_TYPE" ) . String ( )
2020-08-22 15:56:33 +03:00
defaultCharset := "utf8"
2020-12-03 03:39:48 +03:00
Database . UseMySQL = false
Database . UseSQLite3 = false
Database . UsePostgreSQL = false
Database . UseMSSQL = false
2019-08-24 12:24:45 +03:00
switch Database . Type {
case "sqlite3" :
Database . UseSQLite3 = true
case "mysql" :
Database . UseMySQL = true
2020-08-22 15:56:33 +03:00
defaultCharset = "utf8mb4"
2019-08-24 12:24:45 +03:00
case "postgres" :
Database . UsePostgreSQL = true
case "mssql" :
Database . UseMSSQL = true
}
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 18:45:14 +03:00
Database . Schema = sec . Key ( "SCHEMA" ) . String ( )
2019-08-24 12:24:45 +03:00
Database . SSLMode = sec . Key ( "SSL_MODE" ) . MustString ( "disable" )
2020-08-22 15:56:33 +03:00
Database . Charset = sec . Key ( "CHARSET" ) . In ( defaultCharset , [ ] string { "utf8" , "utf8mb4" } )
2022-06-13 15:55:08 +03:00
if Database . UseMySQL && defaultCharset != "utf8mb4" {
log . Error ( "Deprecated database mysql charset utf8 support, please use utf8mb4 or convert utf8 to utf8mb4." )
}
2019-08-24 12:24:45 +03:00
Database . Path = sec . Key ( "PATH" ) . MustString ( filepath . Join ( AppDataPath , "gitea.db" ) )
Database . Timeout = sec . Key ( "SQLITE_TIMEOUT" ) . MustInt ( 500 )
2019-10-22 00:20:47 +03:00
Database . MaxIdleConns = sec . Key ( "MAX_IDLE_CONNS" ) . MustInt ( 2 )
if Database . UseMySQL {
2021-11-17 13:59:23 +03:00
Database . ConnMaxLifetime = sec . Key ( "CONN_MAX_LIFETIME" ) . MustDuration ( 3 * time . Second )
2019-10-22 00:20:47 +03:00
} else {
2021-11-17 13:59:23 +03:00
Database . ConnMaxLifetime = sec . Key ( "CONN_MAX_LIFETIME" ) . MustDuration ( 0 )
2019-10-22 00:20:47 +03:00
}
Database . MaxOpenConns = sec . Key ( "MAX_OPEN_CONNS" ) . MustInt ( 0 )
2019-08-24 12:24:45 +03: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 )
}
// DBConnStr returns database connection string
func DBConnStr ( ) ( string , error ) {
2022-06-20 13:02:49 +03:00
var connStr string
2022-01-20 20:46:10 +03:00
Param := "?"
2019-08-24 12:24:45 +03:00
if strings . Contains ( Database . Name , Param ) {
Param = "&"
}
switch Database . Type {
case "mysql" :
connType := "tcp"
2020-06-11 19:47:55 +03:00
if len ( Database . Host ) > 0 && Database . Host [ 0 ] == '/' { // looks like a unix socket
2019-08-24 12:24:45 +03: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 {
return "" , fmt . Errorf ( "Failed to create directories: %v" , err )
}
2020-02-27 02:51:37 +03:00
connStr = fmt . Sprintf ( "file:%s?cache=shared&mode=rwc&_busy_timeout=%d&_txlock=immediate" , Database . Path , Database . Timeout )
2019-08-24 12:24:45 +03: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 08:33:31 +03:00
if host == "" {
host = "127.0.0.1"
}
if port == "" {
port = "5432"
}
2019-08-24 12:24:45 +03: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 13:02:49 +03:00
return connStr
2019-08-24 12:24:45 +03:00
}
// ParseMSSQLHostPort splits the host into host and port
func ParseMSSQLHostPort ( info string ) ( string , string ) {
2022-07-13 08:33:31 +03: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 06:59:59 +03:00
host , port := "127.0.0.1" , "0"
2019-08-24 12:24:45 +03: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 08:33:31 +03:00
if host == "" {
host = "127.0.0.1"
}
if port == "" {
port = "0"
}
2019-08-24 12:24:45 +03:00
return host , port
}