mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 11:28:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			239 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
| // 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()
 | |
| }
 |