Skip to content

Commit

Permalink
feat: escape backticks (#42)
Browse files Browse the repository at this point in the history
* feat: escape backticks

* perf: improve quote performance

* style: update benchmark

Co-authored-by: humodi <humodi@bytedance.com>
  • Loading branch information
tr1v3r and humodi authored Aug 4, 2021
1 parent 231fa72 commit 2371757
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 20 deletions.
63 changes: 43 additions & 20 deletions mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,32 +203,55 @@ func (dialector Dialector) BindVarTo(writer clause.Writer, stmt *gorm.Statement,
}

func (dialector Dialector) QuoteTo(writer clause.Writer, str string) {
if str == "*" {
writer.WriteString(str)
return
}

writer.WriteByte('`')
if strings.Contains(str, ".") {
for idx, str := range strings.Split(str, ".") {
if str == "*" {
if idx > 0 {
writer.WriteString(".")
var (
unfinishedBacktick int
continuousBacktick int
shiftDelimiter int
selfQuoted = 0
)

for _, v := range []byte(str) {
switch v {
case '`':
continuousBacktick++
if continuousBacktick == 2 {
writer.WriteString("``")
continuousBacktick = 0
}
case '.':
shiftDelimiter = 0
if continuousBacktick > 0 || selfQuoted == 0 {
unfinishedBacktick = 0
continuousBacktick = 0
writer.WriteString("`")
}
writer.WriteByte(v)
continue
default:
if shiftDelimiter-continuousBacktick <= 0 && unfinishedBacktick == 0 {
writer.WriteByte('`')
unfinishedBacktick = 1
if continuousBacktick > 0 {
continuousBacktick -= 1
selfQuoted = 1
} else {
selfQuoted = 0
}
writer.WriteString(str)
continue
}

if idx > 0 {
writer.WriteString(".`")
for ; continuousBacktick > 0; continuousBacktick -= 1 {
writer.WriteString("``")
}
writer.WriteString(str)
writer.WriteByte('`')

writer.WriteByte(v)
}
} else {
writer.WriteString(str)
writer.WriteByte('`')
shiftDelimiter++
}

for ; continuousBacktick > selfQuoted; continuousBacktick -= 2 {
writer.WriteString("``")
}
writer.WriteString("`")
}

func (dialector Dialector) Explain(sql string, vars ...interface{}) string {
Expand Down
60 changes: 60 additions & 0 deletions mysql_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package mysql

import (
"bytes"
"testing"
)

func TestDialector_QuoteTo(t *testing.T) {
testdatas := []struct {
raw string
expect string
}{
{"datadase.tableUser", "`datadase`.`tableUser`"},
{"datadase.table`User", "`datadase`.`table``User`"},
{"`a`.`b`", "`a`.`b`"},
{"`a`.b`", "`a`.`b```"},
{"a.`b`", "`a`.`b`"},
{"`a`.b`c", "`a`.`b``c`"},
{"`a`.`b`c`", "`a`.`b``c`"},
{"`a`.b", "`a`.`b`"},
{"`ab`", "`ab`"},
{"`a``b`", "`a``b`"},
{"`a```b`", "`a````b`"},
{"a`b", "`a``b`"},
{"ab", "`ab`"},
{"`a.b`", "`a.b`"},
{"a.b", "`a`.`b`"},
}

dailor := Open("")
for _, item := range testdatas {
buf := &bytes.Buffer{}
dailor.QuoteTo(buf, item.raw)
if buf.String() != item.expect {
t.Errorf("quote %q fail, got %q, expect %q", item.raw, buf.String(), item.expect)
}
}
}

// BenchmarkDialector_QuoteTo
// Result:
// goos: darwin
// goarch: amd64
// pkg: gorm.io/driver/mysql
// cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
// BenchmarkDialector_QuoteTo 9184232 113.2 ns/op
// BenchmarkDialector_QuoteTo-2 9782818 112.3 ns/op
// BenchmarkDialector_QuoteTo-4 10726722 109.0 ns/op
// BenchmarkDialector_QuoteTo-8 9656778 113.1 ns/op
// BenchmarkDialector_QuoteTo-12 10729615 112.7 ns/op
func BenchmarkDialector_QuoteTo(b *testing.B) {
dailor := Open("")
buf := &bytes.Buffer{}

b.ResetTimer()
for i := 0; i < b.N; i++ {
dailor.QuoteTo(buf, "datadase.table`User")
buf.Reset()
}
}

0 comments on commit 2371757

Please sign in to comment.