Fix join table name quote bug (#1534)

Fix test

Fix test

Add new Quoter object to handle quote

Fix join table name quote bug

Move reserve words related files into dialects sub package (#1544)

Move reserve words related files into dialects sub package

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1544

Fix mssql quote (#1535)

Fix some quotes

Fix mssql quote

Merge core package back into the main repository and split into serval sub packages. (#1543)

Fix test

Improve fmt

update go.mod

Move core as a sub package

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1543

Fix int time deleted bug (#1539)

Fix panic

Fix test

Fix test for mssql time

Add sql type check on deleted cond

Fix int time deleted bug

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1539

Add test for mysql8.0 (#1538)

Fix pk order on test

Add test for mysql8.0

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1538

Add test for join limit (#1536)

Add test for join limit

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1536

Improve drone (#1537)

Fix drone

Improve drone

* use traditional positional parameters on inser...
Reviewed-on: https://gitea.com/xorm/xorm/pulls/1537

Fix slice of struct not cache bug (#895)

Fix failure caused by nil bean

Judge both type of struct and pointer in case of out-of-range

Fix issue #894

Add test for join subquery (#1528)

Fix test

Fix subquery with schema

Add test for join subquery

Add makefile (#1531)

Fix drone

Fix ci

Add deps

Improve drone

Fix envs

Add makefile

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1531

Add password for postgres drone image (#1530)

Add password for postgres drone image

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1530

format time when sqlTypeName is core.Varchar (#1026)

fix time test

add test for time format

sign codes according to contributing rules.

format time when sqlTypeName is core.Varchar. Same with core.DateTime or core.TimeStamp

Add test for second insert error (#1527)

Add test for second insert error

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1527

Add tests for table name (#1517)

add tests for table name

Fix test (#1526)

Fix test

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1526

Fix test (#1526)

Fix test

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1526

Fix wrong warning log on autoincrement column when sync table (#1525)

improve doc

Fix wrong warning log on autoincrement column when sync table

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1525

Fixed Join strings on func Exist (#1520)

fix test

fixed Join strings on func Exist

Co-authored-by: Tomofumi Kusana <tkusana@morisawa.co.jp>
Reviewed-on: https://gitea.com/xorm/xorm/pulls/1520

For nullable columns, store nil values as NULL (#531)

Merge branch 'master' into jcsalem/fix/nil_ptr_is_nullable

fix bug when buffersize with iterate (#941)

Merge branch 'master' into lunny/fix_buffer_iterate

Exclude schema from index name (#1505)

Merge branch 'master' into fix-schema-idx

SetExpr support more go types (#1499)

Improve tests

SetExpr support more go types

fix vet

fix drone lint

remove go1.10 test on drone

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1499

fix vet

fix drone lint

remove go1.10 test on drone

Exclude schema from the index name

Co-authored-by: Guillermo Prandi <guillep2k@users.noreply.github.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/xorm/xorm/pulls/1505

fix test

fix bug

fix bug when buffersize with iterate

SetExpr support more go types (#1499)

Improve tests

SetExpr support more go types

fix vet

fix drone lint

remove go1.10 test on drone

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1499

fix vet

fix drone lint

remove go1.10 test on drone

Fix update with Alias (#1455)

Co-authored-by: Guillermo Prandi <guillep2k@noreply.gitea.io>
Reviewed-on: https://gitea.com/xorm/xorm/pulls/941

fix update map with version (#1448)

fix test

fix update map with version

SetExpr support more go types (#1499)

Improve tests

SetExpr support more go types

fix vet

fix drone lint

remove go1.10 test on drone

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1499

fix vet

fix drone lint

remove go1.10 test on drone

Fix update with Alias (#1455)

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1448

Exclude schema from index name (#1505)

Merge branch 'master' into fix-schema-idx

SetExpr support more go types (#1499)

Improve tests

SetExpr support more go types

fix vet

fix drone lint

remove go1.10 test on drone

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1499

fix vet

fix drone lint

remove go1.10 test on drone

Exclude schema from the index name

Co-authored-by: Guillermo Prandi <guillep2k@users.noreply.github.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/xorm/xorm/pulls/1505

SetExpr support more go types (#1499)

Improve tests

SetExpr support more go types

fix vet

fix drone lint

remove go1.10 test on drone

Reviewed-on: https://gitea.com/xorm/xorm/pulls/1499

For nullable columns, store nil values as NULL

fix vet

fix drone lint

remove go1.10 test on drone

Fix update with Alias (#1455)

Improve c...
Reviewed-on: https://gitea.com/xorm/xorm/pulls/1534
This commit is contained in:
Lunny Xiao 2020-02-25 00:01:36 +00:00
parent 3df77142b3
commit cc33b2d305
18 changed files with 276 additions and 200 deletions

View File

@ -45,11 +45,8 @@ type Dialect interface {
DataSourceName() string DataSourceName() string
IsReserved(string) bool IsReserved(string) bool
Quote(string) string Quoter() schemas.Quoter
AndStr() string
OrStr() string
EqStr() string
RollBackStr() string RollBackStr() string
AutoIncrStr() string AutoIncrStr() string
@ -101,7 +98,7 @@ type Base struct {
// String generate column description string according dialect // String generate column description string according dialect
func String(d Dialect, col *schemas.Column) string { func String(d Dialect, col *schemas.Column) string {
sql := d.Quote(col.Name) + " " sql := d.Quoter().Quote(col.Name) + " "
sql += d.SQLType(col) + " " sql += d.SQLType(col) + " "
@ -129,7 +126,7 @@ func String(d Dialect, col *schemas.Column) string {
// StringNoPk generate column description string according dialect without primary keys // StringNoPk generate column description string according dialect without primary keys
func StringNoPk(d Dialect, col *schemas.Column) string { func StringNoPk(d Dialect, col *schemas.Column) string {
sql := d.Quote(col.Name) + " " sql := d.Quoter().Quote(col.Name) + " "
sql += d.SQLType(col) + " " sql += d.SQLType(col) + " "
@ -186,18 +183,6 @@ func (b *Base) DataSourceName() string {
return b.dataSourceName return b.dataSourceName
} }
func (b *Base) AndStr() string {
return "AND"
}
func (b *Base) OrStr() string {
return "OR"
}
func (b *Base) EqStr() string {
return "="
}
func (db *Base) RollBackStr() string { func (db *Base) RollBackStr() string {
return "ROLL BACK" return "ROLL BACK"
} }
@ -207,7 +192,7 @@ func (db *Base) SupportDropIfExists() bool {
} }
func (db *Base) DropTableSQL(tableName string) string { func (db *Base) DropTableSQL(tableName string) string {
quote := db.dialect.Quote quote := db.dialect.Quoter().Quote
return fmt.Sprintf("DROP TABLE IF EXISTS %s", quote(tableName)) return fmt.Sprintf("DROP TABLE IF EXISTS %s", quote(tableName))
} }
@ -226,14 +211,15 @@ func (db *Base) HasRecords(query string, args ...interface{}) (bool, error) {
} }
func (db *Base) IsColumnExist(tableName, colName string) (bool, error) { func (db *Base) IsColumnExist(tableName, colName string) (bool, error) {
quote := db.dialect.Quoter().Quote
query := fmt.Sprintf( query := fmt.Sprintf(
"SELECT %v FROM %v.%v WHERE %v = ? AND %v = ? AND %v = ?", "SELECT %v FROM %v.%v WHERE %v = ? AND %v = ? AND %v = ?",
db.dialect.Quote("COLUMN_NAME"), quote("COLUMN_NAME"),
db.dialect.Quote("INFORMATION_SCHEMA"), quote("INFORMATION_SCHEMA"),
db.dialect.Quote("COLUMNS"), quote("COLUMNS"),
db.dialect.Quote("TABLE_SCHEMA"), quote("TABLE_SCHEMA"),
db.dialect.Quote("TABLE_NAME"), quote("TABLE_NAME"),
db.dialect.Quote("COLUMN_NAME"), quote("COLUMN_NAME"),
) )
return db.HasRecords(query, db.uri.DBName, tableName, colName) return db.HasRecords(query, db.uri.DBName, tableName, colName)
} }
@ -263,8 +249,7 @@ func (db *Base) CreateTableIfNotExists(table *Table, tableName, storeEngine, cha
}*/ }*/
func (db *Base) CreateIndexSQL(tableName string, index *schemas.Index) string { func (db *Base) CreateIndexSQL(tableName string, index *schemas.Index) string {
quotes := db.dialect.Quote("") quoter := db.dialect.Quoter()
quote := db.dialect.Quote
var unique string var unique string
var idxName string var idxName string
if index.Type == schemas.UniqueType { if index.Type == schemas.UniqueType {
@ -272,12 +257,12 @@ func (db *Base) CreateIndexSQL(tableName string, index *schemas.Index) string {
} }
idxName = index.XName(tableName) idxName = index.XName(tableName)
return fmt.Sprintf("CREATE%s INDEX %v ON %v (%v)", unique, return fmt.Sprintf("CREATE%s INDEX %v ON %v (%v)", unique,
quote(idxName), quote(tableName), quoter.Quote(idxName), quoter.Quote(tableName),
quote(strings.Join(index.Cols, fmt.Sprintf("%c,%c", quotes[1], quotes[0])))) quoter.Quote(strings.Join(index.Cols, quoter.ReverseQuote(","))))
} }
func (db *Base) DropIndexSQL(tableName string, index *schemas.Index) string { func (db *Base) DropIndexSQL(tableName string, index *schemas.Index) string {
quote := db.dialect.Quote quote := db.dialect.Quoter().Quote
var name string var name string
if index.IsRegular { if index.IsRegular {
name = index.XName(tableName) name = index.XName(tableName)
@ -298,11 +283,10 @@ func (b *Base) CreateTableSQL(table *schemas.Table, tableName, storeEngine, char
tableName = table.Name tableName = table.Name
} }
sql += b.dialect.Quote(tableName) quoter := b.dialect.Quoter()
sql += quoter.Quote(tableName)
sql += " (" sql += " ("
quotes := b.dialect.Quote("")
if len(table.ColumnsSeq()) > 0 { if len(table.ColumnsSeq()) > 0 {
pkList := table.PrimaryKeys pkList := table.PrimaryKeys
@ -322,7 +306,7 @@ func (b *Base) CreateTableSQL(table *schemas.Table, tableName, storeEngine, char
if len(pkList) > 1 { if len(pkList) > 1 {
sql += "PRIMARY KEY ( " sql += "PRIMARY KEY ( "
sql += b.dialect.Quote(strings.Join(pkList, fmt.Sprintf("%c,%c", quotes[1], quotes[0]))) sql += quoter.Quote(strings.Join(pkList, quoter.ReverseQuote(",")))
sql += " ), " sql += " ), "
} }

View File

@ -21,11 +21,12 @@ type QuoteFilter struct {
} }
func (s *QuoteFilter) Do(sql string, dialect Dialect, table *schemas.Table) string { func (s *QuoteFilter) Do(sql string, dialect Dialect, table *schemas.Table) string {
dummy := dialect.Quote("") quoter := dialect.Quoter()
if len(dummy) != 2 { if quoter.IsEmpty() {
return sql return sql
} }
prefix, suffix := dummy[0], dummy[1]
prefix, suffix := quoter[0][0], quoter[1][0]
raw := []byte(sql) raw := []byte(sql)
for i, cnt := 0, 0; i < len(raw); i = i + 1 { for i, cnt := 0, 0; i < len(raw); i = i + 1 {
if raw[i] == '`' { if raw[i] == '`' {
@ -38,32 +39,7 @@ func (s *QuoteFilter) Do(sql string, dialect Dialect, table *schemas.Table) stri
} }
} }
return string(raw) return string(raw)
}
// IdFilter filter SQL replace (id) to primary key column name
type IdFilter struct {
}
type Quoter struct {
dialect Dialect
}
func NewQuoter(dialect Dialect) *Quoter {
return &Quoter{dialect}
}
func (q *Quoter) Quote(content string) string {
return q.dialect.Quote(content)
}
func (i *IdFilter) Do(sql string, dialect Dialect, table *schemas.Table) string {
quoter := NewQuoter(dialect)
if table != nil && len(table.PrimaryKeys) == 1 {
sql = strings.Replace(sql, " `(id)` ", " "+quoter.Quote(table.PrimaryKeys[0])+" ", -1)
sql = strings.Replace(sql, " "+quoter.Quote("(id)")+" ", " "+quoter.Quote(table.PrimaryKeys[0])+" ", -1)
return strings.Replace(sql, " (id) ", " "+quoter.Quote(table.PrimaryKeys[0])+" ", -1)
}
return sql
} }
// SeqFilter filter SQL replace ?, ? ... to $1, $2 ... // SeqFilter filter SQL replace ?, ? ... to $1, $2 ...

View File

@ -286,8 +286,8 @@ func (db *mssql) IsReserved(name string) bool {
return ok return ok
} }
func (db *mssql) Quote(name string) string { func (db *mssql) Quoter() schemas.Quoter {
return "[" + name + "]" return schemas.Quoter{"[", "]"}
} }
func (db *mssql) SupportEngine() bool { func (db *mssql) SupportEngine() bool {
@ -503,7 +503,7 @@ func (db *mssql) CreateTableSQL(table *schemas.Table, tableName, storeEngine, ch
sql = "IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = '" + tableName + "' ) CREATE TABLE " sql = "IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = '" + tableName + "' ) CREATE TABLE "
sql += db.Quote(tableName) + " (" sql += db.Quoter().Quote(tableName) + " ("
pkList := table.PrimaryKeys pkList := table.PrimaryKeys
@ -534,7 +534,7 @@ func (db *mssql) ForUpdateSQL(query string) string {
} }
func (db *mssql) Filters() []Filter { func (db *mssql) Filters() []Filter {
return []Filter{&IdFilter{}, &QuoteFilter{}} return []Filter{&QuoteFilter{}}
} }
type odbcDriver struct { type odbcDriver struct {

View File

@ -275,8 +275,8 @@ func (db *mysql) IsReserved(name string) bool {
return ok return ok
} }
func (db *mysql) Quote(name string) string { func (db *mysql) Quoter() schemas.Quoter {
return "`" + name + "`" return schemas.Quoter{"`", "`"}
} }
func (db *mysql) SupportEngine() bool { func (db *mysql) SupportEngine() bool {
@ -512,9 +512,9 @@ func (db *mysql) CreateTableSQL(table *schemas.Table, tableName, storeEngine, ch
tableName = table.Name tableName = table.Name
} }
quotes := db.Quote("") quoter := db.Quoter()
sql += db.Quote(tableName) sql += quoter.Quote(tableName)
sql += " (" sql += " ("
if len(table.ColumnsSeq()) > 0 { if len(table.ColumnsSeq()) > 0 {
@ -536,7 +536,7 @@ func (db *mysql) CreateTableSQL(table *schemas.Table, tableName, storeEngine, ch
if len(pkList) > 1 { if len(pkList) > 1 {
sql += "PRIMARY KEY ( " sql += "PRIMARY KEY ( "
sql += db.Quote(strings.Join(pkList, fmt.Sprintf("%c,%c", quotes[1], quotes[0]))) sql += quoter.Quote(strings.Join(pkList, quoter.ReverseQuote(",")))
sql += " ), " sql += " ), "
} }
@ -562,7 +562,7 @@ func (db *mysql) CreateTableSQL(table *schemas.Table, tableName, storeEngine, ch
} }
func (db *mysql) Filters() []Filter { func (db *mysql) Filters() []Filter {
return []Filter{&IdFilter{}} return []Filter{}
} }
type mymysqlDriver struct { type mymysqlDriver struct {

View File

@ -552,8 +552,8 @@ func (db *oracle) IsReserved(name string) bool {
return ok return ok
} }
func (db *oracle) Quote(name string) string { func (db *oracle) Quoter() schemas.Quoter {
return "[" + name + "]" return schemas.Quoter{"[", "]"}
} }
func (db *oracle) SupportEngine() bool { func (db *oracle) SupportEngine() bool {
@ -582,7 +582,8 @@ func (db *oracle) CreateTableSQL(table *schemas.Table, tableName, storeEngine, c
tableName = table.Name tableName = table.Name
} }
sql += db.Quote(tableName) + " (" quoter := db.Quoter()
sql += quoter.Quote(tableName) + " ("
pkList := table.PrimaryKeys pkList := table.PrimaryKeys
@ -597,11 +598,9 @@ func (db *oracle) CreateTableSQL(table *schemas.Table, tableName, storeEngine, c
sql += ", " sql += ", "
} }
quotes := db.Quote("")
if len(pkList) > 0 { if len(pkList) > 0 {
sql += "PRIMARY KEY ( " sql += "PRIMARY KEY ( "
sql += db.Quote(strings.Join(pkList, fmt.Sprintf("%c,%c", quotes[1], quotes[0]))) sql += quoter.Quote(strings.Join(pkList, quoter.ReverseQuote(",")))
sql += " ), " sql += " ), "
} }
@ -849,7 +848,7 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*schemas.Index, error
} }
func (db *oracle) Filters() []Filter { func (db *oracle) Filters() []Filter {
return []Filter{&QuoteFilter{}, &SeqFilter{Prefix: ":", Start: 1}, &IdFilter{}} return []Filter{&QuoteFilter{}, &SeqFilter{Prefix: ":", Start: 1}}
} }
type goracleDriver struct { type goracleDriver struct {

View File

@ -859,9 +859,8 @@ func (db *postgres) IsReserved(name string) bool {
return ok return ok
} }
func (db *postgres) Quote(name string) string { func (db *postgres) Quoter() schemas.Quoter {
name = strings.Replace(name, ".", `"."`, -1) return schemas.Quoter{`"`, `"`}
return "\"" + name + "\""
} }
func (db *postgres) AutoIncrStr() string { func (db *postgres) AutoIncrStr() string {
@ -911,7 +910,6 @@ func (db *postgres) ModifyColumnSQL(tableName string, col *schemas.Column) strin
} }
func (db *postgres) DropIndexSQL(tableName string, index *schemas.Index) string { func (db *postgres) DropIndexSQL(tableName string, index *schemas.Index) string {
quote := db.Quote
idxName := index.Name idxName := index.Name
tableParts := strings.Split(strings.Replace(tableName, `"`, "", -1), ".") tableParts := strings.Split(strings.Replace(tableName, `"`, "", -1), ".")
@ -928,7 +926,7 @@ func (db *postgres) DropIndexSQL(tableName string, index *schemas.Index) string
if db.uri.Schema != "" { if db.uri.Schema != "" {
idxName = db.uri.Schema + "." + idxName idxName = db.uri.Schema + "." + idxName
} }
return fmt.Sprintf("DROP INDEX %v", quote(idxName)) return fmt.Sprintf("DROP INDEX %v", db.Quoter().Quote(idxName))
} }
func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) { func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) {
@ -1161,7 +1159,7 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*schemas.Index, err
} }
func (db *postgres) Filters() []Filter { func (db *postgres) Filters() []Filter {
return []Filter{&IdFilter{}, &QuoteFilter{}, &SeqFilter{Prefix: "$", Start: 1}} return []Filter{&QuoteFilter{}, &SeqFilter{Prefix: "$", Start: 1}}
} }
type pqDriver struct { type pqDriver struct {

View File

@ -199,8 +199,8 @@ func (db *sqlite3) IsReserved(name string) bool {
return ok return ok
} }
func (db *sqlite3) Quote(name string) string { func (db *sqlite3) Quoter() schemas.Quoter {
return "`" + name + "`" return schemas.Quoter{"`", "`"}
} }
func (db *sqlite3) AutoIncrStr() string { func (db *sqlite3) AutoIncrStr() string {
@ -231,7 +231,6 @@ func (db *sqlite3) TableCheckSQL(tableName string) (string, []interface{}) {
func (db *sqlite3) DropIndexSQL(tableName string, index *schemas.Index) string { func (db *sqlite3) DropIndexSQL(tableName string, index *schemas.Index) string {
// var unique string // var unique string
quote := db.Quote
idxName := index.Name idxName := index.Name
if !strings.HasPrefix(idxName, "UQE_") && if !strings.HasPrefix(idxName, "UQE_") &&
@ -242,7 +241,7 @@ func (db *sqlite3) DropIndexSQL(tableName string, index *schemas.Index) string {
idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name) idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name)
} }
} }
return fmt.Sprintf("DROP INDEX %v", quote(idxName)) return fmt.Sprintf("DROP INDEX %v", db.Quoter().Quote(idxName))
} }
func (db *sqlite3) ForUpdateSQL(query string) string { func (db *sqlite3) ForUpdateSQL(query string) string {
@ -478,7 +477,7 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*schemas.Index, erro
} }
func (db *sqlite3) Filters() []Filter { func (db *sqlite3) Filters() []Filter {
return []Filter{&IdFilter{}} return []Filter{}
} }
type sqlite3Driver struct { type sqlite3Driver struct {

View File

@ -193,10 +193,7 @@ func (engine *Engine) SupportInsertMany() bool {
func (engine *Engine) quoteColumns(columnStr string) string { func (engine *Engine) quoteColumns(columnStr string) string {
columns := strings.Split(columnStr, ",") columns := strings.Split(columnStr, ",")
for i := 0; i < len(columns); i++ { return engine.dialect.Quoter().Join(columns, ",")
columns[i] = engine.Quote(strings.TrimSpace(columns[i]))
}
return strings.Join(columns, ",")
} }
// Quote Use QuoteStr quote the string sql // Quote Use QuoteStr quote the string sql
@ -222,53 +219,13 @@ func (engine *Engine) QuoteTo(buf *strings.Builder, value string) {
if value == "" { if value == "" {
return return
} }
engine.dialect.Quoter().QuoteTo(buf, value)
quoteTo(buf, engine.dialect.Quote(""), value)
}
func quoteTo(buf *strings.Builder, quotePair string, value string) {
if len(quotePair) < 2 { // no quote
_, _ = buf.WriteString(value)
return
}
prefix, suffix := quotePair[0], quotePair[1]
i := 0
for i < len(value) {
// start of a token; might be already quoted
if value[i] == '.' {
_ = buf.WriteByte('.')
i++
} else if value[i] == prefix || value[i] == '`' {
// Has quotes; skip/normalize `name` to prefix+name+sufix
var ch byte
if value[i] == prefix {
ch = suffix
} else {
ch = '`'
}
i++
_ = buf.WriteByte(prefix)
for ; i < len(value) && value[i] != ch; i++ {
_ = buf.WriteByte(value[i])
}
_ = buf.WriteByte(suffix)
i++
} else {
// Requires quotes
_ = buf.WriteByte(prefix)
for ; i < len(value) && value[i] != '.'; i++ {
_ = buf.WriteByte(value[i])
}
_ = buf.WriteByte(suffix)
}
}
} }
/*
func (engine *Engine) quote(sql string) string { func (engine *Engine) quote(sql string) string {
return engine.dialect.Quote(sql) return engine.dialect.Quote(sql)
} }*/
// SqlType will be deprecated, please use SQLType instead // SqlType will be deprecated, please use SQLType instead
// //
@ -530,8 +487,8 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...dia
} }
cols := table.ColumnsSeq() cols := table.ColumnsSeq()
colNames := engine.dialect.Quote(strings.Join(cols, engine.dialect.Quote(", "))) colNames := engine.dialect.Quoter().Join(cols, ", ")
destColNames := dialect.Quote(strings.Join(cols, dialect.Quote(", "))) destColNames := dialect.Quoter().Join(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))
if err != nil { if err != nil {
@ -546,7 +503,7 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...dia
return err return err
} }
_, err = io.WriteString(w, "INSERT INTO "+dialect.Quote(table.Name)+" ("+destColNames+") VALUES (") _, err = io.WriteString(w, "INSERT INTO "+dialect.Quoter().Quote(table.Name)+" ("+destColNames+") VALUES (")
if err != nil { if err != nil {
return err return err
} }
@ -617,7 +574,7 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...dia
// FIXME: Hack for postgres // FIXME: Hack for postgres
if string(dialect.DBType()) == schemas.POSTGRES && table.AutoIncrColumn() != nil { if string(dialect.DBType()) == schemas.POSTGRES && table.AutoIncrColumn() != nil {
_, err = io.WriteString(w, "SELECT setval('"+table.Name+"_id_seq', COALESCE((SELECT MAX("+table.AutoIncrColumn().Name+") + 1 FROM "+dialect.Quote(table.Name)+"), 1), false);\n") _, err = io.WriteString(w, "SELECT setval('"+table.Name+"_id_seq', COALESCE((SELECT MAX("+table.AutoIncrColumn().Name+") + 1 FROM "+dialect.Quoter().Quote(table.Name)+"), 1), false);\n")
if err != nil { if err != nil {
return err return err
} }

165
schemas/quote.go Normal file
View File

@ -0,0 +1,165 @@
// 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 (
"fmt"
"strings"
)
// Quoter represents two quote characters
type Quoter [2]string
// CommonQuoter represetns a common quoter
var CommonQuoter = Quoter{"`", "`"}
func (q Quoter) IsEmpty() bool {
return q[0] == "" && q[1] == ""
}
func (q Quoter) Quote(s string) string {
var buf strings.Builder
q.QuoteTo(&buf, s)
return buf.String()
}
func (q Quoter) Replace(sql string, newQuoter Quoter) string {
if q.IsEmpty() {
return sql
}
if newQuoter.IsEmpty() {
var buf strings.Builder
for i := 0; i < len(sql); i = i + 1 {
if sql[i] != q[0][0] && sql[i] != q[1][0] {
_ = buf.WriteByte(sql[i])
}
}
return buf.String()
}
prefix, suffix := newQuoter[0][0], newQuoter[1][0]
var buf strings.Builder
for i, cnt := 0, 0; i < len(sql); i = i + 1 {
if cnt == 0 && sql[i] == q[0][0] {
_ = buf.WriteByte(prefix)
cnt = 1
} else if cnt == 1 && sql[i] == q[1][0] {
_ = buf.WriteByte(suffix)
cnt = 0
} else {
_ = buf.WriteByte(sql[i])
}
}
return buf.String()
}
func (q Quoter) ReverseQuote(s string) string {
reverseQuoter := Quoter{q[1], q[0]}
return reverseQuoter.Quote(s)
}
// Trim removes quotes from s
func (q Quoter) Trim(s string) string {
if len(s) < 2 {
return s
}
if s[0:1] == q[0] {
s = s[1:]
}
if len(s) > 0 && s[len(s)-1:] == q[0] {
return s[:len(s)-1]
}
return s
}
func TrimSpaceJoin(a []string, sep string) string {
switch len(a) {
case 0:
return ""
case 1:
return a[0]
}
n := len(sep) * (len(a) - 1)
for i := 0; i < len(a); i++ {
n += len(a[i])
}
var b strings.Builder
b.Grow(n)
b.WriteString(strings.TrimSpace(a[0]))
for _, s := range a[1:] {
b.WriteString(sep)
b.WriteString(strings.TrimSpace(s))
}
return b.String()
}
func (q Quoter) Join(s []string, splitter string) string {
//return fmt.Sprintf("%s%s%s", q[0], TrimSpaceJoin(s, fmt.Sprintf("%s%s%s", q[1], splitter, q[0])), q[1])
return q.Quote(TrimSpaceJoin(s, fmt.Sprintf("%s%s%s", q[1], splitter, q[0])))
}
func (q Quoter) QuoteTo(buf *strings.Builder, value string) {
if q.IsEmpty() {
buf.WriteString(value)
return
}
prefix, suffix := q[0][0], q[1][0]
lastCh := 0 // 0 prefix, 1 char, 2 suffix
i := 0
for i < len(value) {
// start of a token; might be already quoted
if value[i] == '.' {
_ = buf.WriteByte('.')
lastCh = 1
i++
} else if value[i] == prefix || value[i] == '`' {
// Has quotes; skip/normalize `name` to prefix+name+sufix
var ch byte
if value[i] == prefix {
ch = suffix
} else {
ch = '`'
}
_ = buf.WriteByte(prefix)
i++
lastCh = 0
for ; i < len(value) && value[i] != ch && value[i] != ' '; i++ {
_ = buf.WriteByte(value[i])
lastCh = 1
}
_ = buf.WriteByte(suffix)
lastCh = 2
i++
} else if value[i] == ' ' {
if lastCh != 2 {
_ = buf.WriteByte(suffix)
lastCh = 2
}
// a AS b or a b
for ; i < len(value); i++ {
if value[i] != ' ' && value[i-1] == ' ' && (len(value) > i+1 && !strings.EqualFold(value[i:i+2], "AS")) {
break
}
_ = buf.WriteByte(value[i])
lastCh = 1
}
} else {
// Requires quotes
_ = buf.WriteByte(prefix)
for ; i < len(value) && value[i] != '.' && value[i] != ' '; i++ {
_ = buf.WriteByte(value[i])
lastCh = 1
}
_ = buf.WriteByte(suffix)
lastCh = 2
}
}
}

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package xorm package schemas
import ( import (
"strings" "strings"
@ -12,10 +12,11 @@ import (
) )
func TestQuoteTo(t *testing.T) { func TestQuoteTo(t *testing.T) {
var quoter = Quoter{"[", "]"}
test := func(t *testing.T, expected string, value string) { test := func(t *testing.T, expected string, value string) {
buf := &strings.Builder{} buf := &strings.Builder{}
quoteTo(buf, "[]", value) quoter.QuoteTo(buf, value)
assert.EqualValues(t, expected, buf.String()) assert.EqualValues(t, expected, buf.String())
} }
@ -35,7 +36,12 @@ func TestQuoteTo(t *testing.T) {
test(t, `["myschema].[mytable"]`, `"myschema.mytable"`) test(t, `["myschema].[mytable"]`, `"myschema.mytable"`)
test(t, "[message_user] AS [sender]", "`message_user` AS `sender`")
assert.EqualValues(t, "[a],[b]", quoter.Join([]string{"a", " b"}, ","))
buf := &strings.Builder{} buf := &strings.Builder{}
quoteTo(buf, "", "noquote") quoter = Quoter{"", ""}
quoter.QuoteTo(buf, "noquote")
assert.EqualValues(t, "noquote", buf.String()) assert.EqualValues(t, "noquote", buf.String())
} }

View File

@ -137,13 +137,13 @@ func TestIn(t *testing.T) {
idsStr = idsStr[:len(idsStr)-1] idsStr = idsStr[:len(idsStr)-1]
users := make([]Userinfo, 0) users := make([]Userinfo, 0)
err = testEngine.In("(id)", ids[0], ids[1], ids[2]).Find(&users) err = testEngine.In("id", ids[0], ids[1], ids[2]).Find(&users)
assert.NoError(t, err) assert.NoError(t, err)
fmt.Println(users) fmt.Println(users)
assert.EqualValues(t, 3, len(users)) assert.EqualValues(t, 3, len(users))
users = make([]Userinfo, 0) users = make([]Userinfo, 0)
err = testEngine.In("(id)", ids).Find(&users) err = testEngine.In("id", ids).Find(&users)
assert.NoError(t, err) assert.NoError(t, err)
fmt.Println(users) fmt.Println(users)
assert.EqualValues(t, 3, len(users)) assert.EqualValues(t, 3, len(users))
@ -161,7 +161,7 @@ func TestIn(t *testing.T) {
idsInterface = append(idsInterface, id) idsInterface = append(idsInterface, id)
} }
err = testEngine.Where(department+" = ?", "dev").In("(id)", idsInterface...).Find(&users) err = testEngine.Where(department+" = ?", "dev").In("id", idsInterface...).Find(&users)
assert.NoError(t, err) assert.NoError(t, err)
fmt.Println(users) fmt.Println(users)
assert.EqualValues(t, 3, len(users)) assert.EqualValues(t, 3, len(users))
@ -175,11 +175,11 @@ func TestIn(t *testing.T) {
dev := testEngine.GetColumnMapper().Obj2Table("Dev") dev := testEngine.GetColumnMapper().Obj2Table("Dev")
err = testEngine.In("(id)", 1).In("(id)", 2).In(department, dev).Find(&users) err = testEngine.In("id", 1).In("id", 2).In(department, dev).Find(&users)
assert.NoError(t, err) assert.NoError(t, err)
fmt.Println(users) fmt.Println(users)
cnt, err = testEngine.In("(id)", ids[0]).Update(&Userinfo{Departname: "dev-"}) cnt, err = testEngine.In("id", ids[0]).Update(&Userinfo{Departname: "dev-"})
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 1, cnt) assert.EqualValues(t, 1, cnt)
@ -189,11 +189,11 @@ func TestIn(t *testing.T) {
assert.True(t, has) assert.True(t, has)
assert.EqualValues(t, "dev-", user.Departname) assert.EqualValues(t, "dev-", user.Departname)
cnt, err = testEngine.In("(id)", ids[0]).Update(&Userinfo{Departname: "dev"}) cnt, err = testEngine.In("id", ids[0]).Update(&Userinfo{Departname: "dev"})
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 1, cnt) assert.EqualValues(t, 1, cnt)
cnt, err = testEngine.In("(id)", ids[1]).Delete(&Userinfo{}) cnt, err = testEngine.In("id", ids[1]).Delete(&Userinfo{})
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 1, cnt) assert.EqualValues(t, 1, cnt)
} }

View File

@ -77,14 +77,14 @@ func TestWhere(t *testing.T) {
assertSync(t, new(Userinfo)) assertSync(t, new(Userinfo))
users := make([]Userinfo, 0) users := make([]Userinfo, 0)
err := testEngine.Where("(id) > ?", 2).Find(&users) err := testEngine.Where("id > ?", 2).Find(&users)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
panic(err) panic(err)
} }
fmt.Println(users) fmt.Println(users)
err = testEngine.Where("(id) > ?", 2).And("(id) < ?", 10).Find(&users) err = testEngine.Where("id > ?", 2).And("id < ?", 10).Find(&users)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
panic(err) panic(err)
@ -312,12 +312,12 @@ func TestOrderSameMapper(t *testing.T) {
assertSync(t, new(Userinfo)) assertSync(t, new(Userinfo))
users := make([]Userinfo, 0) users := make([]Userinfo, 0)
err := testEngine.OrderBy("(id) desc").Find(&users) err := testEngine.OrderBy("id desc").Find(&users)
assert.NoError(t, err) assert.NoError(t, err)
fmt.Println(users) fmt.Println(users)
users2 := make([]Userinfo, 0) users2 := make([]Userinfo, 0)
err = testEngine.Asc("(id)", "Username").Desc("Height").Find(&users2) err = testEngine.Asc("id", "Username").Desc("Height").Find(&users2)
assert.NoError(t, err) assert.NoError(t, err)
fmt.Println(users2) fmt.Println(users2)
} }
@ -790,8 +790,12 @@ func TestFindJoin(t *testing.T) {
DeviceId int64 DeviceId int64
} }
type Order struct {
Id int64
}
assert.NoError(t, prepareEngine()) assert.NoError(t, prepareEngine())
assertSync(t, new(SceneItem), new(DeviceUserPrivrels)) assertSync(t, new(SceneItem), new(DeviceUserPrivrels), new(Order))
var scenes []SceneItem var scenes []SceneItem
err := testEngine.Join("LEFT OUTER", "device_user_privrels", "device_user_privrels.device_id=scene_item.device_id"). err := testEngine.Join("LEFT OUTER", "device_user_privrels", "device_user_privrels.device_id=scene_item.device_id").
@ -802,6 +806,10 @@ func TestFindJoin(t *testing.T) {
err = testEngine.Join("LEFT OUTER", new(DeviceUserPrivrels), "device_user_privrels.device_id=scene_item.device_id"). err = testEngine.Join("LEFT OUTER", new(DeviceUserPrivrels), "device_user_privrels.device_id=scene_item.device_id").
Where("scene_item.type=?", 3).Or("device_user_privrels.user_id=?", 339).Find(&scenes) Where("scene_item.type=?", 3).Or("device_user_privrels.user_id=?", 339).Find(&scenes)
assert.NoError(t, err) assert.NoError(t, err)
scenes = make([]SceneItem, 0)
err = testEngine.Join("INNER", "order", "`scene_item`.device_id=`order`.id").Find(&scenes)
assert.NoError(t, err)
} }
func TestJoinFindLimit(t *testing.T) { func TestJoinFindLimit(t *testing.T) {

View File

@ -201,7 +201,7 @@ func TestInsertDefault(t *testing.T) {
_, err = testEngine.Omit(testEngine.GetColumnMapper().Obj2Table("Status")).Insert(&di2) _, err = testEngine.Omit(testEngine.GetColumnMapper().Obj2Table("Status")).Insert(&di2)
assert.NoError(t, err) assert.NoError(t, err)
has, err := testEngine.Desc("(id)").Get(di) has, err := testEngine.Desc("id").Get(di)
assert.NoError(t, err) assert.NoError(t, err)
if !has { if !has {
err = errors.New("error with no data") err = errors.New("error with no data")
@ -247,7 +247,7 @@ func TestInsertDefault2(t *testing.T) {
t.Error(err) t.Error(err)
} }
has, err := testEngine.Desc("(id)").Get(di) has, err := testEngine.Desc("id").Get(di)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -257,7 +257,7 @@ func TestInsertDefault2(t *testing.T) {
panic(err) panic(err)
} }
has, err = testEngine.NoAutoCondition().Desc("(id)").Get(&di2) has, err = testEngine.NoAutoCondition().Desc("id").Get(&di2)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -330,7 +330,7 @@ func TestInsertCreated(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
has, err := testEngine.Desc("(id)").Get(di) has, err := testEngine.Desc("id").Get(di)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -352,7 +352,7 @@ func TestInsertCreated(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
has, err = testEngine.Desc("(id)").Get(di2) has, err = testEngine.Desc("id").Get(di2)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -374,7 +374,7 @@ func TestInsertCreated(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
has, err = testEngine.Desc("(id)").Get(di3) has, err = testEngine.Desc("id").Get(di3)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -396,7 +396,7 @@ func TestInsertCreated(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
has, err = testEngine.Desc("(id)").Get(di4) has, err = testEngine.Desc("id").Get(di4)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -418,7 +418,7 @@ func TestInsertCreated(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
has, err = testEngine.Desc("(id)").Get(di5) has, err = testEngine.Desc("id").Get(di5)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -442,7 +442,7 @@ func TestInsertCreated(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
has, err = testEngine.Desc("(id)").Get(di6) has, err = testEngine.Desc("id").Get(di6)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -517,7 +517,7 @@ func TestCreatedJsonTime(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
has, err := testEngine.Desc("(id)").Get(di5) has, err := testEngine.Desc("id").Get(di5)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -17,15 +17,12 @@ func TestTransaction(t *testing.T) {
assert.NoError(t, prepareEngine()) assert.NoError(t, prepareEngine())
assertSync(t, new(Userinfo)) assertSync(t, new(Userinfo))
counter := func() { counter := func(t *testing.T) {
total, err := testEngine.Count(&Userinfo{}) _, err := testEngine.Count(&Userinfo{})
if err != nil { assert.NoError(t, err)
t.Error(err)
}
fmt.Printf("----now total %v records\n", total)
} }
counter() counter(t)
//defer counter() //defer counter()
session := testEngine.NewSession() session := testEngine.NewSession()
@ -39,7 +36,7 @@ func TestTransaction(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
user2 := Userinfo{Username: "yyy"} user2 := Userinfo{Username: "yyy"}
_, err = session.Where("(id) = ?", 0).Update(&user2) _, err = session.Where("id = ?", 0).Update(&user2)
assert.NoError(t, err) assert.NoError(t, err)
_, err = session.Delete(&user2) _, err = session.Delete(&user2)
@ -119,7 +116,7 @@ func TestCombineTransactionSameMapper(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
user2 := Userinfo{Username: "zzz"} user2 := Userinfo{Username: "zzz"}
_, err = session.Where("(id) = ?", 0).Update(&user2) _, err = session.Where("id = ?", 0).Update(&user2)
assert.NoError(t, err) assert.NoError(t, err)
_, err = session.Exec("delete from "+testEngine.TableName("`Userinfo`", true)+" where `Username` = ?", user2.Username) _, err = session.Exec("delete from "+testEngine.TableName("`Userinfo`", true)+" where `Username` = ?", user2.Username)

View File

@ -137,7 +137,7 @@ func TestForUpdate(t *testing.T) {
// use lock // use lock
fList := make([]ForUpdate, 0) fList := make([]ForUpdate, 0)
session1.ForUpdate() session1.ForUpdate()
session1.Where("(id) = ?", 1) session1.Where("id = ?", 1)
err = session1.Find(&fList) err = session1.Find(&fList)
switch { switch {
case err != nil: case err != nil:
@ -158,7 +158,7 @@ func TestForUpdate(t *testing.T) {
wg.Add(1) wg.Add(1)
go func() { go func() {
f2 := new(ForUpdate) f2 := new(ForUpdate)
session2.Where("(id) = ?", 1).ForUpdate() session2.Where("id = ?", 1).ForUpdate()
has, err := session2.Get(f2) // wait release lock has, err := session2.Get(f2) // wait release lock
switch { switch {
case err != nil: case err != nil:
@ -175,7 +175,7 @@ func TestForUpdate(t *testing.T) {
wg.Add(1) wg.Add(1)
go func() { go func() {
f3 := new(ForUpdate) f3 := new(ForUpdate)
session3.Where("(id) = ?", 1) session3.Where("id = ?", 1)
has, err := session3.Get(f3) // wait release lock has, err := session3.Get(f3) // wait release lock
switch { switch {
case err != nil: case err != nil:
@ -193,7 +193,7 @@ func TestForUpdate(t *testing.T) {
f := new(ForUpdate) f := new(ForUpdate)
f.Name = "updated by session1" f.Name = "updated by session1"
session1.Where("(id) = ?", 1) session1.Where("id = ?", 1)
session1.Update(f) session1.Update(f)
// release lock // release lock

View File

@ -618,7 +618,7 @@ func (statement *Statement) Cols(columns ...string) *Statement {
newColumns := statement.colmap2NewColsWithQuote() newColumns := statement.colmap2NewColsWithQuote()
statement.ColumnStr = strings.Join(newColumns, ", ") statement.ColumnStr = strings.Join(newColumns, ", ")
statement.ColumnStr = strings.Replace(statement.ColumnStr, statement.Engine.quote("*"), "*", -1) statement.ColumnStr = strings.Replace(statement.ColumnStr, statement.Engine.dialect.Quoter().Quote("*"), "*", -1)
return statement return statement
} }
@ -765,6 +765,11 @@ func (statement *Statement) Join(joinOP string, tablename interface{}, condition
statement.joinArgs = append(statement.joinArgs, subQueryArgs...) statement.joinArgs = append(statement.joinArgs, subQueryArgs...)
default: default:
tbName := statement.Engine.TableName(tablename, true) tbName := statement.Engine.TableName(tablename, true)
if !isSubQuery(tbName) {
var buf strings.Builder
statement.Engine.QuoteTo(&buf, tbName)
tbName = buf.String()
}
fmt.Fprintf(&buf, "%s ON %v", tbName, condition) fmt.Fprintf(&buf, "%s ON %v", tbName, condition)
} }

View File

@ -9,6 +9,7 @@ import (
"strings" "strings"
"xorm.io/builder" "xorm.io/builder"
"xorm.io/xorm/schemas"
) )
type ErrUnsupportedExprType struct { type ErrUnsupportedExprType struct {
@ -40,7 +41,7 @@ func (exprs *exprParams) addParam(colName string, arg interface{}) {
func (exprs *exprParams) isColExist(colName string) bool { func (exprs *exprParams) isColExist(colName string) bool {
for _, name := range exprs.colNames { for _, name := range exprs.colNames {
if strings.EqualFold(trimQuote(name), trimQuote(colName)) { if strings.EqualFold(schemas.CommonQuoter.Trim(name), schemas.CommonQuoter.Trim(colName)) {
return true return true
} }
} }

View File

@ -1,19 +0,0 @@
// 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
func trimQuote(s string) string {
if len(s) == 0 {
return s
}
if s[0] == '`' {
s = s[1:]
}
if len(s) > 0 && s[len(s)-1] == '`' {
return s[:len(s)-1]
}
return s
}