aboutsummaryrefslogtreecommitdiff
path: root/internal/dbutil
diff options
context:
space:
mode:
authorJoe Chen <jc@unknwon.io>2022-06-12 14:15:01 +0800
committerGitHub <noreply@github.com>2022-06-12 14:15:01 +0800
commitb772603d78cb10f0501f3d08b01553bb33914b6e (patch)
tree5f160df17f1bbd357370b3aeeb5289b0a5c3c08d /internal/dbutil
parent2e19f5a3c8193776685a5e9fea9ca8663f14dd8d (diff)
migrations: add tests and remove XORM (#7050)
Diffstat (limited to 'internal/dbutil')
-rw-r--r--internal/dbutil/dsn.go116
-rw-r--r--internal/dbutil/dsn_test.go149
-rw-r--r--internal/dbutil/logger.go4
3 files changed, 269 insertions, 0 deletions
diff --git a/internal/dbutil/dsn.go b/internal/dbutil/dsn.go
new file mode 100644
index 00000000..30279988
--- /dev/null
+++ b/internal/dbutil/dsn.go
@@ -0,0 +1,116 @@
+// Copyright 2020 The Gogs 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 dbutil
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/pkg/errors"
+ "gorm.io/driver/mysql"
+ "gorm.io/driver/postgres"
+ "gorm.io/driver/sqlite"
+ "gorm.io/driver/sqlserver"
+ "gorm.io/gorm"
+
+ "gogs.io/gogs/internal/conf"
+)
+
+// 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) (host, port 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
+ }
+ return host, port
+}
+
+// ParseMSSQLHostPort parses given input in various forms for MSSQL and returns
+// proper host and port number.
+func ParseMSSQLHostPort(info string) (host, port string) {
+ host, port = "127.0.0.1", "1433"
+ 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
+ }
+ return host, port
+}
+
+// NewDSN takes given database options and returns parsed DSN.
+func NewDSN(opts conf.DatabaseOpts) (dsn string, err error) {
+ // In case the database name contains "?" with some parameters
+ concate := "?"
+ if strings.Contains(opts.Name, concate) {
+ concate = "&"
+ }
+
+ switch opts.Type {
+ case "mysql":
+ if opts.Host[0] == '/' { // Looks like a unix socket
+ dsn = fmt.Sprintf("%s:%s@unix(%s)/%s%scharset=utf8mb4&parseTime=true",
+ opts.User, opts.Password, opts.Host, opts.Name, concate)
+ } else {
+ dsn = fmt.Sprintf("%s:%s@tcp(%s)/%s%scharset=utf8mb4&parseTime=true",
+ opts.User, opts.Password, opts.Host, opts.Name, concate)
+ }
+
+ case "postgres":
+ host, port := ParsePostgreSQLHostPort(opts.Host)
+ dsn = fmt.Sprintf("user='%s' password='%s' host='%s' port='%s' dbname='%s' sslmode='%s' search_path='%s' application_name='gogs'",
+ opts.User, opts.Password, host, port, opts.Name, opts.SSLMode, opts.Schema)
+
+ case "mssql":
+ host, port := ParseMSSQLHostPort(opts.Host)
+ dsn = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;",
+ host, port, opts.Name, opts.User, opts.Password)
+
+ case "sqlite3", "sqlite":
+ dsn = "file:" + opts.Path + "?cache=shared&mode=rwc"
+
+ default:
+ return "", errors.Errorf("unrecognized dialect: %s", opts.Type)
+ }
+
+ return dsn, nil
+}
+
+// OpenDB opens a new database connection encapsulated as gorm.DB using given
+// database options and GORM config.
+func OpenDB(opts conf.DatabaseOpts, cfg *gorm.Config) (*gorm.DB, error) {
+ dsn, err := NewDSN(opts)
+ if err != nil {
+ return nil, errors.Wrap(err, "parse DSN")
+ }
+
+ var dialector gorm.Dialector
+ switch opts.Type {
+ case "mysql":
+ dialector = mysql.Open(dsn)
+ case "postgres":
+ dialector = postgres.Open(dsn)
+ case "mssql":
+ dialector = sqlserver.Open(dsn)
+ case "sqlite3":
+ dialector = sqlite.Open(dsn)
+ case "sqlite":
+ dialector = sqlite.Open(dsn)
+ dialector.(*sqlite.Dialector).DriverName = "sqlite"
+ default:
+ panic("unreachable")
+ }
+
+ return gorm.Open(dialector, cfg)
+}
diff --git a/internal/dbutil/dsn_test.go b/internal/dbutil/dsn_test.go
new file mode 100644
index 00000000..0dd92901
--- /dev/null
+++ b/internal/dbutil/dsn_test.go
@@ -0,0 +1,149 @@
+// Copyright 2020 The Gogs 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 dbutil
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "gogs.io/gogs/internal/conf"
+)
+
+func TestParsePostgreSQLHostPort(t *testing.T) {
+ tests := []struct {
+ info string
+ expHost string
+ expPort string
+ }{
+ {info: "127.0.0.1:1234", expHost: "127.0.0.1", expPort: "1234"},
+ {info: "127.0.0.1", expHost: "127.0.0.1", expPort: "5432"},
+ {info: "[::1]:1234", expHost: "[::1]", expPort: "1234"},
+ {info: "[::1]", expHost: "[::1]", expPort: "5432"},
+ {info: "/tmp/pg.sock:1234", expHost: "/tmp/pg.sock", expPort: "1234"},
+ {info: "/tmp/pg.sock", expHost: "/tmp/pg.sock", expPort: "5432"},
+ }
+ for _, test := range tests {
+ t.Run("", func(t *testing.T) {
+ host, port := ParsePostgreSQLHostPort(test.info)
+ assert.Equal(t, test.expHost, host)
+ assert.Equal(t, test.expPort, port)
+ })
+ }
+}
+
+func TestParseMSSQLHostPort(t *testing.T) {
+ tests := []struct {
+ info string
+ expHost string
+ expPort string
+ }{
+ {info: "127.0.0.1:1234", expHost: "127.0.0.1", expPort: "1234"},
+ {info: "127.0.0.1,1234", expHost: "127.0.0.1", expPort: "1234"},
+ {info: "127.0.0.1", expHost: "127.0.0.1", expPort: "1433"},
+ }
+ for _, test := range tests {
+ t.Run("", func(t *testing.T) {
+ host, port := ParseMSSQLHostPort(test.info)
+ assert.Equal(t, test.expHost, host)
+ assert.Equal(t, test.expPort, port)
+ })
+ }
+}
+
+func TestNewDSN(t *testing.T) {
+ t.Run("bad dialect", func(t *testing.T) {
+ _, err := NewDSN(conf.DatabaseOpts{
+ Type: "bad_dialect",
+ })
+ assert.Equal(t, "unrecognized dialect: bad_dialect", fmt.Sprintf("%v", err))
+ })
+
+ tests := []struct {
+ name string
+ opts conf.DatabaseOpts
+ wantDSN string
+ }{
+ {
+ name: "mysql: unix",
+ opts: conf.DatabaseOpts{
+ Type: "mysql",
+ Host: "/tmp/mysql.sock",
+ Name: "gogs",
+ User: "gogs",
+ Password: "pa$$word",
+ },
+ wantDSN: "gogs:pa$$word@unix(/tmp/mysql.sock)/gogs?charset=utf8mb4&parseTime=true",
+ },
+ {
+ name: "mysql: tcp",
+ opts: conf.DatabaseOpts{
+ Type: "mysql",
+ Host: "localhost:3306",
+ Name: "gogs",
+ User: "gogs",
+ Password: "pa$$word",
+ },
+ wantDSN: "gogs:pa$$word@tcp(localhost:3306)/gogs?charset=utf8mb4&parseTime=true",
+ },
+
+ {
+ name: "postgres: unix",
+ opts: conf.DatabaseOpts{
+ Type: "postgres",
+ Host: "/tmp/pg.sock",
+ Name: "gogs",
+ Schema: "test",
+ User: "gogs@local",
+ Password: "pa$$word",
+ SSLMode: "disable",
+ },
+ wantDSN: "user='gogs@local' password='pa$$word' host='/tmp/pg.sock' port='5432' dbname='gogs' sslmode='disable' search_path='test' application_name='gogs'",
+ },
+ {
+ name: "postgres: tcp",
+ opts: conf.DatabaseOpts{
+ Type: "postgres",
+ Host: "127.0.0.1",
+ Name: "gogs",
+ Schema: "test",
+ User: "gogs@local",
+ Password: "pa$$word",
+ SSLMode: "disable",
+ },
+ wantDSN: "user='gogs@local' password='pa$$word' host='127.0.0.1' port='5432' dbname='gogs' sslmode='disable' search_path='test' application_name='gogs'",
+ },
+
+ {
+ name: "mssql",
+ opts: conf.DatabaseOpts{
+ Type: "mssql",
+ Host: "127.0.0.1",
+ Name: "gogs",
+ User: "gogs@local",
+ Password: "pa$$word",
+ },
+ wantDSN: "server=127.0.0.1; port=1433; database=gogs; user id=gogs@local; password=pa$$word;",
+ },
+
+ {
+ name: "sqlite3",
+ opts: conf.DatabaseOpts{
+ Type: "sqlite3",
+ Path: "/tmp/gogs.db",
+ },
+ wantDSN: "file:/tmp/gogs.db?cache=shared&mode=rwc",
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ dsn, err := NewDSN(test.opts)
+ require.NoError(t, err)
+ assert.Equal(t, test.wantDSN, dsn)
+ })
+ }
+}
diff --git a/internal/dbutil/logger.go b/internal/dbutil/logger.go
index 66426ae7..c189949e 100644
--- a/internal/dbutil/logger.go
+++ b/internal/dbutil/logger.go
@@ -1,3 +1,7 @@
+// Copyright 2020 The Gogs 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 dbutil
import (