Add index hint support (#2375)

Fix #1456

Reviewed-on: https://gitea.com/xorm/xorm/pulls/2375
This commit is contained in:
Lunny Xiao 2023-12-15 02:17:13 +00:00
parent 529a264d8e
commit b571d91858
7 changed files with 86 additions and 2 deletions

View File

@ -11,7 +11,7 @@ import (
) )
func TestSplitColStr(t *testing.T) { func TestSplitColStr(t *testing.T) {
var kases = []struct { kases := []struct {
colStr string colStr string
fields []string fields []string
}{ }{
@ -31,7 +31,7 @@ func TestSplitColStr(t *testing.T) {
colStr: ` id INTEGER not null colStr: ` id INTEGER not null
primary key autoincrement`, primary key autoincrement`,
fields: []string{ fields: []string{
"id", "INTEGER", "not null", "primary", "key", "autoincrement", "id", "INTEGER", "not", "null", "primary", "key", "autoincrement",
}, },
}, },
} }

View File

@ -1433,3 +1433,10 @@ func (engine *Engine) Transaction(f func(*Session) (interface{}, error)) (interf
return result, nil return result, nil
} }
func (engine *Engine) IndexHint(op, indexerOrColName string) *Session {
session := engine.NewSession()
session.isAutoClose = true
session.statement.LastError = session.statement.IndexHint(op, indexerOrColName)
return session
}

View File

@ -0,0 +1,54 @@
// Copyright 2023 The Xorm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package statements
import (
"strings"
"xorm.io/builder"
"xorm.io/xorm/schemas"
)
type ErrInvalidIndexHintOperator struct {
Op string
}
func (e ErrInvalidIndexHintOperator) Error() string {
return "invalid index hint operator: " + e.Op
}
func (statement *Statement) IndexHint(op, indexName string) error {
op = strings.ToUpper(op)
statement.indexHints = append(statement.indexHints, indexHint{
op: op,
indexName: indexName,
})
return nil
}
func (statement *Statement) writeIndexHints(w *builder.BytesWriter) error {
if len(statement.indexHints) == 0 {
return nil
}
switch statement.dialect.URI().DBType {
case schemas.MYSQL:
return statement.writeIndexHintsMySQL(w)
default:
return ErrNotImplemented
}
}
func (statement *Statement) writeIndexHintsMySQL(w *builder.BytesWriter) error {
for _, hint := range statement.indexHints {
if hint.op != "USE" && hint.op != "FORCE" && hint.op != "IGNORE" {
return ErrInvalidIndexHintOperator{Op: hint.op}
}
if err := statement.writeStrings(" ", hint.op, " INDEX(", hint.indexName, ")")(w); err != nil {
return err
}
}
return nil
}

View File

@ -254,6 +254,7 @@ func (statement *Statement) writeSelect(buf *builder.BytesWriter, columnStr stri
return statement.writeMultiple(buf, return statement.writeMultiple(buf,
statement.writeSelectColumns(columnStr), statement.writeSelectColumns(columnStr),
statement.writeFrom, statement.writeFrom,
statement.writeIndexHints,
statement.writeWhere, statement.writeWhere,
statement.writeGroupBy, statement.writeGroupBy,
statement.writeHaving, statement.writeHaving,

View File

@ -41,6 +41,11 @@ type join struct {
args []interface{} args []interface{}
} }
type indexHint struct {
op string
indexName string
}
// Statement save all the sql info for executing SQL // Statement save all the sql info for executing SQL
type Statement struct { type Statement struct {
RefTable *schemas.Table RefTable *schemas.Table
@ -84,6 +89,7 @@ type Statement struct {
BufferSize int BufferSize int
Context contexts.ContextCache Context contexts.ContextCache
LastError error LastError error
indexHints []indexHint
} }
// NewStatement creates a new statement // NewStatement creates a new statement

View File

@ -311,3 +311,8 @@ func (session *Session) Import(r io.Reader) ([]sql.Result, error) {
return results, lastError return results, lastError
} }
func (session *Session) IndexHint(op, indexerOrColName string) *Session {
session.statement.IndexHint(op, indexerOrColName)
return session
}

View File

@ -62,3 +62,14 @@ func TestEnableSessionId(t *testing.T) {
_, err := testEngine.Table("userinfo").MustLogSQL(true).Get(new(Userinfo)) _, err := testEngine.Table("userinfo").MustLogSQL(true).Get(new(Userinfo))
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestIndexHint(t *testing.T) {
assert.NoError(t, PrepareEngine())
assertSync(t, new(Userinfo))
if testEngine.Dialect().URI().DBType != "mysql" {
return
}
_, err := testEngine.Table("userinfo").IndexHint("USE", "UQE_userinfo_username").Get(new(Userinfo))
assert.NoError(t, err)
}