1
- // Copyright 2021 The Gitea Authors. All rights reserved.
1
+ // Copyright 2014 The Gogs Authors. All rights reserved.
2
+ // Copyright 2018 The Gitea Authors. All rights reserved.
2
3
// Use of this source code is governed by a MIT-style
3
4
// license that can be found in the LICENSE file.
4
5
@@ -7,15 +8,34 @@ package db
7
8
import (
8
9
"context"
9
10
"database/sql"
11
+ "errors"
10
12
"fmt"
11
13
"io"
12
- "time"
14
+ "reflect"
15
+ "strings"
13
16
14
- "code.gitea.io/gitea/models/migrations"
15
- "code.gitea.io/gitea/modules/log"
16
17
"code.gitea.io/gitea/modules/setting"
18
+
19
+ // Needed for the MySQL driver
20
+ _ "github.com/go-sql-driver/mysql"
17
21
"xorm.io/xorm"
22
+ "xorm.io/xorm/names"
18
23
"xorm.io/xorm/schemas"
24
+
25
+ // Needed for the Postgresql driver
26
+ _ "github.com/lib/pq"
27
+
28
+ // Needed for the MSSQL driver
29
+ _ "github.com/denisenkom/go-mssqldb"
30
+ )
31
+
32
+ var (
33
+ x * xorm.Engine
34
+ tables []interface {}
35
+ initFuncs []func () error
36
+
37
+ // HasEngine specifies if we have a xorm.Engine
38
+ HasEngine bool
19
39
)
20
40
21
41
// Engine represents a xorm engine or session.
@@ -58,25 +78,218 @@ func DumpTables(tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) erro
58
78
return x .DumpTables (tables , w , tp ... )
59
79
}
60
80
61
- // InitEngine In case of problems connecting to DB, retry connection. Eg, PGSQL in Docker Container on Synology
62
- func InitEngine (ctx context.Context ) (err error ) {
63
- log .Info ("Beginning ORM engine initialization." )
64
- for i := 0 ; i < setting .Database .DBConnectRetries ; i ++ {
65
- select {
66
- case <- ctx .Done ():
67
- return fmt .Errorf ("Aborted due to shutdown:\n in retry ORM engine initialization" )
68
- default :
81
+ // RegisterModels registers models
82
+ func RegisterModel (bean interface {}, initFunc ... func () error ) {
83
+ tables = append (tables , bean )
84
+ if len (initFuncs ) > 0 && initFunc [0 ] != nil {
85
+ initFuncs = append (initFuncs , initFunc [0 ])
86
+ }
87
+ }
88
+
89
+ func init () {
90
+ gonicNames := []string {"SSL" , "UID" }
91
+ for _ , name := range gonicNames {
92
+ names .LintGonicMapper [name ] = true
93
+ }
94
+ }
95
+
96
+ // GetNewEngine returns a new xorm engine from the configuration
97
+ func GetNewEngine () (* xorm.Engine , error ) {
98
+ connStr , err := setting .DBConnStr ()
99
+ if err != nil {
100
+ return nil , err
101
+ }
102
+
103
+ var engine * xorm.Engine
104
+
105
+ if setting .Database .UsePostgreSQL && len (setting .Database .Schema ) > 0 {
106
+ // OK whilst we sort out our schema issues - create a schema aware postgres
107
+ registerPostgresSchemaDriver ()
108
+ engine , err = xorm .NewEngine ("postgresschema" , connStr )
109
+ } else {
110
+ engine , err = xorm .NewEngine (setting .Database .Type , connStr )
111
+ }
112
+
113
+ if err != nil {
114
+ return nil , err
115
+ }
116
+ if setting .Database .Type == "mysql" {
117
+ engine .Dialect ().SetParams (map [string ]string {"rowFormat" : "DYNAMIC" })
118
+ } else if setting .Database .Type == "mssql" {
119
+ engine .Dialect ().SetParams (map [string ]string {"DEFAULT_VARCHAR" : "nvarchar" })
120
+ }
121
+ engine .SetSchema (setting .Database .Schema )
122
+ return engine , nil
123
+ }
124
+
125
+ func syncTables () error {
126
+ return x .StoreEngine ("InnoDB" ).Sync2 (tables ... )
127
+ }
128
+
129
+ // NewTestEngine sets a new test xorm.Engine
130
+ func NewTestEngine () (err error ) {
131
+ x , err = GetNewEngine ()
132
+ if err != nil {
133
+ return fmt .Errorf ("Connect to database: %v" , err )
134
+ }
135
+
136
+ x .SetMapper (names.GonicMapper {})
137
+ x .SetLogger (NewXORMLogger (! setting .IsProd ()))
138
+ x .ShowSQL (! setting .IsProd ())
139
+ return syncTables ()
140
+ }
141
+
142
+ // SetEngine sets the xorm.Engine
143
+ func SetEngine () (err error ) {
144
+ x , err = GetNewEngine ()
145
+ if err != nil {
146
+ return fmt .Errorf ("Failed to connect to database: %v" , err )
147
+ }
148
+
149
+ x .SetMapper (names.GonicMapper {})
150
+ // WARNING: for serv command, MUST remove the output to os.stdout,
151
+ // so use log file to instead print to stdout.
152
+ x .SetLogger (NewXORMLogger (setting .Database .LogSQL ))
153
+ x .ShowSQL (setting .Database .LogSQL )
154
+ x .SetMaxOpenConns (setting .Database .MaxOpenConns )
155
+ x .SetMaxIdleConns (setting .Database .MaxIdleConns )
156
+ x .SetConnMaxLifetime (setting .Database .ConnMaxLifetime )
157
+ return nil
158
+ }
159
+
160
+ // NewEngine initializes a new xorm.Engine
161
+ // This function must never call .Sync2() if the provided migration function fails.
162
+ // When called from the "doctor" command, the migration function is a version check
163
+ // that prevents the doctor from fixing anything in the database if the migration level
164
+ // is different from the expected value.
165
+ func NewEngine (ctx context.Context , migrateFunc func (* xorm.Engine ) error ) (err error ) {
166
+ if err = SetEngine (); err != nil {
167
+ return err
168
+ }
169
+
170
+ x .SetDefaultContext (ctx )
171
+
172
+ if err = x .Ping (); err != nil {
173
+ return err
174
+ }
175
+
176
+ if err = migrateFunc (x ); err != nil {
177
+ return fmt .Errorf ("migrate: %v" , err )
178
+ }
179
+
180
+ if err = syncTables (); err != nil {
181
+ return fmt .Errorf ("sync database struct error: %v" , err )
182
+ }
183
+
184
+ for _ , initFunc := range initFuncs {
185
+ if err := initFunc (); err != nil {
186
+ return fmt .Errorf ("initFunc failed: %v" , err )
69
187
}
70
- log .Info ("ORM engine initialization attempt #%d/%d..." , i + 1 , setting .Database .DBConnectRetries )
71
- if err = NewEngine (ctx , migrations .Migrate ); err == nil {
72
- break
73
- } else if i == setting .Database .DBConnectRetries - 1 {
188
+ }
189
+
190
+ return nil
191
+ }
192
+
193
+ // NamesToBean return a list of beans or an error
194
+ func NamesToBean (names ... string ) ([]interface {}, error ) {
195
+ beans := []interface {}{}
196
+ if len (names ) == 0 {
197
+ beans = append (beans , tables ... )
198
+ return beans , nil
199
+ }
200
+ // Need to map provided names to beans...
201
+ beanMap := make (map [string ]interface {})
202
+ for _ , bean := range tables {
203
+
204
+ beanMap [strings .ToLower (reflect .Indirect (reflect .ValueOf (bean )).Type ().Name ())] = bean
205
+ beanMap [strings .ToLower (x .TableName (bean ))] = bean
206
+ beanMap [strings .ToLower (x .TableName (bean , true ))] = bean
207
+ }
208
+
209
+ gotBean := make (map [interface {}]bool )
210
+ for _ , name := range names {
211
+ bean , ok := beanMap [strings .ToLower (strings .TrimSpace (name ))]
212
+ if ! ok {
213
+ return nil , fmt .Errorf ("No table found that matches: %s" , name )
214
+ }
215
+ if ! gotBean [bean ] {
216
+ beans = append (beans , bean )
217
+ gotBean [bean ] = true
218
+ }
219
+ }
220
+ return beans , nil
221
+ }
222
+
223
+ // Ping tests if database is alive
224
+ func Ping () error {
225
+ if x != nil {
226
+ return x .Ping ()
227
+ }
228
+ return errors .New ("database not configured" )
229
+ }
230
+
231
+ // DumpDatabase dumps all data from database according the special database SQL syntax to file system.
232
+ func DumpDatabase (filePath , dbType string ) error {
233
+ var tbs []* schemas.Table
234
+ for _ , t := range tables {
235
+ t , err := x .TableInfo (t )
236
+ if err != nil {
74
237
return err
75
238
}
76
- log .Error ("ORM engine initialization attempt #%d/%d failed. Error: %v" , i + 1 , setting .Database .DBConnectRetries , err )
77
- log .Info ("Backing off for %d seconds" , int64 (setting .Database .DBConnectBackoff / time .Second ))
78
- time .Sleep (setting .Database .DBConnectBackoff )
239
+ tbs = append (tbs , t )
79
240
}
80
- HasEngine = true
81
- return nil
241
+
242
+ type Version struct {
243
+ ID int64 `xorm:"pk autoincr"`
244
+ Version int64
245
+ }
246
+ t , err := x .TableInfo (& Version {})
247
+ if err != nil {
248
+ return err
249
+ }
250
+ tbs = append (tbs , t )
251
+
252
+ if len (dbType ) > 0 {
253
+ return x .DumpTablesToFile (tbs , filePath , schemas .DBType (dbType ))
254
+ }
255
+ return x .DumpTablesToFile (tbs , filePath )
256
+ }
257
+
258
+ // MaxBatchInsertSize returns the table's max batch insert size
259
+ func MaxBatchInsertSize (bean interface {}) int {
260
+ t , err := x .TableInfo (bean )
261
+ if err != nil {
262
+ return 50
263
+ }
264
+ return 999 / len (t .ColumnsSeq ())
265
+ }
266
+
267
+ // Count returns records number according struct's fields as database query conditions
268
+ func Count (bean interface {}) (int64 , error ) {
269
+ return x .Count (bean )
270
+ }
271
+
272
+ // IsTableNotEmpty returns true if table has at least one record
273
+ func IsTableNotEmpty (tableName string ) (bool , error ) {
274
+ return x .Table (tableName ).Exist ()
275
+ }
276
+
277
+ // DeleteAllRecords will delete all the records of this table
278
+ func DeleteAllRecords (tableName string ) error {
279
+ _ , err := x .Exec (fmt .Sprintf ("DELETE FROM %s" , tableName ))
280
+ return err
281
+ }
282
+
283
+ // GetMaxID will return max id of the table
284
+ func GetMaxID (beanOrTableName interface {}) (maxID int64 , err error ) {
285
+ _ , err = x .Select ("MAX(id)" ).Table (beanOrTableName ).Get (& maxID )
286
+ return
287
+ }
288
+
289
+ // FindByMaxID filled results as the condition from database
290
+ func FindByMaxID (maxID int64 , limit int , results interface {}) error {
291
+ return x .Where ("id <= ?" , maxID ).
292
+ OrderBy ("id DESC" ).
293
+ Limit (limit ).
294
+ Find (results )
82
295
}
0 commit comments