You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

quote.go 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright 2020 The Xorm Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package schemas
  5. import (
  6. "strings"
  7. )
  8. // Quoter represents a quoter to the SQL table name and column name
  9. type Quoter struct {
  10. Prefix byte
  11. Suffix byte
  12. IsReserved func(string) bool
  13. }
  14. var (
  15. // AlwaysFalseReverse always think it's not a reverse word
  16. AlwaysNoReserve = func(string) bool { return false }
  17. // AlwaysReverse always reverse the word
  18. AlwaysReserve = func(string) bool { return true }
  19. // CommanQuoteMark represnets the common quote mark
  20. CommanQuoteMark byte = '`'
  21. // CommonQuoter represetns a common quoter
  22. CommonQuoter = Quoter{CommanQuoteMark, CommanQuoteMark, AlwaysReserve}
  23. )
  24. func (q Quoter) IsEmpty() bool {
  25. return q.Prefix == 0 && q.Suffix == 0
  26. }
  27. func (q Quoter) Quote(s string) string {
  28. var buf strings.Builder
  29. q.QuoteTo(&buf, s)
  30. return buf.String()
  31. }
  32. // Trim removes quotes from s
  33. func (q Quoter) Trim(s string) string {
  34. if len(s) < 2 {
  35. return s
  36. }
  37. var buf strings.Builder
  38. for i := 0; i < len(s); i++ {
  39. switch {
  40. case i == 0 && s[i] == q.Prefix:
  41. case i == len(s)-1 && s[i] == q.Suffix:
  42. case s[i] == q.Suffix && s[i+1] == '.':
  43. case s[i] == q.Prefix && s[i-1] == '.':
  44. default:
  45. buf.WriteByte(s[i])
  46. }
  47. }
  48. return buf.String()
  49. }
  50. func (q Quoter) Join(a []string, sep string) string {
  51. var b strings.Builder
  52. q.JoinWrite(&b, a, sep)
  53. return b.String()
  54. }
  55. func (q Quoter) JoinWrite(b *strings.Builder, a []string, sep string) error {
  56. if len(a) == 0 {
  57. return nil
  58. }
  59. n := len(sep) * (len(a) - 1)
  60. for i := 0; i < len(a); i++ {
  61. n += len(a[i])
  62. }
  63. b.Grow(n)
  64. for i, s := range a {
  65. if i > 0 {
  66. if _, err := b.WriteString(sep); err != nil {
  67. return err
  68. }
  69. }
  70. if s != "*" {
  71. q.QuoteTo(b, strings.TrimSpace(s))
  72. }
  73. }
  74. return nil
  75. }
  76. func findWord(v string, start int) int {
  77. for j := start; j < len(v); j++ {
  78. switch v[j] {
  79. case '.', ' ':
  80. return j
  81. }
  82. }
  83. return len(v)
  84. }
  85. func findStart(value string, start int) int {
  86. if value[start] == '.' {
  87. return start + 1
  88. }
  89. if value[start] != ' ' {
  90. return start
  91. }
  92. var k = -1
  93. for j := start; j < len(value); j++ {
  94. if value[j] != ' ' {
  95. k = j
  96. break
  97. }
  98. }
  99. if k == -1 {
  100. return len(value)
  101. }
  102. if (value[k] == 'A' || value[k] == 'a') && (value[k+1] == 'S' || value[k+1] == 's') {
  103. k = k + 2
  104. }
  105. for j := k; j < len(value); j++ {
  106. if value[j] != ' ' {
  107. return j
  108. }
  109. }
  110. return len(value)
  111. }
  112. func (q Quoter) quoteWordTo(buf *strings.Builder, word string) error {
  113. var realWord = word
  114. if (word[0] == CommanQuoteMark && word[len(word)-1] == CommanQuoteMark) ||
  115. (word[0] == q.Prefix && word[len(word)-1] == q.Suffix) {
  116. realWord = word[1 : len(word)-1]
  117. }
  118. if q.IsEmpty() {
  119. _, err := buf.WriteString(realWord)
  120. return err
  121. }
  122. isReserved := q.IsReserved(realWord)
  123. if isReserved {
  124. if err := buf.WriteByte(q.Prefix); err != nil {
  125. return err
  126. }
  127. }
  128. if _, err := buf.WriteString(realWord); err != nil {
  129. return err
  130. }
  131. if isReserved {
  132. return buf.WriteByte(q.Suffix)
  133. }
  134. return nil
  135. }
  136. // QuoteTo quotes the table or column names. i.e. if the quotes are [ and ]
  137. // name -> [name]
  138. // `name` -> [name]
  139. // [name] -> [name]
  140. // schema.name -> [schema].[name]
  141. // `schema`.`name` -> [schema].[name]
  142. // `schema`.name -> [schema].[name]
  143. // schema.`name` -> [schema].[name]
  144. // [schema].name -> [schema].[name]
  145. // schema.[name] -> [schema].[name]
  146. // name AS a -> [name] AS a
  147. // schema.name AS a -> [schema].[name] AS a
  148. func (q Quoter) QuoteTo(buf *strings.Builder, value string) error {
  149. var i int
  150. for i < len(value) {
  151. start := findStart(value, i)
  152. if start > i {
  153. if _, err := buf.WriteString(value[i:start]); err != nil {
  154. return err
  155. }
  156. }
  157. if start == len(value) {
  158. return nil
  159. }
  160. var nextEnd = findWord(value, start)
  161. if err := q.quoteWordTo(buf, value[start:nextEnd]); err != nil {
  162. return err
  163. }
  164. i = nextEnd
  165. }
  166. return nil
  167. }
  168. // Strings quotes a slice of string
  169. func (q Quoter) Strings(s []string) []string {
  170. var res = make([]string, 0, len(s))
  171. for _, a := range s {
  172. res = append(res, q.Quote(a))
  173. }
  174. return res
  175. }
  176. // Replace replaces common quote(`) as the quotes on the sql
  177. func (q Quoter) Replace(sql string) string {
  178. if q.IsEmpty() {
  179. return sql
  180. }
  181. var buf strings.Builder
  182. buf.Grow(len(sql))
  183. var beginSingleQuote bool
  184. for i := 0; i < len(sql); i++ {
  185. if !beginSingleQuote && sql[i] == CommanQuoteMark {
  186. var j = i + 1
  187. for ; j < len(sql); j++ {
  188. if sql[j] == CommanQuoteMark {
  189. break
  190. }
  191. }
  192. word := sql[i+1 : j]
  193. isReserved := q.IsReserved(word)
  194. if isReserved {
  195. buf.WriteByte(q.Prefix)
  196. }
  197. buf.WriteString(word)
  198. if isReserved {
  199. buf.WriteByte(q.Suffix)
  200. }
  201. i = j
  202. } else {
  203. if sql[i] == '\'' {
  204. beginSingleQuote = !beginSingleQuote
  205. }
  206. buf.WriteByte(sql[i])
  207. }
  208. }
  209. return buf.String()
  210. }