Skip to content
This repository has been archived by the owner on Sep 7, 2021. It is now read-only.
This repository is currently being migrated. It's locked while the migration is in progress.

Add quote policy support for table name and column name #1435

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 103 additions & 21 deletions .drone.yml

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dialect_mssql.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ func (db *mssql) SupportInsertMany() bool {
}

func (db *mssql) IsReserved(name string) bool {
_, ok := mssqlReservedWords[name]
_, ok := mssqlReservedWords[strings.ToUpper(name)]
return ok
}

Expand Down Expand Up @@ -533,7 +533,7 @@ func (db *mssql) ForUpdateSql(query string) string {
}

func (db *mssql) Filters() []core.Filter {
return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}}
return []core.Filter{&core.QuoteFilter{}}
}

type odbcDriver struct {
Expand Down
4 changes: 2 additions & 2 deletions dialect_mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ func (db *mysql) SupportInsertMany() bool {
}

func (db *mysql) IsReserved(name string) bool {
_, ok := mysqlReservedWords[name]
_, ok := mysqlReservedWords[strings.ToUpper(name)]
return ok
}

Expand Down Expand Up @@ -560,7 +560,7 @@ func (db *mysql) CreateTableSql(table *core.Table, tableName, storeEngine, chars
}

func (db *mysql) Filters() []core.Filter {
return []core.Filter{&core.IdFilter{}}
return []core.Filter{}
}

type mymysqlDriver struct {
Expand Down
4 changes: 2 additions & 2 deletions dialect_oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ func (db *oracle) SupportInsertMany() bool {
}

func (db *oracle) IsReserved(name string) bool {
_, ok := oracleReservedWords[name]
_, ok := oracleReservedWords[strings.ToUpper(name)]
return ok
}

Expand Down Expand Up @@ -847,7 +847,7 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) {
}

func (db *oracle) Filters() []core.Filter {
return []core.Filter{&core.QuoteFilter{}, &core.SeqFilter{Prefix: ":", Start: 1}, &core.IdFilter{}}
return []core.Filter{&core.QuoteFilter{}, &core.SeqFilter{Prefix: ":", Start: 1}}
}

type goracleDriver struct {
Expand Down
4 changes: 2 additions & 2 deletions dialect_postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@ func (db *postgres) SupportInsertMany() bool {
}

func (db *postgres) IsReserved(name string) bool {
_, ok := postgresReservedWords[name]
_, ok := postgresReservedWords[strings.ToUpper(name)]
return ok
}

Expand Down Expand Up @@ -1160,7 +1160,7 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error)
}

func (db *postgres) Filters() []core.Filter {
return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}, &core.SeqFilter{Prefix: "$", Start: 1}}
return []core.Filter{&core.QuoteFilter{}, &core.SeqFilter{Prefix: "$", Start: 1}}
}

type pqDriver struct {
Expand Down
4 changes: 2 additions & 2 deletions dialect_sqlite3.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ func (db *sqlite3) SupportInsertMany() bool {
}

func (db *sqlite3) IsReserved(name string) bool {
_, ok := sqlite3ReservedWords[name]
_, ok := sqlite3ReservedWords[strings.ToUpper(name)]
return ok
}

Expand Down Expand Up @@ -477,7 +477,7 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error)
}

func (db *sqlite3) Filters() []core.Filter {
return []core.Filter{&core.IdFilter{}}
return []core.Filter{}
}

type sqlite3Driver struct {
Expand Down
69 changes: 8 additions & 61 deletions engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ type Engine struct {
cacherLock sync.RWMutex

defaultContext context.Context

colQuoter Quoter
tableQuoter Quoter
}

func (engine *Engine) setCacher(tableName string, cacher core.Cacher) {
Expand Down Expand Up @@ -175,64 +178,6 @@ func (engine *Engine) SupportInsertMany() bool {
return engine.dialect.SupportInsertMany()
}

func (engine *Engine) quoteColumns(columnStr string) string {
columns := strings.Split(columnStr, ",")
for i := 0; i < len(columns); i++ {
columns[i] = engine.Quote(strings.TrimSpace(columns[i]))
}
return strings.Join(columns, ",")
}

// Quote Use QuoteStr quote the string sql
func (engine *Engine) Quote(value string) string {
value = strings.TrimSpace(value)
if len(value) == 0 {
return value
}

buf := strings.Builder{}
engine.QuoteTo(&buf, value)

return buf.String()
}

// QuoteTo quotes string and writes into the buffer
func (engine *Engine) QuoteTo(buf *strings.Builder, value string) {
if buf == nil {
return
}

value = strings.TrimSpace(value)
if value == "" {
return
}

quotePair := engine.dialect.Quote("")

if value[0] == '`' || len(quotePair) < 2 || value[0] == quotePair[0] { // no quote
_, _ = buf.WriteString(value)
return
} else {
prefix, suffix := quotePair[0], quotePair[1]

_ = buf.WriteByte(prefix)
for i := 0; i < len(value); i++ {
if value[i] == '.' {
_ = buf.WriteByte(suffix)
_ = buf.WriteByte('.')
_ = buf.WriteByte(prefix)
} else {
_ = buf.WriteByte(value[i])
}
}
_ = buf.WriteByte(suffix)
}
}

func (engine *Engine) quote(sql string) string {
return engine.dialect.Quote(sql)
}

// SqlType will be deprecated, please use SQLType instead
//
// Deprecated: use SQLType instead
Expand Down Expand Up @@ -474,6 +419,8 @@ func (engine *Engine) dumpTables(tables []*core.Table, w io.Writer, tp ...core.D
return err
}

colQuoter := newQuoter(dialect, engine.colQuoter.QuotePolicy())

for i, table := range tables {
if i > 0 {
_, err = io.WriteString(w, "\n")
Expand All @@ -493,10 +440,10 @@ func (engine *Engine) dumpTables(tables []*core.Table, w io.Writer, tp ...core.D
}

cols := table.ColumnsSeq()
colNames := engine.dialect.Quote(strings.Join(cols, engine.dialect.Quote(", ")))
destColNames := dialect.Quote(strings.Join(cols, dialect.Quote(", ")))
colNames := quoteJoin(engine.colQuoter, cols)
destColNames := quoteJoin(colQuoter, cols)

rows, err := engine.DB().Query("SELECT " + colNames + " FROM " + engine.Quote(table.Name))
rows, err := engine.DB().Query("SELECT " + colNames + " FROM " + engine.quote(table.Name, false))
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions engine_cond.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ func (engine *Engine) buildConds(table *core.Table, bean interface{},
if len(aliasName) > 0 {
nm = aliasName
}
colName = engine.Quote(nm) + "." + engine.Quote(col.Name)
colName = engine.quote(nm, false) + "." + engine.quote(col.Name, true)
} else {
colName = engine.Quote(col.Name)
colName = engine.quote(col.Name, true)
}

fieldValuePtr, err := col.ValueOf(bean)
Expand Down
186 changes: 186 additions & 0 deletions engine_quote.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// Copyright 2019 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 xorm

import (
"fmt"
"strings"

"xorm.io/builder"
"xorm.io/core"
)

// QuotePolicy describes quote handle policy
type QuotePolicy int

// All QuotePolicies
const (
QuotePolicyAlways QuotePolicy = iota
QuotePolicyNone
QuotePolicyReserved
)

// Quoter represents an object has Quote method
type Quoter interface {
Quotes() (byte, byte)
QuotePolicy() QuotePolicy
IsReserved(string) bool
WriteTo(w *builder.BytesWriter, value string) error
}

type quoter struct {
dialect core.Dialect
quotePolicy QuotePolicy
}

func newQuoter(dialect core.Dialect, quotePolicy QuotePolicy) Quoter {
return &quoter{
dialect: dialect,
quotePolicy: quotePolicy,
}
}

func (q *quoter) Quotes() (byte, byte) {
quotes := q.dialect.Quote("")
return quotes[0], quotes[1]
}

func (q *quoter) QuotePolicy() QuotePolicy {
return q.quotePolicy
}

func (q *quoter) IsReserved(value string) bool {
return q.dialect.IsReserved(value)
}

func (q *quoter) needQuote(value string) bool {
return q.quotePolicy == QuotePolicyAlways || (q.quotePolicy == QuotePolicyReserved && q.IsReserved(value))
}

func (q *quoter) WriteTo(w *builder.BytesWriter, name string) error {
leftQuote, rightQuote := q.Quotes()
needQuote := q.needQuote(name)
if needQuote && name[0] != '`' {
if err := w.WriteByte(leftQuote); err != nil {
return err
}
}
if _, err := w.WriteString(name); err != nil {
return err
}
if needQuote && name[len(name)-1] != '`' {
if err := w.WriteByte(rightQuote); err != nil {
return err
}
}
return nil
}

func quoteColumns(quoter Quoter, columnStr string) string {
columns := strings.Split(columnStr, ",")
return quoteJoin(quoter, columns)
}

func quoteJoin(quoter Quoter, columns []string) string {
for i := 0; i < len(columns); i++ {
columns[i] = quote(quoter, columns[i])
}
return strings.Join(columns, ",")
}

// quote Use QuoteStr quote the string sql
func quote(quoter Quoter, value string) string {
buf := strings.Builder{}
quoteTo(quoter, &buf, value)
return buf.String()
}

// Quote add quotes to the value
func (engine *Engine) quote(value string, isColumn bool) string {
if isColumn {
return quote(engine.colQuoter, value)
}
return quote(engine.tableQuoter, value)
}

// Quote add quotes to the value
func (engine *Engine) Quote(value string, isColumn bool) string {
return engine.quote(value, isColumn)
}

// Quotes return the left quote and right quote
func (engine *Engine) Quotes() (byte, byte) {
quotes := engine.dialect.Quote("")
return quotes[0], quotes[1]
}

// IsReserved return true if the value is a reserved word of the database
func (engine *Engine) IsReserved(value string) bool {
return engine.dialect.IsReserved(value)
}

// SetTableQuotePolicy set table quote policy
func (engine *Engine) SetTableQuotePolicy(policy QuotePolicy) {
engine.tableQuoter = newQuoter(engine.dialect, policy)
}

// SetColumnQuotePolicy set column quote policy
func (engine *Engine) SetColumnQuotePolicy(policy QuotePolicy) {
engine.colQuoter = newQuoter(engine.dialect, policy)
}

// quoteTo quotes string and writes into the buffer
func quoteTo(quoter Quoter, buf *strings.Builder, value string) {
left, right := quoter.Quotes()
if (quoter.QuotePolicy() == QuotePolicyAlways) ||
(quoter.QuotePolicy() == QuotePolicyReserved && quoter.IsReserved(value)) {
realQuoteTo(left, right, buf, value)
return
}
buf.WriteString(value)
}

func realQuoteTo(quoteLeft, quoteRight byte, buf *strings.Builder, value string) {
if buf == nil {
return
}

value = strings.TrimSpace(value)
if value == "" {
return
} else if value == "*" {
buf.WriteString("*")
return
}

if value[0] == '`' || value[0] == quoteLeft { // no quote
_, _ = buf.WriteString(value)
return
} else {
_ = buf.WriteByte(quoteLeft)
for i := 0; i < len(value); i++ {
if value[i] == '.' {
_ = buf.WriteByte(quoteRight)
_ = buf.WriteByte('.')
_ = buf.WriteByte(quoteLeft)
} else {
_ = buf.WriteByte(value[i])
}
}
_ = buf.WriteByte(quoteRight)
}
}

func unQuote(quoter Quoter, value string) string {
left, right := quoter.Quotes()
return strings.Trim(value, fmt.Sprintf("%v%v`", left, right))
}

func quoteJoinFunc(cols []string, quoteFunc func(string) string, sep string) string {
for i := range cols {
cols[i] = quoteFunc(cols[i])
}
return strings.Join(cols, sep+" ")
}
Loading