Merge branch 'v1' into feat/driver-customisation
This commit is contained in:
commit
201f14f026
|
@ -33,7 +33,7 @@ jobs:
|
||||||
# restore-keys: |
|
# restore-keys: |
|
||||||
# go_cache-${{ github.repository }}-
|
# go_cache-${{ github.repository }}-
|
||||||
# go_cache-
|
# go_cache-
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: 1.20
|
go-version: 1.20
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
|
@ -33,7 +33,7 @@ jobs:
|
||||||
# restore-keys: |
|
# restore-keys: |
|
||||||
# go_cache-${{ github.repository }}-
|
# go_cache-${{ github.repository }}-
|
||||||
# go_cache-
|
# go_cache-
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: 1.20
|
go-version: 1.20
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
|
@ -33,7 +33,7 @@ jobs:
|
||||||
# restore-keys: |
|
# restore-keys: |
|
||||||
# go_cache-${{ github.repository }}-
|
# go_cache-${{ github.repository }}-
|
||||||
# go_cache-
|
# go_cache-
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: 1.20
|
go-version: 1.20
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
|
@ -33,7 +33,7 @@ jobs:
|
||||||
# restore-keys: |
|
# restore-keys: |
|
||||||
# go_cache-${{ github.repository }}-
|
# go_cache-${{ github.repository }}-
|
||||||
# go_cache-
|
# go_cache-
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: 1.20
|
go-version: 1.20
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
|
@ -33,7 +33,7 @@ jobs:
|
||||||
# restore-keys: |
|
# restore-keys: |
|
||||||
# go_cache-${{ github.repository }}-
|
# go_cache-${{ github.repository }}-
|
||||||
# go_cache-
|
# go_cache-
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: 1.20
|
go-version: 1.20
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
|
@ -33,7 +33,7 @@ jobs:
|
||||||
# restore-keys: |
|
# restore-keys: |
|
||||||
# go_cache-${{ github.repository }}-
|
# go_cache-${{ github.repository }}-
|
||||||
# go_cache-
|
# go_cache-
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: 1.20
|
go-version: 1.20
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
|
@ -33,7 +33,7 @@ jobs:
|
||||||
# restore-keys: |
|
# restore-keys: |
|
||||||
# go_cache-${{ github.repository }}-
|
# go_cache-${{ github.repository }}-
|
||||||
# go_cache-
|
# go_cache-
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: 1.20
|
go-version: 1.20
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
|
@ -33,7 +33,7 @@ jobs:
|
||||||
# restore-keys: |
|
# restore-keys: |
|
||||||
# go_cache-${{ github.repository }}-
|
# go_cache-${{ github.repository }}-
|
||||||
# go_cache-
|
# go_cache-
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: 1.20
|
go-version: 1.20
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"xorm.io/xorm/core"
|
"xorm.io/xorm/core"
|
||||||
"xorm.io/xorm/schemas"
|
"xorm.io/xorm/schemas"
|
||||||
|
@ -320,7 +321,7 @@ func splitColStr(colStr string) []string {
|
||||||
var lastIdx int
|
var lastIdx int
|
||||||
var hasC, hasQuote bool
|
var hasC, hasQuote bool
|
||||||
for i, c := range colStr {
|
for i, c := range colStr {
|
||||||
if c == ' ' && !hasQuote {
|
if unicode.IsSpace(c) && !hasQuote {
|
||||||
if hasC {
|
if hasC {
|
||||||
results = append(results, colStr[lastIdx:i])
|
results = append(results, colStr[lastIdx:i])
|
||||||
hasC = false
|
hasC = false
|
||||||
|
@ -350,7 +351,7 @@ func parseString(colStr string) (*schemas.Column, error) {
|
||||||
|
|
||||||
for idx, field := range fields {
|
for idx, field := range fields {
|
||||||
if idx == 0 {
|
if idx == 0 {
|
||||||
col.Name = strings.Trim(strings.Trim(field, "`[] "), `"`)
|
col.Name = strings.Trim(strings.TrimSpace(field), "`[]'\"")
|
||||||
continue
|
continue
|
||||||
} else if idx == 1 {
|
} else if idx == 1 {
|
||||||
col.SQLType = schemas.SQLType{Name: field, DefaultLength: 0, DefaultLength2: 0}
|
col.SQLType = schemas.SQLType{Name: field, DefaultLength: 0, DefaultLength2: 0}
|
||||||
|
@ -400,6 +401,8 @@ func (db *sqlite3) GetColumns(queryer core.Queryer, ctx context.Context, tableNa
|
||||||
return nil, nil, errors.New("no table named " + tableName)
|
return nil, nil, errors.New("no table named " + tableName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name = strings.ReplaceAll(name, "\n", " ")
|
||||||
|
|
||||||
nStart := strings.Index(name, "(")
|
nStart := strings.Index(name, "(")
|
||||||
nEnd := strings.LastIndex(name, ")")
|
nEnd := strings.LastIndex(name, ")")
|
||||||
reg := regexp.MustCompile(`[^\(,\)]*(\([^\(]*\))?`)
|
reg := regexp.MustCompile(`[^\(,\)]*(\([^\(]*\))?`)
|
||||||
|
@ -483,7 +486,7 @@ func (db *sqlite3) GetIndexes(queryer core.Queryer, ctx context.Context, tableNa
|
||||||
if !tmpSQL.Valid {
|
if !tmpSQL.Valid {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sql := tmpSQL.String
|
sql := strings.ReplaceAll(tmpSQL.String, "\n", " ")
|
||||||
|
|
||||||
index := new(schemas.Index)
|
index := new(schemas.Index)
|
||||||
nNStart := strings.Index(sql, "INDEX")
|
nNStart := strings.Index(sql, "INDEX")
|
||||||
|
|
|
@ -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
|
||||||
}{
|
}{
|
||||||
|
@ -27,6 +27,13 @@ func TestSplitColStr(t *testing.T) {
|
||||||
"`created`", "DATETIME", "DEFAULT", "'2006-01-02 15:04:05'", "NULL",
|
"`created`", "DATETIME", "DEFAULT", "'2006-01-02 15:04:05'", "NULL",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
colStr: ` id INTEGER not null
|
||||||
|
primary key autoincrement`,
|
||||||
|
fields: []string{
|
||||||
|
"id", "INTEGER", "not", "null", "primary", "key", "autoincrement",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, kase := range kases {
|
for _, kase := range kases {
|
||||||
|
|
|
@ -1439,3 +1439,10 @@ func (engine *Engine) Transaction(f func(*Session) (interface{}, error)) (interf
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (engine *Engine) IndexHint(op, forType, indexerOrColName string) *Session {
|
||||||
|
session := engine.NewSession()
|
||||||
|
session.isAutoClose = true
|
||||||
|
session.statement.LastError = session.statement.IndexHint(op, forType, indexerOrColName)
|
||||||
|
return session
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
// 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, forType, indexName string) error {
|
||||||
|
op = strings.ToUpper(op)
|
||||||
|
statement.indexHints = append(statement.indexHints, indexHint{
|
||||||
|
op: op,
|
||||||
|
forType: forType,
|
||||||
|
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")(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if hint.forType != "" {
|
||||||
|
if err := statement.writeStrings(" FOR ", hint.forType)(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := statement.writeStrings("(", hint.indexName, ")")(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -41,6 +41,12 @@ type join struct {
|
||||||
args []interface{}
|
args []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type indexHint struct {
|
||||||
|
op string
|
||||||
|
forType 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 +90,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
|
||||||
|
|
|
@ -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, forType, indexerOrColName string) *Session {
|
||||||
|
session.statement.IndexHint(op, forType, indexerOrColName)
|
||||||
|
return session
|
||||||
|
}
|
||||||
|
|
|
@ -62,3 +62,20 @@ 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)
|
||||||
|
|
||||||
|
_, err = testEngine.Table("userinfo").IndexHint("USE", "ORDER BY", "UQE_userinfo_username").Get(new(Userinfo))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = testEngine.Table("userinfo").IndexHint("USE", "GROUP BY", "UQE_userinfo_username").Get(new(Userinfo))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue