123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- // 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 (
- "strings"
- )
- // Quoter represents a quoter to the SQL table name and column name
- type Quoter struct {
- Prefix byte
- Suffix byte
- IsReserved func(string) bool
- }
- var (
- // AlwaysFalseReverse always think it's not a reverse word
- AlwaysNoReserve = func(string) bool { return false }
- // AlwaysReverse always reverse the word
- AlwaysReserve = func(string) bool { return true }
- // CommanQuoteMark represnets the common quote mark
- CommanQuoteMark byte = '`'
- // CommonQuoter represetns a common quoter
- CommonQuoter = Quoter{CommanQuoteMark, CommanQuoteMark, AlwaysReserve}
- )
- func (q Quoter) IsEmpty() bool {
- return q.Prefix == 0 && q.Suffix == 0
- }
- func (q Quoter) Quote(s string) string {
- var buf strings.Builder
- q.QuoteTo(&buf, s)
- return buf.String()
- }
- // Trim removes quotes from s
- func (q Quoter) Trim(s string) string {
- if len(s) < 2 {
- return s
- }
- var buf strings.Builder
- for i := 0; i < len(s); i++ {
- switch {
- case i == 0 && s[i] == q.Prefix:
- case i == len(s)-1 && s[i] == q.Suffix:
- case s[i] == q.Suffix && s[i+1] == '.':
- case s[i] == q.Prefix && s[i-1] == '.':
- default:
- buf.WriteByte(s[i])
- }
- }
- return buf.String()
- }
- func (q Quoter) Join(a []string, sep string) string {
- var b strings.Builder
- q.JoinWrite(&b, a, sep)
- return b.String()
- }
- func (q Quoter) JoinWrite(b *strings.Builder, a []string, sep string) error {
- if len(a) == 0 {
- return nil
- }
- n := len(sep) * (len(a) - 1)
- for i := 0; i < len(a); i++ {
- n += len(a[i])
- }
- b.Grow(n)
- for i, s := range a {
- if i > 0 {
- if _, err := b.WriteString(sep); err != nil {
- return err
- }
- }
- q.QuoteTo(b, strings.TrimSpace(s))
- }
- return nil
- }
- func findWord(v string, start int) int {
- for j := start; j < len(v); j++ {
- switch v[j] {
- case '.', ' ':
- return j
- }
- }
- return len(v)
- }
- func findStart(value string, start int) int {
- if value[start] == '.' {
- return start + 1
- }
- if value[start] != ' ' {
- return start
- }
- var k = -1
- for j := start; j < len(value); j++ {
- if value[j] != ' ' {
- k = j
- break
- }
- }
- if k == -1 {
- return len(value)
- }
- if (value[k] == 'A' || value[k] == 'a') && (value[k+1] == 'S' || value[k+1] == 's') {
- k = k + 2
- }
- for j := k; j < len(value); j++ {
- if value[j] != ' ' {
- return j
- }
- }
- return len(value)
- }
- func (q Quoter) quoteWordTo(buf *strings.Builder, word string) error {
- var realWord = word
- if (word[0] == CommanQuoteMark && word[len(word)-1] == CommanQuoteMark) ||
- (word[0] == q.Prefix && word[len(word)-1] == q.Suffix) {
- realWord = word[1 : len(word)-1]
- }
- if q.IsEmpty() {
- _, err := buf.WriteString(realWord)
- return err
- }
- isReserved := q.IsReserved(realWord)
- if isReserved && realWord != "*" {
- if err := buf.WriteByte(q.Prefix); err != nil {
- return err
- }
- }
- if _, err := buf.WriteString(realWord); err != nil {
- return err
- }
- if isReserved && realWord != "*" {
- return buf.WriteByte(q.Suffix)
- }
- return nil
- }
- // QuoteTo quotes the table or column names. i.e. if the quotes are [ and ]
- // name -> [name]
- // `name` -> [name]
- // [name] -> [name]
- // schema.name -> [schema].[name]
- // `schema`.`name` -> [schema].[name]
- // `schema`.name -> [schema].[name]
- // schema.`name` -> [schema].[name]
- // [schema].name -> [schema].[name]
- // schema.[name] -> [schema].[name]
- // name AS a -> [name] AS a
- // schema.name AS a -> [schema].[name] AS a
- func (q Quoter) QuoteTo(buf *strings.Builder, value string) error {
- var i int
- for i < len(value) {
- start := findStart(value, i)
- if start > i {
- if _, err := buf.WriteString(value[i:start]); err != nil {
- return err
- }
- }
- if start == len(value) {
- return nil
- }
- var nextEnd = findWord(value, start)
- if err := q.quoteWordTo(buf, value[start:nextEnd]); err != nil {
- return err
- }
- i = nextEnd
- }
- return nil
- }
- // Strings quotes a slice of string
- func (q Quoter) Strings(s []string) []string {
- var res = make([]string, 0, len(s))
- for _, a := range s {
- res = append(res, q.Quote(a))
- }
- return res
- }
- // Replace replaces common quote(`) as the quotes on the sql
- func (q Quoter) Replace(sql string) string {
- if q.IsEmpty() {
- return sql
- }
- var buf strings.Builder
- buf.Grow(len(sql))
- var beginSingleQuote bool
- for i := 0; i < len(sql); i++ {
- if !beginSingleQuote && sql[i] == CommanQuoteMark {
- var j = i + 1
- for ; j < len(sql); j++ {
- if sql[j] == CommanQuoteMark {
- break
- }
- }
- word := sql[i+1 : j]
- isReserved := q.IsReserved(word)
- if isReserved {
- buf.WriteByte(q.Prefix)
- }
- buf.WriteString(word)
- if isReserved {
- buf.WriteByte(q.Suffix)
- }
- i = j
- } else {
- if sql[i] == '\'' {
- beginSingleQuote = !beginSingleQuote
- }
- buf.WriteByte(sql[i])
- }
- }
- return buf.String()
- }
|