You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

335 lines
9.3 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
8 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "database/sql"
  7. "errors"
  8. "fmt"
  9. "net/url"
  10. "os"
  11. "path"
  12. "strings"
  13. // Needed for the MySQL driver
  14. _ "github.com/go-sql-driver/mysql"
  15. "github.com/go-xorm/core"
  16. "github.com/go-xorm/xorm"
  17. // Needed for the Postgresql driver
  18. _ "github.com/lib/pq"
  19. // Needed for the MSSSQL driver
  20. _ "github.com/denisenkom/go-mssqldb"
  21. "code.gitea.io/gitea/models/migrations"
  22. "code.gitea.io/gitea/modules/log"
  23. "code.gitea.io/gitea/modules/setting"
  24. )
  25. // Engine represents a xorm engine or session.
  26. type Engine interface {
  27. Table(tableNameOrBean interface{}) *xorm.Session
  28. Count(interface{}) (int64, error)
  29. Decr(column string, arg ...interface{}) *xorm.Session
  30. Delete(interface{}) (int64, error)
  31. Exec(string, ...interface{}) (sql.Result, error)
  32. Find(interface{}, ...interface{}) error
  33. Get(interface{}) (bool, error)
  34. Id(interface{}) *xorm.Session
  35. In(string, ...interface{}) *xorm.Session
  36. Incr(column string, arg ...interface{}) *xorm.Session
  37. Insert(...interface{}) (int64, error)
  38. InsertOne(interface{}) (int64, error)
  39. Iterate(interface{}, xorm.IterFunc) error
  40. Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *xorm.Session
  41. SQL(interface{}, ...interface{}) *xorm.Session
  42. Where(interface{}, ...interface{}) *xorm.Session
  43. }
  44. var (
  45. x *xorm.Engine
  46. tables []interface{}
  47. // HasEngine specifies if we have a xorm.Engine
  48. HasEngine bool
  49. // DbCfg holds the database settings
  50. DbCfg struct {
  51. Type, Host, Name, User, Passwd, Path, SSLMode string
  52. Timeout int
  53. }
  54. // EnableSQLite3 use SQLite3
  55. EnableSQLite3 bool
  56. // EnableTiDB enable TiDB
  57. EnableTiDB bool
  58. )
  59. func init() {
  60. tables = append(tables,
  61. new(User),
  62. new(PublicKey),
  63. new(AccessToken),
  64. new(Repository),
  65. new(DeployKey),
  66. new(Collaboration),
  67. new(Access),
  68. new(Upload),
  69. new(Watch),
  70. new(Star),
  71. new(Follow),
  72. new(Action),
  73. new(Issue),
  74. new(PullRequest),
  75. new(Comment),
  76. new(Attachment),
  77. new(Label),
  78. new(IssueLabel),
  79. new(Milestone),
  80. new(Mirror),
  81. new(Release),
  82. new(LoginSource),
  83. new(Webhook),
  84. new(HookTask),
  85. new(Team),
  86. new(OrgUser),
  87. new(TeamUser),
  88. new(TeamRepo),
  89. new(Notice),
  90. new(EmailAddress),
  91. new(Notification),
  92. new(IssueUser),
  93. new(LFSMetaObject),
  94. new(TwoFactor),
  95. new(GPGKey),
  96. new(RepoUnit),
  97. new(RepoRedirect),
  98. new(ExternalLoginUser),
  99. new(ProtectedBranch),
  100. new(UserOpenID),
  101. new(IssueWatch),
  102. new(CommitStatus),
  103. )
  104. gonicNames := []string{"SSL", "UID"}
  105. for _, name := range gonicNames {
  106. core.LintGonicMapper[name] = true
  107. }
  108. }
  109. // LoadConfigs loads the database settings
  110. func LoadConfigs() {
  111. sec := setting.Cfg.Section("database")
  112. DbCfg.Type = sec.Key("DB_TYPE").String()
  113. switch DbCfg.Type {
  114. case "sqlite3":
  115. setting.UseSQLite3 = true
  116. case "mysql":
  117. setting.UseMySQL = true
  118. case "postgres":
  119. setting.UsePostgreSQL = true
  120. case "tidb":
  121. setting.UseTiDB = true
  122. case "mssql":
  123. setting.UseMSSQL = true
  124. }
  125. DbCfg.Host = sec.Key("HOST").String()
  126. DbCfg.Name = sec.Key("NAME").String()
  127. DbCfg.User = sec.Key("USER").String()
  128. if len(DbCfg.Passwd) == 0 {
  129. DbCfg.Passwd = sec.Key("PASSWD").String()
  130. }
  131. DbCfg.SSLMode = sec.Key("SSL_MODE").String()
  132. DbCfg.Path = sec.Key("PATH").MustString("data/gitea.db")
  133. DbCfg.Timeout = sec.Key("SQLITE_TIMEOUT").MustInt(500)
  134. sec = setting.Cfg.Section("indexer")
  135. setting.Indexer.IssuePath = sec.Key("ISSUE_INDEXER_PATH").MustString("indexers/issues.bleve")
  136. setting.Indexer.UpdateQueueLength = sec.Key("UPDATE_BUFFER_LEN").MustInt(20)
  137. }
  138. // parsePostgreSQLHostPort parses given input in various forms defined in
  139. // https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
  140. // and returns proper host and port number.
  141. func parsePostgreSQLHostPort(info string) (string, string) {
  142. host, port := "127.0.0.1", "5432"
  143. if strings.Contains(info, ":") && !strings.HasSuffix(info, "]") {
  144. idx := strings.LastIndex(info, ":")
  145. host = info[:idx]
  146. port = info[idx+1:]
  147. } else if len(info) > 0 {
  148. host = info
  149. }
  150. return host, port
  151. }
  152. func parseMSSQLHostPort(info string) (string, string) {
  153. host, port := "127.0.0.1", "1433"
  154. if strings.Contains(info, ":") {
  155. host = strings.Split(info, ":")[0]
  156. port = strings.Split(info, ":")[1]
  157. } else if strings.Contains(info, ",") {
  158. host = strings.Split(info, ",")[0]
  159. port = strings.TrimSpace(strings.Split(info, ",")[1])
  160. } else if len(info) > 0 {
  161. host = info
  162. }
  163. return host, port
  164. }
  165. func getEngine() (*xorm.Engine, error) {
  166. connStr := ""
  167. var Param = "?"
  168. if strings.Contains(DbCfg.Name, Param) {
  169. Param = "&"
  170. }
  171. switch DbCfg.Type {
  172. case "mysql":
  173. if DbCfg.Host[0] == '/' { // looks like a unix socket
  174. connStr = fmt.Sprintf("%s:%s@unix(%s)/%s%scharset=utf8&parseTime=true",
  175. DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name, Param)
  176. } else {
  177. connStr = fmt.Sprintf("%s:%s@tcp(%s)/%s%scharset=utf8&parseTime=true",
  178. DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name, Param)
  179. }
  180. case "postgres":
  181. host, port := parsePostgreSQLHostPort(DbCfg.Host)
  182. if host[0] == '/' { // looks like a unix socket
  183. connStr = fmt.Sprintf("postgres://%s:%s@:%s/%s%ssslmode=%s&host=%s",
  184. url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Passwd), port, DbCfg.Name, Param, DbCfg.SSLMode, host)
  185. } else {
  186. connStr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s%ssslmode=%s",
  187. url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Passwd), host, port, DbCfg.Name, Param, DbCfg.SSLMode)
  188. }
  189. case "mssql":
  190. host, port := parseMSSQLHostPort(DbCfg.Host)
  191. connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, DbCfg.Name, DbCfg.User, DbCfg.Passwd)
  192. case "sqlite3":
  193. if !EnableSQLite3 {
  194. return nil, errors.New("this binary version does not build support for SQLite3")
  195. }
  196. if err := os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm); err != nil {
  197. return nil, fmt.Errorf("Failed to create directories: %v", err)
  198. }
  199. connStr = fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d", DbCfg.Path, DbCfg.Timeout)
  200. case "tidb":
  201. if !EnableTiDB {
  202. return nil, errors.New("this binary version does not build support for TiDB")
  203. }
  204. if err := os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm); err != nil {
  205. return nil, fmt.Errorf("Failed to create directories: %v", err)
  206. }
  207. connStr = "goleveldb://" + DbCfg.Path
  208. default:
  209. return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
  210. }
  211. return xorm.NewEngine(DbCfg.Type, connStr)
  212. }
  213. // NewTestEngine sets a new test xorm.Engine
  214. func NewTestEngine(x *xorm.Engine) (err error) {
  215. x, err = getEngine()
  216. if err != nil {
  217. return fmt.Errorf("Connect to database: %v", err)
  218. }
  219. x.SetMapper(core.GonicMapper{})
  220. x.SetLogger(log.XORMLogger)
  221. return x.StoreEngine("InnoDB").Sync2(tables...)
  222. }
  223. // SetEngine sets the xorm.Engine
  224. func SetEngine() (err error) {
  225. x, err = getEngine()
  226. if err != nil {
  227. return fmt.Errorf("Failed to connect to database: %v", err)
  228. }
  229. x.SetMapper(core.GonicMapper{})
  230. // WARNING: for serv command, MUST remove the output to os.stdout,
  231. // so use log file to instead print to stdout.
  232. x.SetLogger(log.XORMLogger)
  233. x.ShowSQL(true)
  234. return nil
  235. }
  236. // NewEngine initializes a new xorm.Engine
  237. func NewEngine() (err error) {
  238. if err = SetEngine(); err != nil {
  239. return err
  240. }
  241. if err = x.Ping(); err != nil {
  242. return err
  243. }
  244. if err = migrations.Migrate(x); err != nil {
  245. return fmt.Errorf("migrate: %v", err)
  246. }
  247. if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
  248. return fmt.Errorf("sync database struct error: %v", err)
  249. }
  250. return nil
  251. }
  252. // Statistic contains the database statistics
  253. type Statistic struct {
  254. Counter struct {
  255. User, Org, PublicKey,
  256. Repo, Watch, Star, Action, Access,
  257. Issue, Comment, Oauth, Follow,
  258. Mirror, Release, LoginSource, Webhook,
  259. Milestone, Label, HookTask,
  260. Team, UpdateTask, Attachment int64
  261. }
  262. }
  263. // GetStatistic returns the database statistics
  264. func GetStatistic() (stats Statistic) {
  265. stats.Counter.User = CountUsers()
  266. stats.Counter.Org = CountOrganizations()
  267. stats.Counter.PublicKey, _ = x.Count(new(PublicKey))
  268. stats.Counter.Repo = CountRepositories(true)
  269. stats.Counter.Watch, _ = x.Count(new(Watch))
  270. stats.Counter.Star, _ = x.Count(new(Star))
  271. stats.Counter.Action, _ = x.Count(new(Action))
  272. stats.Counter.Access, _ = x.Count(new(Access))
  273. stats.Counter.Issue, _ = x.Count(new(Issue))
  274. stats.Counter.Comment, _ = x.Count(new(Comment))
  275. stats.Counter.Oauth = 0
  276. stats.Counter.Follow, _ = x.Count(new(Follow))
  277. stats.Counter.Mirror, _ = x.Count(new(Mirror))
  278. stats.Counter.Release, _ = x.Count(new(Release))
  279. stats.Counter.LoginSource = CountLoginSources()
  280. stats.Counter.Webhook, _ = x.Count(new(Webhook))
  281. stats.Counter.Milestone, _ = x.Count(new(Milestone))
  282. stats.Counter.Label, _ = x.Count(new(Label))
  283. stats.Counter.HookTask, _ = x.Count(new(HookTask))
  284. stats.Counter.Team, _ = x.Count(new(Team))
  285. stats.Counter.Attachment, _ = x.Count(new(Attachment))
  286. return
  287. }
  288. // Ping tests if database is alive
  289. func Ping() error {
  290. return x.Ping()
  291. }
  292. // DumpDatabase dumps all data from database according the special database SQL syntax to file system.
  293. func DumpDatabase(filePath string, dbType string) error {
  294. var tbs []*core.Table
  295. for _, t := range tables {
  296. tbs = append(tbs, x.TableInfo(t).Table)
  297. }
  298. if len(dbType) > 0 {
  299. return x.DumpTablesToFile(tbs, filePath, core.DbType(dbType))
  300. }
  301. return x.DumpTablesToFile(tbs, filePath)
  302. }