improve table prefix and quote feature for gdb

This commit is contained in:
John
2019-12-13 15:25:49 +08:00
parent 58a25c6f61
commit 27cf47bcd3
5 changed files with 153 additions and 43 deletions

View File

@ -97,6 +97,7 @@ type DB interface {
getDebug() bool
getPrefix() string
quoteWord(s string) string
quoteString(s string) string
doSetSchema(sqlDb *sql.DB, schema string) error
filterFields(table string, data map[string]interface{}) map[string]interface{}
convertValue(fieldValue []byte, fieldType string) interface{}

View File

@ -15,8 +15,6 @@ import (
"regexp"
"strings"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/os/gcache"
"github.com/gogf/gf/os/gtime"
@ -29,8 +27,8 @@ const (
)
var (
wordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`) // Regular expression object for a word.
lastOperatorReg = regexp.MustCompile(`[<>=]+\s*$`) // Regular expression object for a string which has operator at its tail.
// lastOperatorReg is the regular expression object for a string which has operator at its tail.
lastOperatorReg = regexp.MustCompile(`[<>=]+\s*$`)
)
// 打印SQL对象(仅在debug=true时有效)
@ -607,14 +605,18 @@ func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
return records, nil
}
// 使用关键字操作符转义给定字符串。
// 如果给定的字符串不为单词,那么不转义,直接返回该字符串。
// quoteWord checks given string <s> a word, if true quotes it with security chars of the database
// and returns the quoted string; or else return <s> without any change.
func (bs *dbBase) quoteWord(s string) string {
charLeft, charRight := bs.db.getChars()
if wordReg.MatchString(s) && !gstr.ContainsAny(s, charLeft+charRight) {
return charLeft + s + charRight
}
return s
return doQuoteWord(s, charLeft, charRight)
}
// quoteString quotes string with quote chars. Strings like:
// "user", "user u", "user,user_detail", "user u, user_detail ut", "u.id asc".
func (bs *dbBase) quoteString(s string) string {
charLeft, charRight := bs.db.getChars()
return doQuoteString(s, charLeft, charRight)
}
// 动态切换数据库

View File

@ -14,6 +14,7 @@ import (
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/os/gtime"
"reflect"
"regexp"
"strings"
"time"
@ -45,6 +46,55 @@ const (
ORM_TAG_FOR_PRIMARY = "primary"
)
var (
// quoteWordReg is the regular expression object for a word check.
quoteWordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
)
// doQuoteWord checks given string <s> a word, if true quotes it with <charLeft> and <charRight>
// and returns the quoted string; or else return <s> without any change.
func doQuoteWord(s, charLeft, charRight string) string {
if quoteWordReg.MatchString(s) && !gstr.ContainsAny(s, charLeft+charRight) {
return charLeft + s + charRight
}
return s
}
// doQuoteString quotes string with quote chars. It handles strings like:
// "user", "user u", "user,user_detail", "user u, user_detail ut", "u.id asc".
func doQuoteString(s, charLeft, charRight string) string {
array1 := gstr.SplitAndTrim(s, ",")
for k1, v1 := range array1 {
array2 := gstr.SplitAndTrim(v1, " ")
array3 := gstr.SplitAndTrim(array2[0], ".")
if len(array3) == 1 {
array3[0] = doQuoteWord(array3[0], charLeft, charRight)
} else if len(array3) == 2 {
array3[1] = doQuoteWord(array3[1], charLeft, charRight)
}
array2[0] = gstr.Join(array3, ".")
array1[k1] = gstr.Join(array2, " ")
}
return gstr.Join(array1, ",")
}
// addTablePrefix adds prefix string to the table. It handles strings like:
// "user", "user u", "user,user_detail", "user u, user_detail ut", "user as u, user_detail as ut".
//
// Note that, this should be used before any quoting function calls.
func addTablePrefix(table, prefix string) string {
if prefix == "" {
return table
}
array1 := gstr.SplitAndTrim(table, ",")
for k1, v1 := range array1 {
array2 := gstr.SplitAndTrim(v1, " ")
array2[0] = prefix + array2[0]
array1[k1] = gstr.Join(array2, " ")
}
return gstr.Join(array1, ",")
}
// 获得struct对象对应的where查询条件
func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interface{}) {
array := ([]string)(nil)
@ -135,7 +185,7 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (
if gstr.Pos(newWhere, "?") == -1 {
if lastOperatorReg.MatchString(newWhere) {
newWhere += "?"
} else if wordReg.MatchString(newWhere) {
} else if quoteWordReg.MatchString(newWhere) {
newWhere += "=?"
}
}

View File

@ -11,7 +11,6 @@ import (
"errors"
"fmt"
"reflect"
"strings"
"time"
"github.com/gogf/gf/container/gset"
@ -69,11 +68,8 @@ const (
// The parameter <tables> can be more than one table names, like :
// "user", "user u", "user, user_detail", "user u, user_detail ud"
func (bs *dbBase) Table(table string) *Model {
if !gstr.Contains(table, ",") {
array := gstr.SplitAndTrim(table, " ")
array[0] = bs.db.quoteWord(bs.db.getPrefix() + array[0])
table = gstr.Join(array, " ")
}
table = addTablePrefix(table, bs.db.getPrefix())
table = bs.db.quoteString(table)
return &Model{
db: bs.db,
tablesInit: table,
@ -95,11 +91,8 @@ func (bs *dbBase) From(tables string) *Model {
// Table acts like dbBase.Table except it operates on transaction.
// See dbBase.Table.
func (tx *TX) Table(table string) *Model {
if !gstr.Contains(table, ",") {
array := gstr.SplitAndTrim(table, " ")
array[0] = tx.db.quoteWord(tx.db.getPrefix() + array[0])
table = gstr.Join(array, " ")
}
table = addTablePrefix(table, tx.db.getPrefix())
table = tx.db.quoteString(table)
return &Model{
db: tx.db,
tx: tx,
@ -187,9 +180,8 @@ func (m *Model) getModel() *Model {
// LeftJoin does "LEFT JOIN ... ON ..." statement on the model.
func (m *Model) LeftJoin(table string, on string) *Model {
model := m.getModel()
array := gstr.SplitAndTrim(table, " ")
array[0] = m.db.quoteWord(m.db.getPrefix() + array[0])
table = gstr.Join(array, " ")
table = addTablePrefix(table, m.db.getPrefix())
table = m.db.quoteString(table)
model.tables += fmt.Sprintf(" LEFT JOIN %s ON (%s)", table, on)
return model
}
@ -197,9 +189,8 @@ func (m *Model) LeftJoin(table string, on string) *Model {
// RightJoin does "RIGHT JOIN ... ON ..." statement on the model.
func (m *Model) RightJoin(table string, on string) *Model {
model := m.getModel()
array := gstr.SplitAndTrim(table, " ")
array[0] = m.db.quoteWord(m.db.getPrefix() + array[0])
table = gstr.Join(array, " ")
table = addTablePrefix(table, m.db.getPrefix())
table = m.db.quoteString(table)
model.tables += fmt.Sprintf(" RIGHT JOIN %s ON (%s)", table, on)
return model
}
@ -207,9 +198,8 @@ func (m *Model) RightJoin(table string, on string) *Model {
// InnerJoin does "INNER JOIN ... ON ..." statement on the model.
func (m *Model) InnerJoin(table string, on string) *Model {
model := m.getModel()
array := gstr.SplitAndTrim(table, " ")
array[0] = m.db.quoteWord(m.db.getPrefix() + array[0])
table = gstr.Join(array, " ")
table = addTablePrefix(table, m.db.getPrefix())
table = m.db.quoteString(table)
model.tables += fmt.Sprintf(" INNER JOIN %s ON (%s)", table, on)
return model
}
@ -323,24 +313,14 @@ func (m *Model) Or(where interface{}, args ...interface{}) *Model {
// GroupBy sets the "GROUP BY" statement for the model.
func (m *Model) GroupBy(groupBy string) *Model {
model := m.getModel()
if !gstr.Contains(groupBy, ",") {
array := strings.Split(groupBy, " ")
array[0] = m.db.quoteWord(array[0])
groupBy = strings.Join(array, " ")
}
model.groupBy = groupBy
model.groupBy = m.db.quoteString(groupBy)
return model
}
// OrderBy sets the "ORDER BY" statement for the model.
func (m *Model) OrderBy(orderBy string) *Model {
model := m.getModel()
if !gstr.Contains(orderBy, ",") {
array := strings.Split(orderBy, " ")
array[0] = m.db.quoteWord(array[0])
orderBy = strings.Join(array, " ")
}
model.orderBy = orderBy
model.orderBy = m.db.quoteString(orderBy)
return model
}

View File

@ -0,0 +1,77 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gdb
import (
"github.com/gogf/gf/test/gtest"
"testing"
)
func Test_Func_doQuoteWord(t *testing.T) {
gtest.Case(t, func() {
array := map[string]string{
"user": "`user`",
"user u": "user u",
"user_detail": "`user_detail`",
"user,user_detail": "user,user_detail",
"user u, user_detail ut": "user u, user_detail ut",
"u.id asc": "u.id asc",
"u.id asc, ut.uid desc": "u.id asc, ut.uid desc",
}
for k, v := range array {
gtest.Assert(doQuoteWord(k, "`", "`"), v)
}
})
}
func Test_Func_doQuoteString(t *testing.T) {
gtest.Case(t, func() {
// "user", "user u", "user,user_detail", "user u, user_detail ut", "u.id asc".
array := map[string]string{
"user": "`user`",
"user u": "`user` u",
"user,user_detail": "`user`,`user_detail`",
"user u, user_detail ut": "`user` u,`user_detail` ut",
"u.id asc": "u.`id` asc",
"u.id asc, ut.uid desc": "u.`id` asc,ut.`uid` desc",
}
for k, v := range array {
gtest.Assert(doQuoteString(k, "`", "`"), v)
}
})
}
func Test_Func_addTablePrefix(t *testing.T) {
gtest.Case(t, func() {
prefix := ""
array := map[string]string{
"user": "user",
"user u": "user u",
"user as u": "user as u",
"user,user_detail": "user,user_detail",
"user u, user_detail ut": "user u, user_detail ut",
"user as u, user_detail as ut": "user as u, user_detail as ut",
}
for k, v := range array {
gtest.Assert(addTablePrefix(k, prefix), v)
}
})
gtest.Case(t, func() {
prefix := "gf_"
array := map[string]string{
"user": "gf_user",
"user u": "gf_user u",
"user as u": "gf_user as u",
"user,user_detail": "gf_user,gf_user_detail",
"user u, user_detail ut": "gf_user u,gf_user_detail ut",
"user as u, user_detail as ut": "gf_user as u,gf_user_detail as ut",
}
for k, v := range array {
gtest.Assert(addTablePrefix(k, prefix), v)
}
})
}