166 lines
3.4 KiB
Go
166 lines
3.4 KiB
Go
// Copyright 2020 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 schemas
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Quoter represents two quote characters
|
|
type Quoter [2]string
|
|
|
|
// CommonQuoter represetns a common quoter
|
|
var CommonQuoter = Quoter{"`", "`"}
|
|
|
|
func (q Quoter) IsEmpty() bool {
|
|
return q[0] == "" && q[1] == ""
|
|
}
|
|
|
|
func (q Quoter) Quote(s string) string {
|
|
var buf strings.Builder
|
|
q.QuoteTo(&buf, s)
|
|
return buf.String()
|
|
}
|
|
|
|
func (q Quoter) Replace(sql string, newQuoter Quoter) string {
|
|
if q.IsEmpty() {
|
|
return sql
|
|
}
|
|
|
|
if newQuoter.IsEmpty() {
|
|
var buf strings.Builder
|
|
for i := 0; i < len(sql); i = i + 1 {
|
|
if sql[i] != q[0][0] && sql[i] != q[1][0] {
|
|
_ = buf.WriteByte(sql[i])
|
|
}
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
prefix, suffix := newQuoter[0][0], newQuoter[1][0]
|
|
var buf strings.Builder
|
|
for i, cnt := 0, 0; i < len(sql); i = i + 1 {
|
|
if cnt == 0 && sql[i] == q[0][0] {
|
|
_ = buf.WriteByte(prefix)
|
|
cnt = 1
|
|
} else if cnt == 1 && sql[i] == q[1][0] {
|
|
_ = buf.WriteByte(suffix)
|
|
cnt = 0
|
|
} else {
|
|
_ = buf.WriteByte(sql[i])
|
|
}
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
func (q Quoter) ReverseQuote(s string) string {
|
|
reverseQuoter := Quoter{q[1], q[0]}
|
|
return reverseQuoter.Quote(s)
|
|
}
|
|
|
|
// Trim removes quotes from s
|
|
func (q Quoter) Trim(s string) string {
|
|
if len(s) < 2 {
|
|
return s
|
|
}
|
|
|
|
if s[0:1] == q[0] {
|
|
s = s[1:]
|
|
}
|
|
if len(s) > 0 && s[len(s)-1:] == q[0] {
|
|
return s[:len(s)-1]
|
|
}
|
|
return s
|
|
}
|
|
|
|
func TrimSpaceJoin(a []string, sep string) string {
|
|
switch len(a) {
|
|
case 0:
|
|
return ""
|
|
case 1:
|
|
return a[0]
|
|
}
|
|
n := len(sep) * (len(a) - 1)
|
|
for i := 0; i < len(a); i++ {
|
|
n += len(a[i])
|
|
}
|
|
|
|
var b strings.Builder
|
|
b.Grow(n)
|
|
b.WriteString(strings.TrimSpace(a[0]))
|
|
for _, s := range a[1:] {
|
|
b.WriteString(sep)
|
|
b.WriteString(strings.TrimSpace(s))
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func (q Quoter) Join(s []string, splitter string) string {
|
|
//return fmt.Sprintf("%s%s%s", q[0], TrimSpaceJoin(s, fmt.Sprintf("%s%s%s", q[1], splitter, q[0])), q[1])
|
|
return q.Quote(TrimSpaceJoin(s, fmt.Sprintf("%s%s%s", q[1], splitter, q[0])))
|
|
}
|
|
|
|
func (q Quoter) QuoteTo(buf *strings.Builder, value string) {
|
|
if q.IsEmpty() {
|
|
buf.WriteString(value)
|
|
return
|
|
}
|
|
|
|
prefix, suffix := q[0][0], q[1][0]
|
|
lastCh := 0 // 0 prefix, 1 char, 2 suffix
|
|
i := 0
|
|
for i < len(value) {
|
|
// start of a token; might be already quoted
|
|
if value[i] == '.' {
|
|
_ = buf.WriteByte('.')
|
|
lastCh = 1
|
|
i++
|
|
} else if value[i] == prefix || value[i] == '`' {
|
|
// Has quotes; skip/normalize `name` to prefix+name+sufix
|
|
var ch byte
|
|
if value[i] == prefix {
|
|
ch = suffix
|
|
} else {
|
|
ch = '`'
|
|
}
|
|
_ = buf.WriteByte(prefix)
|
|
i++
|
|
lastCh = 0
|
|
for ; i < len(value) && value[i] != ch && value[i] != ' '; i++ {
|
|
_ = buf.WriteByte(value[i])
|
|
lastCh = 1
|
|
}
|
|
_ = buf.WriteByte(suffix)
|
|
lastCh = 2
|
|
i++
|
|
} else if value[i] == ' ' {
|
|
if lastCh != 2 {
|
|
_ = buf.WriteByte(suffix)
|
|
lastCh = 2
|
|
}
|
|
|
|
// a AS b or a b
|
|
for ; i < len(value); i++ {
|
|
if value[i] != ' ' && value[i-1] == ' ' && (len(value) > i+1 && !strings.EqualFold(value[i:i+2], "AS")) {
|
|
break
|
|
}
|
|
|
|
_ = buf.WriteByte(value[i])
|
|
lastCh = 1
|
|
}
|
|
} else {
|
|
// Requires quotes
|
|
_ = buf.WriteByte(prefix)
|
|
for ; i < len(value) && value[i] != '.' && value[i] != ' '; i++ {
|
|
_ = buf.WriteByte(value[i])
|
|
lastCh = 1
|
|
}
|
|
_ = buf.WriteByte(suffix)
|
|
lastCh = 2
|
|
}
|
|
}
|
|
}
|