d28f005552
Fix limit for databases other than sqlite go mod tidy && go mod vendor Remove unneeded break statements Make everything work with the new xorm version Fix xorm logging Fix lint Fix redis init Fix using id field Fix database init for testing Change default database log level Add xorm logger Use const for postgres go mod tidy Merge branch 'master' into update/xorm # Conflicts: # go.mod # go.sum # vendor/modules.txt go mod vendor Fix loading fixtures for postgres Go mod vendor1 Update xorm to version 1 Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/323
240 lines
4.8 KiB
Go
240 lines
4.8 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 (
|
|
"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
|
|
}
|
|
}
|
|
if s != "*" {
|
|
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 {
|
|
if err := buf.WriteByte(q.Prefix); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if _, err := buf.WriteString(realWord); err != nil {
|
|
return err
|
|
}
|
|
if isReserved {
|
|
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()
|
|
}
|