@@ -95,87 +95,132 @@ func init() {
95
95
}
96
96
}
97
97
98
- // newXORMEngine returns a new XORM engine from the configuration
99
- func newXORMEngine () (* xorm.Engine , error ) {
100
- connStr , err := setting .DBConnStr ()
98
+ // newXORMEngineGroup creates an xorm.EngineGroup (with one master and one or more slaves).
99
+ // It assumes you have separate master and slave DSNs defined via the settings package.
100
+ func newXORMEngineGroup () (Engine , error ) {
101
+ // Retrieve master DSN from settings.
102
+ masterConnStr , err := setting .DBMasterConnStr ()
101
103
if err != nil {
102
- return nil , err
104
+ return nil , fmt . Errorf ( "failed to determine master DSN: %w" , err )
103
105
}
104
106
105
- var engine * xorm.Engine
106
-
107
+ var masterEngine * xorm.Engine
108
+ // For PostgreSQL: if a schema is provided, we use the special "postgresschema" driver.
107
109
if setting .Database .Type .IsPostgreSQL () && len (setting .Database .Schema ) > 0 {
108
- // OK whilst we sort out our schema issues - create a schema aware postgres
109
110
registerPostgresSchemaDriver ()
110
- engine , err = xorm .NewEngine ("postgresschema" , connStr )
111
+ masterEngine , err = xorm .NewEngine ("postgresschema" , masterConnStr )
111
112
} else {
112
- engine , err = xorm .NewEngine (setting .Database .Type .String (), connStr )
113
+ masterEngine , err = xorm .NewEngine (setting .Database .Type .String (), masterConnStr )
113
114
}
114
-
115
115
if err != nil {
116
- return nil , err
116
+ return nil , fmt . Errorf ( "failed to create master engine: %w" , err )
117
117
}
118
118
if setting .Database .Type .IsMySQL () {
119
- engine .Dialect ().SetParams (map [string ]string {"rowFormat" : "DYNAMIC" })
119
+ masterEngine .Dialect ().SetParams (map [string ]string {"rowFormat" : "DYNAMIC" })
120
120
}
121
- engine .SetSchema (setting .Database .Schema )
122
- return engine , nil
121
+ masterEngine .SetSchema (setting .Database .Schema )
122
+
123
+ slaveConnStrs , err := setting .DBSlaveConnStrs ()
124
+ if err != nil {
125
+ return nil , fmt .Errorf ("failed to load slave DSNs: %w" , err )
126
+ }
127
+
128
+ var slaveEngines []* xorm.Engine
129
+ // Iterate over all slave DSNs and create engines
130
+ for _ , dsn := range slaveConnStrs {
131
+ slaveEngine , err := xorm .NewEngine (setting .Database .Type .String (), dsn )
132
+ if err != nil {
133
+ return nil , fmt .Errorf ("failed to create slave engine for dsn %q: %w" , dsn , err )
134
+ }
135
+ if setting .Database .Type .IsMySQL () {
136
+ slaveEngine .Dialect ().SetParams (map [string ]string {"rowFormat" : "DYNAMIC" })
137
+ }
138
+ slaveEngine .SetSchema (setting .Database .Schema )
139
+ slaveEngines = append (slaveEngines , slaveEngine )
140
+ }
141
+
142
+ policy := setting .BuildLoadBalancePolicy (& setting .Database , slaveEngines )
143
+
144
+ // Create the EngineGroup using the selected policy
145
+ group , err := xorm .NewEngineGroup (masterEngine , slaveEngines , policy )
146
+ if err != nil {
147
+ return nil , fmt .Errorf ("failed to create engine group: %w" , err )
148
+ }
149
+ return engineGroupWrapper {group }, nil
150
+ }
151
+
152
+ type engineGroupWrapper struct {
153
+ * xorm.EngineGroup
154
+ }
155
+
156
+ func (w engineGroupWrapper ) AddHook (hook contexts.Hook ) bool {
157
+ w .EngineGroup .AddHook (hook )
158
+ return true
123
159
}
124
160
125
- // SyncAllTables sync the schemas of all tables, is required by unit test code
161
+ // SyncAllTables sync the schemas of all tables
126
162
func SyncAllTables () error {
127
163
_ , err := x .StoreEngine ("InnoDB" ).SyncWithOptions (xorm.SyncOptions {
128
164
WarnIfDatabaseColumnMissed : true ,
129
165
}, tables ... )
130
166
return err
131
167
}
132
168
133
- // InitEngine initializes the xorm.Engine and sets it as db.DefaultContext
169
+ // InitEngine initializes the xorm EngineGroup and sets it as db.DefaultContext
134
170
func InitEngine (ctx context.Context ) error {
135
- xormEngine , err := newXORMEngine ()
171
+ xormEngine , err := newXORMEngineGroup ()
136
172
if err != nil {
137
173
return fmt .Errorf ("failed to connect to database: %w" , err )
138
174
}
175
+ // Try to cast to the concrete type to access diagnostic methods
176
+ if eng , ok := xormEngine .(engineGroupWrapper ); ok {
177
+ eng .SetMapper (names.GonicMapper {})
178
+ // WARNING: for serv command, MUST remove the output to os.Stdout,
179
+ // so use a log file instead of printing to stdout.
180
+ eng .SetLogger (NewXORMLogger (setting .Database .LogSQL ))
181
+ eng .ShowSQL (setting .Database .LogSQL )
182
+ eng .SetMaxOpenConns (setting .Database .MaxOpenConns )
183
+ eng .SetMaxIdleConns (setting .Database .MaxIdleConns )
184
+ eng .SetConnMaxLifetime (setting .Database .ConnMaxLifetime )
185
+ eng .SetConnMaxIdleTime (setting .Database .ConnMaxIdleTime )
186
+ eng .SetDefaultContext (ctx )
187
+
188
+ if setting .Database .SlowQueryThreshold > 0 {
189
+ eng .AddHook (& SlowQueryHook {
190
+ Treshold : setting .Database .SlowQueryThreshold ,
191
+ Logger : log .GetLogger ("xorm" ),
192
+ })
193
+ }
139
194
140
- xormEngine .SetMapper (names.GonicMapper {})
141
- // WARNING: for serv command, MUST remove the output to os.stdout,
142
- // so use log file to instead print to stdout.
143
- xormEngine .SetLogger (NewXORMLogger (setting .Database .LogSQL ))
144
- xormEngine .ShowSQL (setting .Database .LogSQL )
145
- xormEngine .SetMaxOpenConns (setting .Database .MaxOpenConns )
146
- xormEngine .SetMaxIdleConns (setting .Database .MaxIdleConns )
147
- xormEngine .SetConnMaxLifetime (setting .Database .ConnMaxLifetime )
148
- xormEngine .SetConnMaxIdleTime (setting .Database .ConnMaxIdleTime )
149
- xormEngine .SetDefaultContext (ctx )
150
-
151
- if setting .Database .SlowQueryThreshold > 0 {
152
- xormEngine .AddHook (& SlowQueryHook {
153
- Treshold : setting .Database .SlowQueryThreshold ,
154
- Logger : log .GetLogger ("xorm" ),
155
- })
156
- }
157
-
158
- errorLogger := log .GetLogger ("xorm" )
159
- if setting .IsInTesting {
160
- errorLogger = log .GetLogger (log .DEFAULT )
161
- }
195
+ errorLogger := log .GetLogger ("xorm" )
196
+ if setting .IsInTesting {
197
+ errorLogger = log .GetLogger (log .DEFAULT )
198
+ }
162
199
163
- xormEngine .AddHook (& ErrorQueryHook {
164
- Logger : errorLogger ,
165
- })
200
+ eng .AddHook (& ErrorQueryHook {
201
+ Logger : errorLogger ,
202
+ })
166
203
167
- xormEngine .AddHook (& TracingHook {})
204
+ eng .AddHook (& TracingHook {})
168
205
169
- SetDefaultEngine (ctx , xormEngine )
206
+ SetDefaultEngine (ctx , eng )
207
+ } else {
208
+ // Fallback: if type assertion fails, set default engine without extended diagnostics
209
+ SetDefaultEngine (ctx , xormEngine )
210
+ }
170
211
return nil
171
212
}
172
213
173
- // SetDefaultEngine sets the default engine for db
174
- func SetDefaultEngine (ctx context.Context , eng * xorm.Engine ) {
175
- x = eng
214
+ // SetDefaultEngine sets the default engine for db.
215
+ func SetDefaultEngine (ctx context.Context , eng Engine ) {
216
+ masterEngine , err := GetMasterEngine (eng )
217
+ if err == nil {
218
+ x = masterEngine
219
+ }
220
+
176
221
DefaultContext = & Context {
177
222
Context : ctx ,
178
- e : x ,
223
+ e : eng ,
179
224
}
180
225
}
181
226
@@ -191,12 +236,12 @@ func UnsetDefaultEngine() {
191
236
DefaultContext = nil
192
237
}
193
238
194
- // InitEngineWithMigration initializes a new xorm.Engine and sets it as the db.DefaultContext
239
+ // InitEngineWithMigration initializes a new xorm EngineGroup, runs migrations, and sets it as db.DefaultContext
195
240
// This function must never call .Sync() if the provided migration function fails.
196
241
// When called from the "doctor" command, the migration function is a version check
197
242
// that prevents the doctor from fixing anything in the database if the migration level
198
243
// is different from the expected value.
199
- func InitEngineWithMigration (ctx context.Context , migrateFunc func (* xorm. Engine ) error ) (err error ) {
244
+ func InitEngineWithMigration (ctx context.Context , migrateFunc func (Engine ) error ) (err error ) {
200
245
if err = InitEngine (ctx ); err != nil {
201
246
return err
202
247
}
@@ -230,14 +275,14 @@ func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine)
230
275
return nil
231
276
}
232
277
233
- // NamesToBean return a list of beans or an error
278
+ // NamesToBean returns a list of beans given names
234
279
func NamesToBean (names ... string ) ([]any , error ) {
235
280
beans := []any {}
236
281
if len (names ) == 0 {
237
282
beans = append (beans , tables ... )
238
283
return beans , nil
239
284
}
240
- // Need to map provided names to beans...
285
+ // Map provided names to beans
241
286
beanMap := make (map [string ]any )
242
287
for _ , bean := range tables {
243
288
beanMap [strings .ToLower (reflect .Indirect (reflect .ValueOf (bean )).Type ().Name ())] = bean
@@ -259,7 +304,7 @@ func NamesToBean(names ...string) ([]any, error) {
259
304
return beans , nil
260
305
}
261
306
262
- // DumpDatabase dumps all data from database according the special database SQL syntax to file system.
307
+ // DumpDatabase dumps all data from database using special SQL syntax to the file system.
263
308
func DumpDatabase (filePath , dbType string ) error {
264
309
var tbs []* schemas.Table
265
310
for _ , t := range tables {
@@ -295,29 +340,33 @@ func MaxBatchInsertSize(bean any) int {
295
340
return 999 / len (t .ColumnsSeq ())
296
341
}
297
342
298
- // IsTableNotEmpty returns true if table has at least one record
343
+ // IsTableNotEmpty returns true if the table has at least one record
299
344
func IsTableNotEmpty (beanOrTableName any ) (bool , error ) {
300
345
return x .Table (beanOrTableName ).Exist ()
301
346
}
302
347
303
- // DeleteAllRecords will delete all the records of this table
348
+ // DeleteAllRecords deletes all records in the given table.
304
349
func DeleteAllRecords (tableName string ) error {
305
350
_ , err := x .Exec (fmt .Sprintf ("DELETE FROM %s" , tableName ))
306
351
return err
307
352
}
308
353
309
- // GetMaxID will return max id of the table
354
+ // GetMaxID returns the maximum id in the table
310
355
func GetMaxID (beanOrTableName any ) (maxID int64 , err error ) {
311
356
_ , err = x .Select ("MAX(id)" ).Table (beanOrTableName ).Get (& maxID )
312
357
return maxID , err
313
358
}
314
359
315
360
func SetLogSQL (ctx context.Context , on bool ) {
316
- e := GetEngine (ctx )
317
- if x , ok := e .(* xorm.Engine ); ok {
318
- x .ShowSQL (on )
319
- } else if sess , ok := e .(* xorm.Session ); ok {
361
+ ctxEngine := GetEngine (ctx )
362
+
363
+ if sess , ok := ctxEngine .(* xorm.Session ); ok {
320
364
sess .Engine ().ShowSQL (on )
365
+ } else if wrapper , ok := ctxEngine .(engineGroupWrapper ); ok {
366
+ // Handle engineGroupWrapper directly
367
+ wrapper .ShowSQL (on )
368
+ } else if masterEngine , err := GetMasterEngine (ctxEngine ); err == nil {
369
+ masterEngine .ShowSQL (on )
321
370
}
322
371
}
323
372
@@ -374,3 +423,18 @@ func (h *ErrorQueryHook) AfterProcess(c *contexts.ContextHook) error {
374
423
}
375
424
return nil
376
425
}
426
+
427
+ // GetMasterEngine extracts the master xorm.Engine from the provided xorm.Engine.
428
+ // This handles both direct xorm.Engine cases and engines that implement a Master() method.
429
+ func GetMasterEngine (x Engine ) (* xorm.Engine , error ) {
430
+ if getter , ok := x .(interface { Master () * xorm.Engine }); ok {
431
+ return getter .Master (), nil
432
+ }
433
+
434
+ engine , ok := x .(* xorm.Engine )
435
+ if ! ok {
436
+ return nil , fmt .Errorf ("unsupported engine type: %T" , x )
437
+ }
438
+
439
+ return engine , nil
440
+ }
0 commit comments