aboutsummaryrefslogtreecommitdiffstats
path: root/models/db
diff options
context:
space:
mode:
Diffstat (limited to 'models/db')
-rw-r--r--models/db/context.go16
-rw-r--r--models/db/context_test.go2
-rwxr-xr-xmodels/db/engine.go2
-rw-r--r--models/db/engine_hook.go25
-rw-r--r--models/db/engine_init.go11
-rw-r--r--models/db/engine_test.go2
-rw-r--r--models/db/error.go2
-rw-r--r--models/db/list_test.go2
-rw-r--r--models/db/log.go2
-rw-r--r--models/db/name.go11
-rw-r--r--models/db/search.go4
-rw-r--r--models/db/sql_postgres_with_schema.go4
12 files changed, 51 insertions, 32 deletions
diff --git a/models/db/context.go b/models/db/context.go
index 51627712b1..ad99ada8c8 100644
--- a/models/db/context.go
+++ b/models/db/context.go
@@ -67,7 +67,7 @@ func contextSafetyCheck(e Engine) {
_ = e.SQL("SELECT 1").Iterate(&m{}, func(int, any) error {
callers := make([]uintptr, 32)
callerNum := runtime.Callers(1, callers)
- for i := 0; i < callerNum; i++ {
+ for i := range callerNum {
if funcName := runtime.FuncForPC(callers[i]).Name(); funcName == "xorm.io/xorm.(*Session).Iterate" {
contextSafetyDeniedFuncPCs = append(contextSafetyDeniedFuncPCs, callers[i])
}
@@ -82,7 +82,7 @@ func contextSafetyCheck(e Engine) {
// it should be very fast: xxxx ns/op
callers := make([]uintptr, 32)
callerNum := runtime.Callers(3, callers) // skip 3: runtime.Callers, contextSafetyCheck, GetEngine
- for i := 0; i < callerNum; i++ {
+ for i := range callerNum {
if slices.Contains(contextSafetyDeniedFuncPCs, callers[i]) {
panic(errors.New("using database context in an iterator would cause corrupted results"))
}
@@ -178,6 +178,15 @@ func WithTx(parentCtx context.Context, f func(ctx context.Context) error) error
return txWithNoCheck(parentCtx, f)
}
+// WithTx2 is similar to WithTx, but it has two return values: result and error.
+func WithTx2[T any](parentCtx context.Context, f func(ctx context.Context) (T, error)) (ret T, errRet error) {
+ errRet = WithTx(parentCtx, func(ctx context.Context) (errInner error) {
+ ret, errInner = f(ctx)
+ return errInner
+ })
+ return ret, errRet
+}
+
func txWithNoCheck(parentCtx context.Context, f func(ctx context.Context) error) error {
sess := xormEngine.NewSession()
defer sess.Close()
@@ -289,6 +298,9 @@ func FindIDs(ctx context.Context, tableName, idCol string, cond builder.Cond) ([
// DecrByIDs decreases the given column for entities of the "bean" type with one of the given ids by one
// Timestamps of the entities won't be updated
func DecrByIDs(ctx context.Context, ids []int64, decrCol string, bean any) error {
+ if len(ids) == 0 {
+ return nil
+ }
_, err := GetEngine(ctx).Decr(decrCol).In("id", ids).NoAutoCondition().NoAutoTime().Update(bean)
return err
}
diff --git a/models/db/context_test.go b/models/db/context_test.go
index e8c6b74d93..a6bd11d2ae 100644
--- a/models/db/context_test.go
+++ b/models/db/context_test.go
@@ -118,7 +118,7 @@ func TestContextSafety(t *testing.T) {
})
return nil
})
- assert.EqualValues(t, testCount, actualCount)
+ assert.Equal(t, testCount, actualCount)
// deny the bad usages
assert.PanicsWithError(t, "using database context in an iterator would cause corrupted results", func() {
diff --git a/models/db/engine.go b/models/db/engine.go
index 91015f7038..ba287d58f0 100755
--- a/models/db/engine.go
+++ b/models/db/engine.go
@@ -127,7 +127,7 @@ func IsTableNotEmpty(beanOrTableName any) (bool, error) {
// DeleteAllRecords will delete all the records of this table
func DeleteAllRecords(tableName string) error {
- _, err := xormEngine.Exec(fmt.Sprintf("DELETE FROM %s", tableName))
+ _, err := xormEngine.Exec("DELETE FROM " + tableName)
return err
}
diff --git a/models/db/engine_hook.go b/models/db/engine_hook.go
index b4c543c3dd..8709a2c2a1 100644
--- a/models/db/engine_hook.go
+++ b/models/db/engine_hook.go
@@ -7,28 +7,41 @@ import (
"context"
"time"
+ "code.gitea.io/gitea/modules/gtprof"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
"xorm.io/xorm/contexts"
)
-type SlowQueryHook struct {
+type EngineHook struct {
Threshold time.Duration
Logger log.Logger
}
-var _ contexts.Hook = (*SlowQueryHook)(nil)
+var _ contexts.Hook = (*EngineHook)(nil)
-func (*SlowQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
- return c.Ctx, nil
+func (*EngineHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
+ ctx, _ := gtprof.GetTracer().Start(c.Ctx, gtprof.TraceSpanDatabase)
+ return ctx, nil
}
-func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error {
+func (h *EngineHook) AfterProcess(c *contexts.ContextHook) error {
+ span := gtprof.GetContextSpan(c.Ctx)
+ if span != nil {
+ // Do not record SQL parameters here:
+ // * It shouldn't expose the parameters because they contain sensitive information, end users need to report the trace details safely.
+ // * Some parameters contain quite long texts, waste memory and are difficult to display.
+ span.SetAttributeString(gtprof.TraceAttrDbSQL, c.SQL)
+ span.End()
+ } else {
+ setting.PanicInDevOrTesting("span in database engine hook is nil")
+ }
if c.ExecuteTime >= h.Threshold {
// 8 is the amount of skips passed to runtime.Caller, so that in the log the correct function
// is being displayed (the function that ultimately wants to execute the query in the code)
// instead of the function of the slow query hook being called.
- h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
+ h.Logger.Log(8, &log.Event{Level: log.WARN}, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
}
return nil
}
diff --git a/models/db/engine_init.go b/models/db/engine_init.go
index da85018957..bb02aff274 100644
--- a/models/db/engine_init.go
+++ b/models/db/engine_init.go
@@ -42,9 +42,10 @@ func newXORMEngine() (*xorm.Engine, error) {
if err != nil {
return nil, err
}
- if setting.Database.Type == "mysql" {
+ switch setting.Database.Type {
+ case "mysql":
engine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"})
- } else if setting.Database.Type == "mssql" {
+ case "mssql":
engine.Dialect().SetParams(map[string]string{"DEFAULT_VARCHAR": "nvarchar"})
}
engine.SetSchema(setting.Database.Schema)
@@ -72,7 +73,7 @@ func InitEngine(ctx context.Context) error {
xe.SetDefaultContext(ctx)
if setting.Database.SlowQueryThreshold > 0 {
- xe.AddHook(&SlowQueryHook{
+ xe.AddHook(&EngineHook{
Threshold: setting.Database.SlowQueryThreshold,
Logger: log.GetLogger("xorm"),
})
@@ -105,7 +106,7 @@ func UnsetDefaultEngine() {
// When called from the "doctor" command, the migration function is a version check
// that prevents the doctor from fixing anything in the database if the migration level
// is different from the expected value.
-func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err error) {
+func InitEngineWithMigration(ctx context.Context, migrateFunc func(context.Context, *xorm.Engine) error) (err error) {
if err = InitEngine(ctx); err != nil {
return err
}
@@ -122,7 +123,7 @@ func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine)
// Installation should only be being re-run if users want to recover an old database.
// However, we should think carefully about should we support re-install on an installed instance,
// as there may be other problems due to secret reinitialization.
- if err = migrateFunc(xormEngine); err != nil {
+ if err = migrateFunc(ctx, xormEngine); err != nil {
return fmt.Errorf("migrate: %w", err)
}
diff --git a/models/db/engine_test.go b/models/db/engine_test.go
index 10a1a33ff0..a236f83735 100644
--- a/models/db/engine_test.go
+++ b/models/db/engine_test.go
@@ -52,7 +52,7 @@ func TestDeleteOrphanedObjects(t *testing.T) {
countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{})
assert.NoError(t, err)
- assert.EqualValues(t, countBefore, countAfter)
+ assert.Equal(t, countBefore, countAfter)
}
func TestPrimaryKeys(t *testing.T) {
diff --git a/models/db/error.go b/models/db/error.go
index 665e970e17..d47c7adac4 100644
--- a/models/db/error.go
+++ b/models/db/error.go
@@ -65,7 +65,7 @@ func (err ErrNotExist) Error() string {
if err.ID != 0 {
return fmt.Sprintf("%s does not exist [id: %d]", name, err.ID)
}
- return fmt.Sprintf("%s does not exist", name)
+ return name + " does not exist"
}
// Unwrap unwraps this as a ErrNotExist err
diff --git a/models/db/list_test.go b/models/db/list_test.go
index 45194611f8..170473a968 100644
--- a/models/db/list_test.go
+++ b/models/db/list_test.go
@@ -47,6 +47,6 @@ func TestFind(t *testing.T) {
repoUnits, newCnt, err := db.FindAndCount[repo_model.RepoUnit](db.DefaultContext, opts)
assert.NoError(t, err)
- assert.EqualValues(t, cnt, newCnt)
+ assert.Equal(t, cnt, newCnt)
assert.Len(t, repoUnits, repoUnitCount)
}
diff --git a/models/db/log.go b/models/db/log.go
index 307788ea2e..a9df6f541d 100644
--- a/models/db/log.go
+++ b/models/db/log.go
@@ -29,7 +29,7 @@ const stackLevel = 8
// Log a message with defined skip and at logging level
func (l *XORMLogBridge) Log(skip int, level log.Level, format string, v ...any) {
- l.logger.Log(skip+1, level, format, v...)
+ l.logger.Log(skip+1, &log.Event{Level: level}, format, v...)
}
// Debug show debug log
diff --git a/models/db/name.go b/models/db/name.go
index 5f98edbb28..48c7fdbce5 100644
--- a/models/db/name.go
+++ b/models/db/name.go
@@ -5,14 +5,13 @@ package db
import (
"fmt"
+ "slices"
"strings"
"unicode/utf8"
"code.gitea.io/gitea/modules/util"
)
-var ErrNameEmpty = util.SilentWrap{Message: "name is empty", Err: util.ErrInvalidArgument}
-
// ErrNameReserved represents a "reserved name" error.
type ErrNameReserved struct {
Name string
@@ -79,13 +78,11 @@ func (err ErrNameCharsNotAllowed) Unwrap() error {
func IsUsableName(reservedNames, reservedPatterns []string, name string) error {
name = strings.TrimSpace(strings.ToLower(name))
if utf8.RuneCountInString(name) == 0 {
- return ErrNameEmpty
+ return util.NewInvalidArgumentErrorf("name is empty")
}
- for i := range reservedNames {
- if name == reservedNames[i] {
- return ErrNameReserved{name}
- }
+ if slices.Contains(reservedNames, name) {
+ return ErrNameReserved{name}
}
for _, pat := range reservedPatterns {
diff --git a/models/db/search.go b/models/db/search.go
index e0a1b6bde9..44d54f21fc 100644
--- a/models/db/search.go
+++ b/models/db/search.go
@@ -29,7 +29,3 @@ const (
// NoConditionID means a condition to filter the records which don't match any id.
// eg: "milestone_id=-1" means "find the items without any milestone.
const NoConditionID int64 = -1
-
-// NonExistingID means a condition to match no result (eg: a non-existing user)
-// It doesn't use -1 or -2 because they are used as builtin users.
-const NonExistingID int64 = -1000000
diff --git a/models/db/sql_postgres_with_schema.go b/models/db/sql_postgres_with_schema.go
index 64b61b2ef3..812fe4a6a6 100644
--- a/models/db/sql_postgres_with_schema.go
+++ b/models/db/sql_postgres_with_schema.go
@@ -39,7 +39,7 @@ func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) {
// golangci lint is incorrect here - there is no benefit to using driver.ExecerContext here
// and in any case pq does not implement it
- if execer, ok := conn.(driver.Execer); ok { //nolint:staticcheck
+ if execer, ok := conn.(driver.Execer); ok { //nolint:staticcheck // see above
_, err := execer.Exec(`SELECT set_config(
'search_path',
$1 || ',' || current_setting('search_path'),
@@ -64,7 +64,7 @@ func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) {
// driver.String.ConvertValue will never return err for string
// golangci lint is incorrect here - there is no benefit to using stmt.ExecWithContext here
- _, err = stmt.Exec([]driver.Value{schemaValue}) //nolint:staticcheck
+ _, err = stmt.Exec([]driver.Value{schemaValue}) //nolint:staticcheck // see above
if err != nil {
_ = conn.Close()
return nil, err