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.

models.go 9.5KB

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