mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	refactor postgres connection string building (#27723)
This patchset changes the connection string builder to use net.URL and the host/port parser to use the stdlib function for splitting host from port. It also adds a footnote about a potentially required portnumber for postgres UNIX sockets. Fixes: #24552
This commit is contained in:
		@@ -424,7 +424,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
 | 
				
			|||||||
## Database (`database`)
 | 
					## Database (`database`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `DB_TYPE`: **mysql**: The database type in use \[mysql, postgres, mssql, sqlite3\].
 | 
					- `DB_TYPE`: **mysql**: The database type in use \[mysql, postgres, mssql, sqlite3\].
 | 
				
			||||||
- `HOST`: **127.0.0.1:3306**: Database host address and port or absolute path for unix socket \[mysql, postgres\] (ex: /var/run/mysqld/mysqld.sock).
 | 
					- `HOST`: **127.0.0.1:3306**: Database host address and port or absolute path for unix socket \[mysql, postgres[^1]\] (ex: /var/run/mysqld/mysqld.sock).
 | 
				
			||||||
- `NAME`: **gitea**: Database name.
 | 
					- `NAME`: **gitea**: Database name.
 | 
				
			||||||
- `USER`: **root**: Database username.
 | 
					- `USER`: **root**: Database username.
 | 
				
			||||||
- `PASSWD`: **_empty_**: Database user password. Use \`your password\` or """your password""" for quoting if you use special characters in the password.
 | 
					- `PASSWD`: **_empty_**: Database user password. Use \`your password\` or """your password""" for quoting if you use special characters in the password.
 | 
				
			||||||
@@ -455,6 +455,8 @@ The following configuration set `Content-Type: application/vnd.android.package-a
 | 
				
			|||||||
- `CONN_MAX_LIFETIME` **0 or 3s**: Sets the maximum amount of time a DB connection may be reused - default is 0, meaning there is no limit (except on MySQL where it is 3s - see #6804 & #7071).
 | 
					- `CONN_MAX_LIFETIME` **0 or 3s**: Sets the maximum amount of time a DB connection may be reused - default is 0, meaning there is no limit (except on MySQL where it is 3s - see #6804 & #7071).
 | 
				
			||||||
- `AUTO_MIGRATION` **true**: Whether execute database models migrations automatically.
 | 
					- `AUTO_MIGRATION` **true**: Whether execute database models migrations automatically.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[^1]: It may be necessary to specify a hostport even when listening on a unix socket, as the port is part of the socket name. see [#24552](https://github.com/go-gitea/gitea/issues/24552#issuecomment-1681649367) for additional details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Please see #8540 & #8273 for further discussion of the appropriate values for `MAX_OPEN_CONNS`, `MAX_IDLE_CONNS` & `CONN_MAX_LIFETIME` and their
 | 
					Please see #8540 & #8273 for further discussion of the appropriate values for `MAX_OPEN_CONNS`, `MAX_IDLE_CONNS` & `CONN_MAX_LIFETIME` and their
 | 
				
			||||||
relation to port exhaustion.
 | 
					relation to port exhaustion.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ package setting
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
@@ -135,15 +136,18 @@ func DBConnStr() (string, error) {
 | 
				
			|||||||
// parsePostgreSQLHostPort parses given input in various forms defined in
 | 
					// parsePostgreSQLHostPort parses given input in various forms defined in
 | 
				
			||||||
// https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
 | 
					// https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
 | 
				
			||||||
// and returns proper host and port number.
 | 
					// and returns proper host and port number.
 | 
				
			||||||
func parsePostgreSQLHostPort(info string) (string, string) {
 | 
					func parsePostgreSQLHostPort(info string) (host, port string) {
 | 
				
			||||||
	host, port := "127.0.0.1", "5432"
 | 
						if h, p, err := net.SplitHostPort(info); err == nil {
 | 
				
			||||||
	if strings.Contains(info, ":") && !strings.HasSuffix(info, "]") {
 | 
							host, port = h, p
 | 
				
			||||||
		idx := strings.LastIndex(info, ":")
 | 
						} else {
 | 
				
			||||||
		host = info[:idx]
 | 
							// treat the "info" as "host", if it's an IPv6 address, remove the wrapper
 | 
				
			||||||
		port = info[idx+1:]
 | 
					 | 
				
			||||||
	} else if len(info) > 0 {
 | 
					 | 
				
			||||||
		host = info
 | 
							host = info
 | 
				
			||||||
 | 
							if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
 | 
				
			||||||
 | 
								host = host[1 : len(host)-1]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// set fallback values
 | 
				
			||||||
	if host == "" {
 | 
						if host == "" {
 | 
				
			||||||
		host = "127.0.0.1"
 | 
							host = "127.0.0.1"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -155,14 +159,22 @@ func parsePostgreSQLHostPort(info string) (string, string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func getPostgreSQLConnectionString(dbHost, dbUser, dbPasswd, dbName, dbParam, dbsslMode string) (connStr string) {
 | 
					func getPostgreSQLConnectionString(dbHost, dbUser, dbPasswd, dbName, dbParam, dbsslMode string) (connStr string) {
 | 
				
			||||||
	host, port := parsePostgreSQLHostPort(dbHost)
 | 
						host, port := parsePostgreSQLHostPort(dbHost)
 | 
				
			||||||
	if host[0] == '/' { // looks like a unix socket
 | 
						connURL := url.URL{
 | 
				
			||||||
		connStr = fmt.Sprintf("postgres://%s:%s@:%s/%s%ssslmode=%s&host=%s",
 | 
							Scheme:   "postgres",
 | 
				
			||||||
			url.PathEscape(dbUser), url.PathEscape(dbPasswd), port, dbName, dbParam, dbsslMode, host)
 | 
							User:     url.UserPassword(dbUser, dbPasswd),
 | 
				
			||||||
	} else {
 | 
							Host:     net.JoinHostPort(host, port),
 | 
				
			||||||
		connStr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s%ssslmode=%s",
 | 
							Path:     dbName,
 | 
				
			||||||
			url.PathEscape(dbUser), url.PathEscape(dbPasswd), host, port, dbName, dbParam, dbsslMode)
 | 
							OmitHost: false,
 | 
				
			||||||
 | 
							RawQuery: dbParam,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return connStr
 | 
						query := connURL.Query()
 | 
				
			||||||
 | 
						if dbHost[0] == '/' { // looks like a unix socket
 | 
				
			||||||
 | 
							query.Add("host", dbHost)
 | 
				
			||||||
 | 
							connURL.Host = ":" + port
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query.Set("sslmode", dbsslMode)
 | 
				
			||||||
 | 
						connURL.RawQuery = query.Encode()
 | 
				
			||||||
 | 
						return connURL.String()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ParseMSSQLHostPort splits the host into host and port
 | 
					// ParseMSSQLHostPort splits the host into host and port
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,46 +10,49 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Test_parsePostgreSQLHostPort(t *testing.T) {
 | 
					func Test_parsePostgreSQLHostPort(t *testing.T) {
 | 
				
			||||||
	tests := []struct {
 | 
						tests := map[string]struct {
 | 
				
			||||||
		HostPort string
 | 
							HostPort string
 | 
				
			||||||
		Host     string
 | 
							Host     string
 | 
				
			||||||
		Port     string
 | 
							Port     string
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							"host-port": {
 | 
				
			||||||
			HostPort: "127.0.0.1:1234",
 | 
								HostPort: "127.0.0.1:1234",
 | 
				
			||||||
			Host:     "127.0.0.1",
 | 
								Host:     "127.0.0.1",
 | 
				
			||||||
			Port:     "1234",
 | 
								Port:     "1234",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							"no-port": {
 | 
				
			||||||
			HostPort: "127.0.0.1",
 | 
								HostPort: "127.0.0.1",
 | 
				
			||||||
			Host:     "127.0.0.1",
 | 
								Host:     "127.0.0.1",
 | 
				
			||||||
			Port:     "5432",
 | 
								Port:     "5432",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							"ipv6-port": {
 | 
				
			||||||
			HostPort: "[::1]:1234",
 | 
								HostPort: "[::1]:1234",
 | 
				
			||||||
			Host:     "[::1]",
 | 
								Host:     "::1",
 | 
				
			||||||
			Port:     "1234",
 | 
								Port:     "1234",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							"ipv6-no-port": {
 | 
				
			||||||
			HostPort: "[::1]",
 | 
								HostPort: "[::1]",
 | 
				
			||||||
			Host:     "[::1]",
 | 
								Host:     "::1",
 | 
				
			||||||
			Port:     "5432",
 | 
								Port:     "5432",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							"unix-socket": {
 | 
				
			||||||
			HostPort: "/tmp/pg.sock:1234",
 | 
								HostPort: "/tmp/pg.sock:1234",
 | 
				
			||||||
			Host:     "/tmp/pg.sock",
 | 
								Host:     "/tmp/pg.sock",
 | 
				
			||||||
			Port:     "1234",
 | 
								Port:     "1234",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							"unix-socket-no-port": {
 | 
				
			||||||
			HostPort: "/tmp/pg.sock",
 | 
								HostPort: "/tmp/pg.sock",
 | 
				
			||||||
			Host:     "/tmp/pg.sock",
 | 
								Host:     "/tmp/pg.sock",
 | 
				
			||||||
			Port:     "5432",
 | 
								Port:     "5432",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, test := range tests {
 | 
						for k, test := range tests {
 | 
				
			||||||
		host, port := parsePostgreSQLHostPort(test.HostPort)
 | 
							t.Run(k, func(t *testing.T) {
 | 
				
			||||||
		assert.Equal(t, test.Host, host)
 | 
								t.Log(test.HostPort)
 | 
				
			||||||
		assert.Equal(t, test.Port, port)
 | 
								host, port := parsePostgreSQLHostPort(test.HostPort)
 | 
				
			||||||
 | 
								assert.Equal(t, test.Host, host)
 | 
				
			||||||
 | 
								assert.Equal(t, test.Port, port)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -72,7 +75,7 @@ func Test_getPostgreSQLConnectionString(t *testing.T) {
 | 
				
			|||||||
			Name:    "gitea",
 | 
								Name:    "gitea",
 | 
				
			||||||
			Param:   "",
 | 
								Param:   "",
 | 
				
			||||||
			SSLMode: "false",
 | 
								SSLMode: "false",
 | 
				
			||||||
			Output:  "postgres://testuser:space%20space%20%21%23$%25%5E%5E%25%5E%60%60%60-=%3F=@:5432/giteasslmode=false&host=/tmp/pg.sock",
 | 
								Output:  "postgres://testuser:space%20space%20%21%23$%25%5E%5E%25%5E%60%60%60-=%3F=@:5432/gitea?host=%2Ftmp%2Fpg.sock&sslmode=false",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Host:    "localhost",
 | 
								Host:    "localhost",
 | 
				
			||||||
@@ -82,7 +85,7 @@ func Test_getPostgreSQLConnectionString(t *testing.T) {
 | 
				
			|||||||
			Name:    "gitea",
 | 
								Name:    "gitea",
 | 
				
			||||||
			Param:   "",
 | 
								Param:   "",
 | 
				
			||||||
			SSLMode: "true",
 | 
								SSLMode: "true",
 | 
				
			||||||
			Output:  "postgres://pgsqlusername:I%20love%20Gitea%21@localhost:5432/giteasslmode=true",
 | 
								Output:  "postgres://pgsqlusername:I%20love%20Gitea%21@localhost:5432/gitea?sslmode=true",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user