2014-02-12 12:49:46 -05:00
|
|
|
// Copyright 2014 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.
|
|
|
|
|
|
2019-10-24 01:51:46 -07:00
|
|
|
package db
|
2014-02-13 23:23:23 +08:00
|
|
|
|
2014-02-18 17:48:02 -05:00
|
|
|
import (
|
2022-06-10 19:54:36 +08:00
|
|
|
"context"
|
2014-10-19 01:35:24 -04:00
|
|
|
"database/sql"
|
2014-02-18 17:48:02 -05:00
|
|
|
"fmt"
|
|
|
|
|
"os"
|
2014-03-21 01:09:22 -04:00
|
|
|
"path"
|
2020-09-29 22:26:07 +08:00
|
|
|
"path/filepath"
|
2014-04-14 14:49:50 +08:00
|
|
|
"strings"
|
2019-02-06 18:46:15 -05:00
|
|
|
"time"
|
2014-02-18 17:48:02 -05:00
|
|
|
|
2020-09-29 22:26:07 +08:00
|
|
|
"github.com/pkg/errors"
|
2020-09-06 10:11:08 +08:00
|
|
|
"gorm.io/gorm"
|
2020-09-29 22:26:07 +08:00
|
|
|
"gorm.io/gorm/logger"
|
2020-02-20 02:25:02 +08:00
|
|
|
log "unknwon.dev/clog/v2"
|
2019-10-24 01:51:46 -07:00
|
|
|
"xorm.io/core"
|
|
|
|
|
"xorm.io/xorm"
|
2014-02-18 17:48:02 -05:00
|
|
|
|
2020-02-22 09:05:26 +08:00
|
|
|
"gogs.io/gogs/internal/conf"
|
2019-10-24 01:51:46 -07:00
|
|
|
"gogs.io/gogs/internal/db/migrations"
|
2020-09-29 22:26:07 +08:00
|
|
|
"gogs.io/gogs/internal/dbutil"
|
2014-02-18 17:48:02 -05:00
|
|
|
)
|
2014-02-13 23:23:23 +08:00
|
|
|
|
2017-04-06 00:14:30 -04:00
|
|
|
// Engine represents a XORM engine or session.
|
2014-10-19 01:35:24 -04:00
|
|
|
type Engine interface {
|
|
|
|
|
Delete(interface{}) (int64, error)
|
2019-10-23 23:03:17 -07:00
|
|
|
Exec(...interface{}) (sql.Result, error)
|
2015-02-13 00:58:46 -05:00
|
|
|
Find(interface{}, ...interface{}) error
|
2015-02-10 23:44:16 -05:00
|
|
|
Get(interface{}) (bool, error)
|
2018-08-16 20:26:09 +08:00
|
|
|
ID(interface{}) *xorm.Session
|
2016-08-15 18:40:32 -07:00
|
|
|
In(string, ...interface{}) *xorm.Session
|
2014-10-19 01:35:24 -04:00
|
|
|
Insert(...interface{}) (int64, error)
|
2015-02-13 00:58:46 -05:00
|
|
|
InsertOne(interface{}) (int64, error)
|
2016-07-26 17:26:48 +08:00
|
|
|
Iterate(interface{}, xorm.IterFunc) error
|
2015-02-13 00:58:46 -05:00
|
|
|
Sql(string, ...interface{}) *xorm.Session
|
2016-12-20 23:23:57 -05:00
|
|
|
Table(interface{}) *xorm.Session
|
2016-09-23 07:38:12 +08:00
|
|
|
Where(interface{}, ...interface{}) *xorm.Session
|
2014-10-19 01:35:24 -04:00
|
|
|
}
|
|
|
|
|
|
2014-03-21 01:48:10 -04:00
|
|
|
var (
|
2020-04-11 01:25:19 +08:00
|
|
|
x *xorm.Engine
|
|
|
|
|
legacyTables []interface{}
|
|
|
|
|
HasEngine bool
|
2014-03-21 01:48:10 -04:00
|
|
|
)
|
|
|
|
|
|
2014-04-05 22:46:32 +08:00
|
|
|
func init() {
|
2020-04-11 01:25:19 +08:00
|
|
|
legacyTables = append(legacyTables,
|
|
|
|
|
new(User), new(PublicKey), new(TwoFactor), new(TwoFactorRecoveryCode),
|
2020-10-06 15:43:28 +08:00
|
|
|
new(Repository), new(DeployKey), new(Collaboration), new(Upload),
|
2015-09-01 19:07:02 -04:00
|
|
|
new(Watch), new(Star), new(Follow), new(Action),
|
2015-09-02 05:09:12 -04:00
|
|
|
new(Issue), new(PullRequest), new(Comment), new(Attachment), new(IssueUser),
|
2015-08-10 14:42:50 +08:00
|
|
|
new(Label), new(IssueLabel), new(Milestone),
|
2020-04-11 20:18:05 +08:00
|
|
|
new(Mirror), new(Release), new(Webhook), new(HookTask),
|
2017-02-23 18:25:12 -05:00
|
|
|
new(ProtectBranch), new(ProtectBranchWhitelist),
|
2015-02-23 02:15:53 -05:00
|
|
|
new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
|
2015-02-11 21:58:37 -05:00
|
|
|
new(Notice), new(EmailAddress))
|
2015-08-27 23:06:14 +08:00
|
|
|
|
2015-11-07 00:39:45 -05:00
|
|
|
gonicNames := []string{"SSL"}
|
2015-08-27 23:06:14 +08:00
|
|
|
for _, name := range gonicNames {
|
|
|
|
|
core.LintGonicMapper[name] = true
|
|
|
|
|
}
|
2014-04-05 22:46:32 +08:00
|
|
|
}
|
|
|
|
|
|
2014-09-04 17:19:26 +02:00
|
|
|
func getEngine() (*xorm.Engine, error) {
|
2020-02-22 18:58:16 +08:00
|
|
|
Param := "?"
|
|
|
|
|
if strings.Contains(conf.Database.Name, Param) {
|
2016-07-24 14:32:46 +08:00
|
|
|
Param = "&"
|
2016-07-02 10:39:39 -04:00
|
|
|
}
|
2020-02-22 18:58:16 +08:00
|
|
|
|
2020-09-06 10:11:08 +08:00
|
|
|
driver := conf.Database.Type
|
2020-02-22 18:58:16 +08:00
|
|
|
connStr := ""
|
|
|
|
|
switch conf.Database.Type {
|
2014-03-30 10:47:08 -04:00
|
|
|
case "mysql":
|
2020-02-22 18:58:16 +08:00
|
|
|
conf.UseMySQL = true
|
|
|
|
|
if conf.Database.Host[0] == '/' { // looks like a unix socket
|
2017-11-23 02:42:30 +08:00
|
|
|
connStr = fmt.Sprintf("%s:%s@unix(%s)/%s%scharset=utf8mb4&parseTime=true",
|
2020-02-22 18:58:16 +08:00
|
|
|
conf.Database.User, conf.Database.Password, conf.Database.Host, conf.Database.Name, Param)
|
2015-03-14 02:21:47 +08:00
|
|
|
} else {
|
2017-11-23 02:42:30 +08:00
|
|
|
connStr = fmt.Sprintf("%s:%s@tcp(%s)/%s%scharset=utf8mb4&parseTime=true",
|
2020-02-22 18:58:16 +08:00
|
|
|
conf.Database.User, conf.Database.Password, conf.Database.Host, conf.Database.Name, Param)
|
2015-03-14 02:21:47 +08:00
|
|
|
}
|
2022-03-06 17:55:17 +08:00
|
|
|
engineParams := map[string]string{"rowFormat": "DYNAMIC"}
|
2020-02-22 18:58:16 +08:00
|
|
|
return xorm.NewEngineWithParams(conf.Database.Type, connStr, engineParams)
|
|
|
|
|
|
2014-03-30 10:47:08 -04:00
|
|
|
case "postgres":
|
2020-02-22 18:58:16 +08:00
|
|
|
conf.UsePostgreSQL = true
|
|
|
|
|
host, port := parsePostgreSQLHostPort(conf.Database.Host)
|
2022-01-05 22:02:33 +08:00
|
|
|
connStr = fmt.Sprintf("user='%s' password='%s' host='%s' port='%s' dbname='%s' sslmode='%s' search_path='%s'",
|
|
|
|
|
conf.Database.User, conf.Database.Password, host, port, conf.Database.Name, conf.Database.SSLMode, conf.Database.Schema)
|
2020-09-06 10:11:08 +08:00
|
|
|
driver = "pgx"
|
2020-02-22 18:58:16 +08:00
|
|
|
|
2017-02-14 02:50:00 +01:00
|
|
|
case "mssql":
|
2020-02-22 18:58:16 +08:00
|
|
|
conf.UseMSSQL = true
|
|
|
|
|
host, port := parseMSSQLHostPort(conf.Database.Host)
|
2020-04-04 21:14:15 +08:00
|
|
|
connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, conf.Database.Name, conf.Database.User, conf.Database.Password)
|
2020-02-22 18:58:16 +08:00
|
|
|
|
2014-04-12 11:48:12 -07:00
|
|
|
case "sqlite3":
|
2020-02-22 18:58:16 +08:00
|
|
|
if err := os.MkdirAll(path.Dir(conf.Database.Path), os.ModePerm); err != nil {
|
2019-02-06 18:50:02 -05:00
|
|
|
return nil, fmt.Errorf("create directories: %v", err)
|
2015-08-24 21:01:23 +08:00
|
|
|
}
|
2020-02-22 18:58:16 +08:00
|
|
|
conf.UseSQLite3 = true
|
|
|
|
|
connStr = "file:" + conf.Database.Path + "?cache=shared&mode=rwc"
|
|
|
|
|
|
2014-03-30 10:47:08 -04:00
|
|
|
default:
|
2020-02-22 18:58:16 +08:00
|
|
|
return nil, fmt.Errorf("unknown database type: %s", conf.Database.Type)
|
2014-03-30 10:47:08 -04:00
|
|
|
}
|
2020-09-06 10:11:08 +08:00
|
|
|
return xorm.NewEngine(driver, connStr)
|
2014-09-04 17:19:26 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-21 13:39:32 +08:00
|
|
|
func NewTestEngine() error {
|
|
|
|
|
x, err := getEngine()
|
2014-03-30 10:47:08 -04:00
|
|
|
if err != nil {
|
2019-02-06 18:50:02 -05:00
|
|
|
return fmt.Errorf("connect to database: %v", err)
|
2014-03-30 10:47:08 -04:00
|
|
|
}
|
2014-09-04 17:19:26 +02:00
|
|
|
|
2022-01-05 22:02:33 +08:00
|
|
|
if conf.UsePostgreSQL {
|
|
|
|
|
x.SetSchema(conf.Database.Schema)
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-23 09:54:16 +02:00
|
|
|
x.SetMapper(core.GonicMapper{})
|
2020-04-11 01:25:19 +08:00
|
|
|
return x.StoreEngine("InnoDB").Sync2(legacyTables...)
|
2014-03-30 10:47:08 -04:00
|
|
|
}
|
|
|
|
|
|
2020-05-04 16:25:57 +08:00
|
|
|
func SetEngine() (*gorm.DB, error) {
|
|
|
|
|
var err error
|
2014-09-04 17:19:26 +02:00
|
|
|
x, err = getEngine()
|
2014-02-18 17:48:02 -05:00
|
|
|
if err != nil {
|
2020-05-04 16:25:57 +08:00
|
|
|
return nil, fmt.Errorf("connect to database: %v", err)
|
2014-02-18 17:48:02 -05:00
|
|
|
}
|
|
|
|
|
|
2022-01-05 22:02:33 +08:00
|
|
|
if conf.UsePostgreSQL {
|
|
|
|
|
x.SetSchema(conf.Database.Schema)
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-23 09:54:16 +02:00
|
|
|
x.SetMapper(core.GonicMapper{})
|
|
|
|
|
|
2020-09-29 22:26:07 +08:00
|
|
|
var logPath string
|
|
|
|
|
if conf.HookMode {
|
|
|
|
|
logPath = filepath.Join(conf.Log.RootPath, "hooks", "xorm.log")
|
|
|
|
|
} else {
|
|
|
|
|
logPath = filepath.Join(conf.Log.RootPath, "xorm.log")
|
|
|
|
|
}
|
2020-02-22 09:05:26 +08:00
|
|
|
sec := conf.File.Section("log.xorm")
|
2020-09-29 22:26:07 +08:00
|
|
|
fileWriter, err := log.NewFileWriter(logPath,
|
2017-03-23 18:34:25 -04:00
|
|
|
log.FileRotationConfig{
|
|
|
|
|
Rotate: sec.Key("ROTATE").MustBool(true),
|
|
|
|
|
Daily: sec.Key("ROTATE_DAILY").MustBool(true),
|
|
|
|
|
MaxSize: sec.Key("MAX_SIZE").MustInt64(100) * 1024 * 1024,
|
|
|
|
|
MaxDays: sec.Key("MAX_DAYS").MustInt64(3),
|
2020-09-29 22:26:07 +08:00
|
|
|
},
|
|
|
|
|
)
|
2014-03-17 14:03:58 -04:00
|
|
|
if err != nil {
|
2020-05-04 16:25:57 +08:00
|
|
|
return nil, fmt.Errorf("create 'xorm.log': %v", err)
|
2014-03-17 14:03:58 -04:00
|
|
|
}
|
2017-02-09 15:09:37 -05:00
|
|
|
|
2020-04-04 21:14:15 +08:00
|
|
|
x.SetMaxOpenConns(conf.Database.MaxOpenConns)
|
|
|
|
|
x.SetMaxIdleConns(conf.Database.MaxIdleConns)
|
2019-02-06 18:46:15 -05:00
|
|
|
x.SetConnMaxLifetime(time.Second)
|
|
|
|
|
|
2020-02-22 09:05:26 +08:00
|
|
|
if conf.IsProdMode() {
|
2020-09-29 22:26:07 +08:00
|
|
|
x.SetLogger(xorm.NewSimpleLogger3(fileWriter, xorm.DEFAULT_LOG_PREFIX, xorm.DEFAULT_LOG_FLAG, core.LOG_WARNING))
|
2017-02-09 15:09:37 -05:00
|
|
|
} else {
|
2020-09-29 22:26:07 +08:00
|
|
|
x.SetLogger(xorm.NewSimpleLogger(fileWriter))
|
2017-02-09 15:09:37 -05:00
|
|
|
}
|
2016-02-16 22:35:08 +08:00
|
|
|
x.ShowSQL(true)
|
2020-09-29 22:26:07 +08:00
|
|
|
|
|
|
|
|
var gormLogger logger.Writer
|
|
|
|
|
if conf.HookMode {
|
|
|
|
|
gormLogger = &dbutil.Logger{Writer: fileWriter}
|
|
|
|
|
} else {
|
|
|
|
|
gormLogger, err = newLogWriter()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "new log writer")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Init(gormLogger)
|
2014-02-18 17:48:02 -05:00
|
|
|
}
|
|
|
|
|
|
2014-03-29 17:50:51 -04:00
|
|
|
func NewEngine() (err error) {
|
2022-06-05 13:34:21 +08:00
|
|
|
db, err := SetEngine()
|
|
|
|
|
if err != nil {
|
2014-03-29 17:50:51 -04:00
|
|
|
return err
|
2014-04-05 22:46:32 +08:00
|
|
|
}
|
2015-01-22 14:49:52 +02:00
|
|
|
|
2022-06-05 13:34:21 +08:00
|
|
|
if err = migrations.Migrate(x, db); err != nil {
|
2015-02-11 21:58:37 -05:00
|
|
|
return fmt.Errorf("migrate: %v", err)
|
2015-01-22 14:49:52 +02:00
|
|
|
}
|
|
|
|
|
|
2020-04-11 01:25:19 +08:00
|
|
|
if err = x.StoreEngine("InnoDB").Sync2(legacyTables...); err != nil {
|
2022-06-05 13:34:21 +08:00
|
|
|
return errors.Wrap(err, "sync tables")
|
2014-02-19 17:50:53 +08:00
|
|
|
}
|
2015-01-23 09:54:16 +02:00
|
|
|
|
2014-03-29 17:50:51 -04:00
|
|
|
return nil
|
2014-02-18 17:48:02 -05:00
|
|
|
}
|
2014-03-20 16:04:56 -04:00
|
|
|
|
|
|
|
|
type Statistic struct {
|
|
|
|
|
Counter struct {
|
2014-08-28 22:29:00 +08:00
|
|
|
User, Org, PublicKey,
|
|
|
|
|
Repo, Watch, Star, Action, Access,
|
|
|
|
|
Issue, Comment, Oauth, Follow,
|
|
|
|
|
Mirror, Release, LoginSource, Webhook,
|
|
|
|
|
Milestone, Label, HookTask,
|
|
|
|
|
Team, UpdateTask, Attachment int64
|
2014-03-20 16:04:56 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-10 19:54:36 +08:00
|
|
|
func GetStatistic(ctx context.Context) (stats Statistic) {
|
2014-07-07 04:15:08 -04:00
|
|
|
stats.Counter.User = CountUsers()
|
2014-08-28 22:29:00 +08:00
|
|
|
stats.Counter.Org = CountOrganizations()
|
2014-06-21 00:51:41 -04:00
|
|
|
stats.Counter.PublicKey, _ = x.Count(new(PublicKey))
|
2016-07-24 14:32:46 +08:00
|
|
|
stats.Counter.Repo = CountRepositories(true)
|
2014-06-21 00:51:41 -04:00
|
|
|
stats.Counter.Watch, _ = x.Count(new(Watch))
|
2014-08-28 22:29:00 +08:00
|
|
|
stats.Counter.Star, _ = x.Count(new(Star))
|
2014-06-21 00:51:41 -04:00
|
|
|
stats.Counter.Action, _ = x.Count(new(Action))
|
|
|
|
|
stats.Counter.Access, _ = x.Count(new(Access))
|
|
|
|
|
stats.Counter.Issue, _ = x.Count(new(Issue))
|
|
|
|
|
stats.Counter.Comment, _ = x.Count(new(Comment))
|
2015-09-17 16:11:44 -04:00
|
|
|
stats.Counter.Oauth = 0
|
2014-08-28 22:29:00 +08:00
|
|
|
stats.Counter.Follow, _ = x.Count(new(Follow))
|
|
|
|
|
stats.Counter.Mirror, _ = x.Count(new(Mirror))
|
2014-06-21 00:51:41 -04:00
|
|
|
stats.Counter.Release, _ = x.Count(new(Release))
|
2022-06-10 19:54:36 +08:00
|
|
|
stats.Counter.LoginSource = LoginSources.Count(ctx)
|
2014-06-21 00:51:41 -04:00
|
|
|
stats.Counter.Webhook, _ = x.Count(new(Webhook))
|
|
|
|
|
stats.Counter.Milestone, _ = x.Count(new(Milestone))
|
2014-08-28 22:29:00 +08:00
|
|
|
stats.Counter.Label, _ = x.Count(new(Label))
|
|
|
|
|
stats.Counter.HookTask, _ = x.Count(new(HookTask))
|
|
|
|
|
stats.Counter.Team, _ = x.Count(new(Team))
|
|
|
|
|
stats.Counter.Attachment, _ = x.Count(new(Attachment))
|
2021-05-19 13:38:13 +08:00
|
|
|
return stats
|
2014-03-20 16:04:56 -04:00
|
|
|
}
|
2014-05-05 00:55:17 -04:00
|
|
|
|
2014-08-06 17:21:24 -04:00
|
|
|
func Ping() error {
|
2021-12-20 18:46:54 +08:00
|
|
|
if x == nil {
|
|
|
|
|
return errors.New("database not available")
|
|
|
|
|
}
|
2014-08-06 17:21:24 -04:00
|
|
|
return x.Ping()
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-26 04:37:05 -05:00
|
|
|
// The version table. Should have only one row with id==1
|
|
|
|
|
type Version struct {
|
|
|
|
|
ID int64
|
|
|
|
|
Version int64
|
|
|
|
|
}
|