Refactor orderby and support arguments #2150
|
@ -400,7 +400,7 @@ func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName
|
|||
" `COLUMN_KEY`, `EXTRA`, `COLUMN_COMMENT`, `CHARACTER_MAXIMUM_LENGTH`, " +
|
||||
alreadyQuoted + " AS NEEDS_QUOTE " +
|
||||
"FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?" +
|
||||
" ORDER BY `COLUMNS`.ORDINAL_POSITION"
|
||||
" ORDER BY `COLUMNS`.ORDINAL_POSITION ASC"
|
||||
|
||||
rows, err := queryer.QueryContext(ctx, s, args...)
|
||||
if err != nil {
|
||||
|
|
12
engine.go
12
engine.go
|
@ -380,7 +380,7 @@ func (engine *Engine) loadTableInfo(table *schemas.Table) error {
|
|||
seq = 0
|
||||
}
|
||||
}
|
||||
var colName = strings.Trim(parts[0], `"`)
|
||||
colName := strings.Trim(parts[0], `"`)
|
||||
if col := table.GetColumn(colName); col != nil {
|
||||
col.Indexes[index.Name] = index.Type
|
||||
} else {
|
||||
|
@ -502,9 +502,9 @@ func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w
|
|||
}
|
||||
}
|
||||
|
||||
var dstTableName = dstTable.Name
|
||||
var quoter = dstDialect.Quoter().Quote
|
||||
var quotedDstTableName = quoter(dstTable.Name)
|
||||
dstTableName := dstTable.Name
|
||||
quoter := dstDialect.Quoter().Quote
|
||||
quotedDstTableName := quoter(dstTable.Name)
|
||||
if dstDialect.URI().Schema != "" {
|
||||
dstTableName = fmt.Sprintf("%s.%s", dstDialect.URI().Schema, dstTable.Name)
|
||||
quotedDstTableName = fmt.Sprintf("%s.%s", quoter(dstDialect.URI().Schema), quoter(dstTable.Name))
|
||||
|
@ -1006,10 +1006,10 @@ func (engine *Engine) Asc(colNames ...string) *Session {
|
|||
}
|
||||
|
||||
// OrderBy will generate "ORDER BY order"
|
||||
func (engine *Engine) OrderBy(order string) *Session {
|
||||
func (engine *Engine) OrderBy(order interface{}, args ...interface{}) *Session {
|
||||
session := engine.NewSession()
|
||||
session.isAutoClose = true
|
||||
return session.OrderBy(order)
|
||||
return session.OrderBy(order, args...)
|
||||
}
|
||||
|
||||
// Prepare enables prepare statement
|
||||
|
|
2
go.mod
2
go.mod
|
@ -17,5 +17,5 @@ require (
|
|||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/ziutek/mymysql v1.5.4
|
||||
modernc.org/sqlite v1.14.2
|
||||
xorm.io/builder v0.3.9
|
||||
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978
|
||||
)
|
||||
|
|
4
go.sum
4
go.sum
|
@ -659,5 +659,5 @@ modernc.org/z v1.2.19 h1:BGyRFWhDVn5LFS5OcX4Yd/MlpRTOc7hOPTdcIpCiUao=
|
|||
modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||
xorm.io/builder v0.3.9 h1:Sd65/LdWyO7LR8+Cbd+e7mm3sK/7U9k0jS3999IDHMc=
|
||||
xorm.io/builder v0.3.9/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 h1:bvLlAPW1ZMTWA32LuZMBEGHAUOcATZjzHcotf3SWweM=
|
||||
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||
|
|
|
@ -247,6 +247,10 @@ func TestOrder(t *testing.T) {
|
|||
users2 := make([]Userinfo, 0)
|
||||
err = testEngine.Asc("id", "username").Desc("height").Find(&users2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
users = make([]Userinfo, 0)
|
||||
err = testEngine.OrderBy("CASE WHEN username LIKE ? THEN 0 ELSE 1 END DESC", "a").Find(&users)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGroupBy(t *testing.T) {
|
||||
|
|
|
@ -54,7 +54,7 @@ type Interface interface {
|
|||
Nullable(...string) *Session
|
||||
Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session
|
||||
Omit(columns ...string) *Session
|
||||
OrderBy(order string) *Session
|
||||
OrderBy(order interface{}, args ...interface{}) *Session
|
||||
Ping() error
|
||||
Query(sqlOrArgs ...interface{}) (resultsSlice []map[string][]byte, err error)
|
||||
QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error)
|
||||
|
|
111
internal/statements/cond.go
Normal file
111
internal/statements/cond.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
// Copyright 2022 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 statements
|
||||
|
||||
import (
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
type QuoteReplacer struct {
|
||||
*builder.BytesWriter
|
||||
quoter schemas.Quoter
|
||||
}
|
||||
|
||||
func (q *QuoteReplacer) Write(p []byte) (n int, err error) {
|
||||
c := q.quoter.Replace(string(p))
|
||||
return q.BytesWriter.Builder.WriteString(c)
|
||||
}
|
||||
|
||||
func (statement *Statement) QuoteReplacer(w *builder.BytesWriter) *QuoteReplacer {
|
||||
return &QuoteReplacer{
|
||||
BytesWriter: w,
|
||||
quoter: statement.dialect.Quoter(),
|
||||
}
|
||||
}
|
||||
|
||||
// Where add Where statement
|
||||
func (statement *Statement) Where(query interface{}, args ...interface{}) *Statement {
|
||||
return statement.And(query, args...)
|
||||
}
|
||||
|
||||
// And add Where & and statement
|
||||
func (statement *Statement) And(query interface{}, args ...interface{}) *Statement {
|
||||
switch qr := query.(type) {
|
||||
case string:
|
||||
cond := builder.Expr(qr, args...)
|
||||
statement.cond = statement.cond.And(cond)
|
||||
case map[string]interface{}:
|
||||
cond := make(builder.Eq)
|
||||
for k, v := range qr {
|
||||
cond[statement.quote(k)] = v
|
||||
}
|
||||
statement.cond = statement.cond.And(cond)
|
||||
case builder.Cond:
|
||||
statement.cond = statement.cond.And(qr)
|
||||
for _, v := range args {
|
||||
if vv, ok := v.(builder.Cond); ok {
|
||||
statement.cond = statement.cond.And(vv)
|
||||
}
|
||||
}
|
||||
default:
|
||||
statement.LastError = ErrConditionType
|
||||
}
|
||||
|
||||
return statement
|
||||
}
|
||||
|
||||
// Or add Where & Or statement
|
||||
func (statement *Statement) Or(query interface{}, args ...interface{}) *Statement {
|
||||
switch qr := query.(type) {
|
||||
case string:
|
||||
cond := builder.Expr(qr, args...)
|
||||
statement.cond = statement.cond.Or(cond)
|
||||
case map[string]interface{}:
|
||||
cond := make(builder.Eq)
|
||||
for k, v := range qr {
|
||||
cond[statement.quote(k)] = v
|
||||
}
|
||||
statement.cond = statement.cond.Or(cond)
|
||||
case builder.Cond:
|
||||
statement.cond = statement.cond.Or(qr)
|
||||
for _, v := range args {
|
||||
if vv, ok := v.(builder.Cond); ok {
|
||||
statement.cond = statement.cond.Or(vv)
|
||||
}
|
||||
}
|
||||
default:
|
||||
statement.LastError = ErrConditionType
|
||||
}
|
||||
return statement
|
||||
}
|
||||
|
||||
// In generate "Where column IN (?) " statement
|
||||
func (statement *Statement) In(column string, args ...interface{}) *Statement {
|
||||
in := builder.In(statement.quote(column), args...)
|
||||
statement.cond = statement.cond.And(in)
|
||||
return statement
|
||||
}
|
||||
|
||||
// NotIn generate "Where column NOT IN (?) " statement
|
||||
func (statement *Statement) NotIn(column string, args ...interface{}) *Statement {
|
||||
notIn := builder.NotIn(statement.quote(column), args...)
|
||||
statement.cond = statement.cond.And(notIn)
|
||||
return statement
|
||||
}
|
||||
|
||||
// SetNoAutoCondition if you do not want convert bean's field as query condition, then use this function
|
||||
func (statement *Statement) SetNoAutoCondition(no ...bool) *Statement {
|
||||
statement.NoAutoCondition = true
|
||||
if len(no) > 0 {
|
||||
statement.NoAutoCondition = no[0]
|
||||
}
|
||||
return statement
|
||||
}
|
||||
|
||||
// Conds returns condtions
|
||||
func (statement *Statement) Conds() builder.Cond {
|
||||
return statement.cond
|
||||
}
|
78
internal/statements/join.go
Normal file
78
internal/statements/join.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
// Copyright 2022 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 statements
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm/dialects"
|
||||
"xorm.io/xorm/internal/utils"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
// Join The joinOP should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
|
||||
func (statement *Statement) Join(joinOP string, tablename interface{}, condition string, args ...interface{}) *Statement {
|
||||
var buf strings.Builder
|
||||
if len(statement.JoinStr) > 0 {
|
||||
fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP)
|
||||
} else {
|
||||
fmt.Fprintf(&buf, "%v JOIN ", joinOP)
|
||||
}
|
||||
|
||||
switch tp := tablename.(type) {
|
||||
case builder.Builder:
|
||||
subSQL, subQueryArgs, err := tp.ToSQL()
|
||||
if err != nil {
|
||||
statement.LastError = err
|
||||
return statement
|
||||
}
|
||||
|
||||
fields := strings.Split(tp.TableName(), ".")
|
||||
aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1])
|
||||
aliasName = schemas.CommonQuoter.Trim(aliasName)
|
||||
|
||||
fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condition))
|
||||
statement.joinArgs = append(statement.joinArgs, subQueryArgs...)
|
||||
case *builder.Builder:
|
||||
subSQL, subQueryArgs, err := tp.ToSQL()
|
||||
if err != nil {
|
||||
statement.LastError = err
|
||||
return statement
|
||||
}
|
||||
|
||||
fields := strings.Split(tp.TableName(), ".")
|
||||
aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1])
|
||||
aliasName = schemas.CommonQuoter.Trim(aliasName)
|
||||
|
||||
fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condition))
|
||||
statement.joinArgs = append(statement.joinArgs, subQueryArgs...)
|
||||
default:
|
||||
tbName := dialects.FullTableName(statement.dialect, statement.tagParser.GetTableMapper(), tablename, true)
|
||||
if !utils.IsSubQuery(tbName) {
|
||||
var buf strings.Builder
|
||||
_ = statement.dialect.Quoter().QuoteTo(&buf, tbName)
|
||||
tbName = buf.String()
|
||||
} else {
|
||||
tbName = statement.ReplaceQuote(tbName)
|
||||
}
|
||||
fmt.Fprintf(&buf, "%s ON %v", tbName, statement.ReplaceQuote(condition))
|
||||
}
|
||||
|
||||
statement.JoinStr = buf.String()
|
||||
statement.joinArgs = append(statement.joinArgs, args...)
|
||||
return statement
|
||||
}
|
||||
|
||||
func (statement *Statement) writeJoin(w builder.Writer) error {
|
||||
if statement.JoinStr != "" {
|
||||
if _, err := fmt.Fprint(w, " ", statement.JoinStr); err != nil {
|
||||
return err
|
||||
}
|
||||
w.Append(statement.joinArgs...)
|
||||
}
|
||||
return nil
|
||||
}
|
90
internal/statements/order_by.go
Normal file
90
internal/statements/order_by.go
Normal file
|
@ -0,0 +1,90 @@
|
|||
// Copyright 2022 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 statements
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
func (statement *Statement) HasOrderBy() bool {
|
||||
return statement.orderStr != ""
|
||||
}
|
||||
|
||||
// ResetOrderBy reset ordery conditions
|
||||
func (statement *Statement) ResetOrderBy() {
|
||||
statement.orderStr = ""
|
||||
statement.orderArgs = nil
|
||||
}
|
||||
|
||||
// WriteOrderBy write order by to writer
|
||||
func (statement *Statement) WriteOrderBy(w builder.Writer) error {
|
||||
if len(statement.orderStr) > 0 {
|
||||
if _, err := fmt.Fprintf(w, " ORDER BY %s", statement.orderStr); err != nil {
|
||||
return err
|
||||
}
|
||||
w.Append(statement.orderArgs...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OrderBy generate "Order By order" statement
|
||||
func (statement *Statement) OrderBy(order interface{}, args ...interface{}) *Statement {
|
||||
if len(statement.orderStr) > 0 {
|
||||
statement.orderStr += ", "
|
||||
}
|
||||
var rawOrder string
|
||||
switch t := order.(type) {
|
||||
case (*builder.Expression):
|
||||
rawOrder = t.Content()
|
||||
args = t.Args()
|
||||
case string:
|
||||
rawOrder = t
|
||||
default:
|
||||
statement.LastError = ErrUnSupportedSQLType
|
||||
return statement
|
||||
}
|
||||
statement.orderStr += statement.ReplaceQuote(rawOrder)
|
||||
if len(args) > 0 {
|
||||
statement.orderArgs = append(statement.orderArgs, args...)
|
||||
}
|
||||
return statement
|
||||
}
|
||||
|
||||
// Desc generate `ORDER BY xx DESC`
|
||||
func (statement *Statement) Desc(colNames ...string) *Statement {
|
||||
var buf strings.Builder
|
||||
if len(statement.orderStr) > 0 {
|
||||
fmt.Fprint(&buf, statement.orderStr, ", ")
|
||||
}
|
||||
for i, col := range colNames {
|
||||
if i > 0 {
|
||||
fmt.Fprint(&buf, ", ")
|
||||
}
|
||||
_ = statement.dialect.Quoter().QuoteTo(&buf, col)
|
||||
fmt.Fprint(&buf, " DESC")
|
||||
}
|
||||
statement.orderStr = buf.String()
|
||||
return statement
|
||||
}
|
||||
|
||||
// Asc provide asc order by query condition, the input parameters are columns.
|
||||
func (statement *Statement) Asc(colNames ...string) *Statement {
|
||||
var buf strings.Builder
|
||||
if len(statement.orderStr) > 0 {
|
||||
fmt.Fprint(&buf, statement.orderStr, ", ")
|
||||
}
|
||||
for i, col := range colNames {
|
||||
if i > 0 {
|
||||
fmt.Fprint(&buf, ", ")
|
||||
}
|
||||
_ = statement.dialect.Quoter().QuoteTo(&buf, col)
|
||||
fmt.Fprint(&buf, " ASC")
|
||||
}
|
||||
statement.orderStr = buf.String()
|
||||
return statement
|
||||
}
|
|
@ -11,6 +11,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm/internal/utils"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
|
@ -28,7 +29,7 @@ func (statement *Statement) GenQuerySQL(sqlOrArgs ...interface{}) (string, []int
|
|||
return "", nil, ErrTableNotFound
|
||||
}
|
||||
|
||||
var columnStr = statement.ColumnStr()
|
||||
columnStr := statement.ColumnStr()
|
||||
if len(statement.SelectStr) > 0 {
|
||||
columnStr = statement.SelectStr
|
||||
} else {
|
||||
|
@ -58,19 +59,7 @@ func (statement *Statement) GenQuerySQL(sqlOrArgs ...interface{}) (string, []int
|
|||
return "", nil, err
|
||||
}
|
||||
|
||||
sqlStr, condArgs, err := statement.genSelectSQL(columnStr, true, true)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
args := append(statement.joinArgs, condArgs...)
|
||||
|
||||
// for mssql and use limit
|
||||
qs := strings.Count(sqlStr, "?")
|
||||
if len(args)*2 == qs {
|
||||
args = append(args, args...)
|
||||
}
|
||||
|
||||
return sqlStr, args, nil
|
||||
return statement.genSelectSQL(columnStr, true, true)
|
||||
}
|
||||
|
||||
// GenSumSQL generates sum SQL
|
||||
|
@ -83,7 +72,7 @@ func (statement *Statement) GenSumSQL(bean interface{}, columns ...string) (stri
|
|||
return "", nil, err
|
||||
}
|
||||
|
||||
var sumStrs = make([]string, 0, len(columns))
|
||||
sumStrs := make([]string, 0, len(columns))
|
||||
for _, colName := range columns {
|
||||
if !strings.Contains(colName, " ") && !strings.Contains(colName, "(") {
|
||||
colName = statement.quote(colName)
|
||||
|
@ -94,16 +83,11 @@ func (statement *Statement) GenSumSQL(bean interface{}, columns ...string) (stri
|
|||
}
|
||||
sumSelect := strings.Join(sumStrs, ", ")
|
||||
|
||||
if err := statement.mergeConds(bean); err != nil {
|
||||
if err := statement.MergeConds(bean); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
sqlStr, condArgs, err := statement.genSelectSQL(sumSelect, true, true)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return sqlStr, append(statement.joinArgs, condArgs...), nil
|
||||
return statement.genSelectSQL(sumSelect, true, true)
|
||||
}
|
||||
|
||||
// GenGetSQL generates Get SQL
|
||||
|
@ -119,7 +103,7 @@ func (statement *Statement) GenGetSQL(bean interface{}) (string, []interface{},
|
|||
}
|
||||
}
|
||||
|
||||
var columnStr = statement.ColumnStr()
|
||||
columnStr := statement.ColumnStr()
|
||||
if len(statement.SelectStr) > 0 {
|
||||
columnStr = statement.SelectStr
|
||||
} else {
|
||||
|
@ -146,7 +130,7 @@ func (statement *Statement) GenGetSQL(bean interface{}) (string, []interface{},
|
|||
}
|
||||
|
||||
if isStruct {
|
||||
if err := statement.mergeConds(bean); err != nil {
|
||||
if err := statement.MergeConds(bean); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
} else {
|
||||
|
@ -155,12 +139,7 @@ func (statement *Statement) GenGetSQL(bean interface{}) (string, []interface{},
|
|||
}
|
||||
}
|
||||
|
||||
sqlStr, condArgs, err := statement.genSelectSQL(columnStr, true, true)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return sqlStr, append(statement.joinArgs, condArgs...), nil
|
||||
return statement.genSelectSQL(columnStr, true, true)
|
||||
}
|
||||
|
||||
// GenCountSQL generates the SQL for counting
|
||||
|
@ -175,12 +154,12 @@ func (statement *Statement) GenCountSQL(beans ...interface{}) (string, []interfa
|
|||
if err := statement.SetRefBean(beans[0]); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if err := statement.mergeConds(beans[0]); err != nil {
|
||||
if err := statement.MergeConds(beans[0]); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var selectSQL = statement.SelectStr
|
||||
selectSQL := statement.SelectStr
|
||||
if len(selectSQL) <= 0 {
|
||||
if statement.IsDistinct {
|
||||
selectSQL = fmt.Sprintf("count(DISTINCT %s)", statement.ColumnStr())
|
||||
|
@ -206,55 +185,58 @@ func (statement *Statement) GenCountSQL(beans ...interface{}) (string, []interfa
|
|||
sqlStr = fmt.Sprintf("SELECT %s FROM (%s) sub", selectSQL, sqlStr)
|
||||
}
|
||||
|
||||
return sqlStr, append(statement.joinArgs, condArgs...), nil
|
||||
return sqlStr, condArgs, nil
|
||||
}
|
||||
|
||||
func (statement *Statement) fromBuilder() *strings.Builder {
|
||||
var builder strings.Builder
|
||||
var quote = statement.quote
|
||||
var dialect = statement.dialect
|
||||
|
||||
builder.WriteString(" FROM ")
|
||||
|
||||
if dialect.URI().DBType == schemas.MSSQL && strings.Contains(statement.TableName(), "..") {
|
||||
builder.WriteString(statement.TableName())
|
||||
} else {
|
||||
builder.WriteString(quote(statement.TableName()))
|
||||
func (statement *Statement) writeFrom(w builder.Writer) error {
|
||||
if _, err := fmt.Fprint(w, " FROM "); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := statement.writeTableName(w); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := statement.writeAlias(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return statement.writeJoin(w)
|
||||
}
|
||||
|
||||
if statement.TableAlias != "" {
|
||||
if dialect.URI().DBType == schemas.ORACLE {
|
||||
builder.WriteString(" ")
|
||||
} else {
|
||||
builder.WriteString(" AS ")
|
||||
func (statement *Statement) writeLimitOffset(w builder.Writer) error {
|
||||
if statement.Start > 0 {
|
||||
if statement.LimitN != nil {
|
||||
_, err := fmt.Fprintf(w, " LIMIT %v OFFSET %v", *statement.LimitN, statement.Start)
|
||||
return err
|
||||
}
|
||||
builder.WriteString(quote(statement.TableAlias))
|
||||
_, err := fmt.Fprintf(w, " LIMIT 0 OFFSET %v", statement.Start)
|
||||
return err
|
||||
}
|
||||
if statement.JoinStr != "" {
|
||||
builder.WriteString(" ")
|
||||
builder.WriteString(statement.JoinStr)
|
||||
if statement.LimitN != nil {
|
||||
_, err := fmt.Fprint(w, " LIMIT ", *statement.LimitN)
|
||||
return err
|
||||
}
|
||||
return &builder
|
||||
// no limit statement
|
||||
return nil
|
||||
}
|
||||
|
||||
func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderBy bool) (string, []interface{}, error) {
|
||||
var (
|
||||
distinct string
|
||||
dialect = statement.dialect
|
||||
fromStr = statement.fromBuilder().String()
|
||||
top, mssqlCondi, whereStr string
|
||||
distinct string
|
||||
dialect = statement.dialect
|
||||
top, whereStr string
|
||||
mssqlCondi = builder.NewWriter()
|
||||
)
|
||||
|
||||
if statement.IsDistinct && !strings.HasPrefix(columnStr, "count") {
|
||||
distinct = "DISTINCT "
|
||||
}
|
||||
|
||||
condSQL, condArgs, err := statement.GenCondSQL(statement.cond)
|
||||
if err != nil {
|
||||
condWriter := builder.NewWriter()
|
||||
if err := statement.cond.WriteTo(statement.QuoteReplacer(condWriter)); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if len(condSQL) > 0 {
|
||||
whereStr = fmt.Sprintf(" WHERE %s", condSQL)
|
||||
|
||||
if condWriter.Len() > 0 {
|
||||
whereStr = " WHERE "
|
||||
}
|
||||
|
||||
pLimitN := statement.LimitN
|
||||
|
@ -289,49 +271,81 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB
|
|||
}
|
||||
}
|
||||
|
||||
var orderStr string
|
||||
if needOrderBy && len(statement.OrderStr) > 0 {
|
||||
orderStr = fmt.Sprintf(" ORDER BY %s", statement.OrderStr)
|
||||
if _, err := fmt.Fprintf(mssqlCondi, "(%s NOT IN (SELECT TOP %d %s",
|
||||
column, statement.Start, column); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
var groupStr string
|
||||
if len(statement.GroupByStr) > 0 {
|
||||
groupStr = fmt.Sprintf(" GROUP BY %s", statement.GroupByStr)
|
||||
if err := statement.writeFrom(mssqlCondi); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if whereStr != "" {
|
||||
if _, err := fmt.Fprint(mssqlCondi, whereStr); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if err := utils.WriteBuilder(mssqlCondi, statement.QuoteReplacer(condWriter)); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
if needOrderBy {
|
||||
if err := statement.WriteOrderBy(mssqlCondi); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
if err := statement.WriteGroupBy(mssqlCondi); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if _, err := fmt.Fprint(mssqlCondi, "))"); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
mssqlCondi = fmt.Sprintf("(%s NOT IN (SELECT TOP %d %s%s%s%s%s))",
|
||||
column, statement.Start, column, fromStr, whereStr, orderStr, groupStr)
|
||||
}
|
||||
}
|
||||
|
||||
var buf strings.Builder
|
||||
fmt.Fprintf(&buf, "SELECT %v%v%v%v%v", distinct, top, columnStr, fromStr, whereStr)
|
||||
if len(mssqlCondi) > 0 {
|
||||
buf := builder.NewWriter()
|
||||
if _, err := fmt.Fprintf(buf, "SELECT %v%v%v", distinct, top, columnStr); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if err := statement.writeFrom(buf); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if whereStr != "" {
|
||||
if _, err := fmt.Fprint(buf, whereStr); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if err := utils.WriteBuilder(buf, statement.QuoteReplacer(condWriter)); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
if mssqlCondi.Len() > 0 {
|
||||
if len(whereStr) > 0 {
|
||||
fmt.Fprint(&buf, " AND ", mssqlCondi)
|
||||
if _, err := fmt.Fprint(buf, " AND "); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(&buf, " WHERE ", mssqlCondi)
|
||||
if _, err := fmt.Fprint(buf, " WHERE "); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := utils.WriteBuilder(buf, mssqlCondi); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if statement.GroupByStr != "" {
|
||||
fmt.Fprint(&buf, " GROUP BY ", statement.GroupByStr)
|
||||
if err := statement.WriteGroupBy(buf); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if statement.HavingStr != "" {
|
||||
fmt.Fprint(&buf, " ", statement.HavingStr)
|
||||
if err := statement.writeHaving(buf); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if needOrderBy && statement.OrderStr != "" {
|
||||
fmt.Fprint(&buf, " ORDER BY ", statement.OrderStr)
|
||||
if needOrderBy {
|
||||
if err := statement.WriteOrderBy(buf); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
if needLimit {
|
||||
if dialect.URI().DBType != schemas.MSSQL && dialect.URI().DBType != schemas.ORACLE {
|
||||
if statement.Start > 0 {
|
||||
if pLimitN != nil {
|
||||
fmt.Fprintf(&buf, " LIMIT %v OFFSET %v", *pLimitN, statement.Start)
|
||||
} else {
|
||||
fmt.Fprintf(&buf, " LIMIT 0 OFFSET %v", statement.Start)
|
||||
}
|
||||
} else if pLimitN != nil {
|
||||
fmt.Fprint(&buf, " LIMIT ", *pLimitN)
|
||||
if err := statement.writeLimitOffset(buf); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
} else if dialect.URI().DBType == schemas.ORACLE {
|
||||
if pLimitN != nil {
|
||||
|
@ -341,16 +355,16 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB
|
|||
if rawColStr == "*" {
|
||||
rawColStr = "at.*"
|
||||
}
|
||||
fmt.Fprintf(&buf, "SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d",
|
||||
fmt.Fprintf(buf, "SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d",
|
||||
columnStr, rawColStr, oldString, statement.Start+*pLimitN, statement.Start)
|
||||
}
|
||||
}
|
||||
}
|
||||
if statement.IsForUpdate {
|
||||
return dialect.ForUpdateSQL(buf.String()), condArgs, nil
|
||||
return dialect.ForUpdateSQL(buf.String()), buf.Args(), nil
|
||||
}
|
||||
|
||||
return buf.String(), condArgs, nil
|
||||
return buf.String(), buf.Args(), nil
|
||||
}
|
||||
|
||||
// GenExistSQL generates Exist SQL
|
||||
|
@ -359,10 +373,6 @@ func (statement *Statement) GenExistSQL(bean ...interface{}) (string, []interfac
|
|||
return statement.GenRawSQL(), statement.RawParams, nil
|
||||
}
|
||||
|
||||
var sqlStr string
|
||||
var args []interface{}
|
||||
var joinStr string
|
||||
var err error
|
||||
var b interface{}
|
||||
if len(bean) > 0 {
|
||||
b = bean[0]
|
||||
|
@ -381,45 +391,70 @@ func (statement *Statement) GenExistSQL(bean ...interface{}) (string, []interfac
|
|||
if len(tableName) <= 0 {
|
||||
return "", nil, ErrTableNotFound
|
||||
}
|
||||
if statement.RefTable == nil {
|
||||
tableName = statement.quote(tableName)
|
||||
if len(statement.JoinStr) > 0 {
|
||||
joinStr = statement.JoinStr
|
||||
}
|
||||
if statement.RefTable != nil {
|
||||
return statement.Limit(1).GenGetSQL(b)
|
||||
}
|
||||
|
||||
tableName = statement.quote(tableName)
|
||||
|
||||
buf := builder.NewWriter()
|
||||
if statement.dialect.URI().DBType == schemas.MSSQL {
|
||||
if _, err := fmt.Fprintf(buf, "SELECT TOP 1 * FROM %s", tableName); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if err := statement.writeJoin(buf); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if statement.Conds().IsValid() {
|
||||
condSQL, condArgs, err := statement.GenCondSQL(statement.Conds())
|
||||
if err != nil {
|
||||
if _, err := fmt.Fprintf(buf, " WHERE "); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if statement.dialect.URI().DBType == schemas.MSSQL {
|
||||
sqlStr = fmt.Sprintf("SELECT TOP 1 * FROM %s %s WHERE %s", tableName, joinStr, condSQL)
|
||||
} else if statement.dialect.URI().DBType == schemas.ORACLE {
|
||||
sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE (%s) %s AND ROWNUM=1", tableName, joinStr, condSQL)
|
||||
} else {
|
||||
sqlStr = fmt.Sprintf("SELECT 1 FROM %s %s WHERE %s LIMIT 1", tableName, joinStr, condSQL)
|
||||
if err := statement.Conds().WriteTo(statement.QuoteReplacer(buf)); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
args = condArgs
|
||||
} else {
|
||||
if statement.dialect.URI().DBType == schemas.MSSQL {
|
||||
sqlStr = fmt.Sprintf("SELECT TOP 1 * FROM %s %s", tableName, joinStr)
|
||||
} else if statement.dialect.URI().DBType == schemas.ORACLE {
|
||||
sqlStr = fmt.Sprintf("SELECT * FROM %s %s WHERE ROWNUM=1", tableName, joinStr)
|
||||
} else {
|
||||
sqlStr = fmt.Sprintf("SELECT 1 FROM %s %s LIMIT 1", tableName, joinStr)
|
||||
}
|
||||
} else if statement.dialect.URI().DBType == schemas.ORACLE {
|
||||
if _, err := fmt.Fprintf(buf, "SELECT * FROM %s", tableName); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if err := statement.writeJoin(buf); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if _, err := fmt.Fprintf(buf, " WHERE "); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if statement.Conds().IsValid() {
|
||||
if err := statement.Conds().WriteTo(statement.QuoteReplacer(buf)); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
args = []interface{}{}
|
||||
if _, err := fmt.Fprintf(buf, " AND "); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
if _, err := fmt.Fprintf(buf, "ROWNUM=1"); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
} else {
|
||||
statement.Limit(1)
|
||||
sqlStr, args, err = statement.GenGetSQL(b)
|
||||
if err != nil {
|
||||
if _, err := fmt.Fprintf(buf, "SELECT 1 FROM %s", tableName); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if err := statement.writeJoin(buf); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if statement.Conds().IsValid() {
|
||||
if _, err := fmt.Fprintf(buf, " WHERE "); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if err := statement.Conds().WriteTo(statement.QuoteReplacer(buf)); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
if _, err := fmt.Fprintf(buf, " LIMIT 1"); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return sqlStr, args, nil
|
||||
return buf.String(), buf.Args(), nil
|
||||
}
|
||||
|
||||
// GenFindSQL generates Find SQL
|
||||
|
@ -428,15 +463,11 @@ func (statement *Statement) GenFindSQL(autoCond builder.Cond) (string, []interfa
|
|||
return statement.GenRawSQL(), statement.RawParams, nil
|
||||
}
|
||||
|
||||
var sqlStr string
|
||||
var args []interface{}
|
||||
var err error
|
||||
|
||||
if len(statement.TableName()) <= 0 {
|
||||
return "", nil, ErrTableNotFound
|
||||
}
|
||||
|
||||
var columnStr = statement.ColumnStr()
|
||||
columnStr := statement.ColumnStr()
|
||||
if len(statement.SelectStr) > 0 {
|
||||
columnStr = statement.SelectStr
|
||||
} else {
|
||||
|
@ -464,16 +495,5 @@ func (statement *Statement) GenFindSQL(autoCond builder.Cond) (string, []interfa
|
|||
|
||||
statement.cond = statement.cond.And(autoCond)
|
||||
|
||||
sqlStr, condArgs, err := statement.genSelectSQL(columnStr, true, true)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
args = append(statement.joinArgs, condArgs...)
|
||||
// for mssql and use limit
|
||||
qs := strings.Count(sqlStr, "?")
|
||||
if len(args)*2 == qs {
|
||||
args = append(args, args...)
|
||||
}
|
||||
|
||||
return sqlStr, args, nil
|
||||
return statement.genSelectSQL(columnStr, true, true)
|
||||
}
|
||||
|
|
137
internal/statements/select.go
Normal file
137
internal/statements/select.go
Normal file
|
@ -0,0 +1,137 @@
|
|||
// Copyright 2022 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 statements
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
// Select replace select
|
||||
func (statement *Statement) Select(str string) *Statement {
|
||||
statement.SelectStr = statement.ReplaceQuote(str)
|
||||
return statement
|
||||
}
|
||||
|
||||
func col2NewCols(columns ...string) []string {
|
||||
newColumns := make([]string, 0, len(columns))
|
||||
for _, col := range columns {
|
||||
col = strings.Replace(col, "`", "", -1)
|
||||
col = strings.Replace(col, `"`, "", -1)
|
||||
ccols := strings.Split(col, ",")
|
||||
for _, c := range ccols {
|
||||
newColumns = append(newColumns, strings.TrimSpace(c))
|
||||
}
|
||||
}
|
||||
return newColumns
|
||||
}
|
||||
|
||||
// Cols generate "col1, col2" statement
|
||||
func (statement *Statement) Cols(columns ...string) *Statement {
|
||||
cols := col2NewCols(columns...)
|
||||
for _, nc := range cols {
|
||||
statement.ColumnMap.Add(nc)
|
||||
}
|
||||
return statement
|
||||
}
|
||||
|
||||
// ColumnStr returns column string
|
||||
func (statement *Statement) ColumnStr() string {
|
||||
return statement.dialect.Quoter().Join(statement.ColumnMap, ", ")
|
||||
}
|
||||
|
||||
// AllCols update use only: update all columns
|
||||
func (statement *Statement) AllCols() *Statement {
|
||||
statement.useAllCols = true
|
||||
return statement
|
||||
}
|
||||
|
||||
// MustCols update use only: must update columns
|
||||
func (statement *Statement) MustCols(columns ...string) *Statement {
|
||||
newColumns := col2NewCols(columns...)
|
||||
for _, nc := range newColumns {
|
||||
statement.MustColumnMap[strings.ToLower(nc)] = true
|
||||
}
|
||||
return statement
|
||||
}
|
||||
|
||||
// UseBool indicates that use bool fields as update contents and query contiditions
|
||||
func (statement *Statement) UseBool(columns ...string) *Statement {
|
||||
if len(columns) > 0 {
|
||||
statement.MustCols(columns...)
|
||||
} else {
|
||||
statement.allUseBool = true
|
||||
}
|
||||
return statement
|
||||
}
|
||||
|
||||
// Omit do not use the columns
|
||||
func (statement *Statement) Omit(columns ...string) {
|
||||
newColumns := col2NewCols(columns...)
|
||||
for _, nc := range newColumns {
|
||||
statement.OmitColumnMap = append(statement.OmitColumnMap, nc)
|
||||
}
|
||||
}
|
||||
|
||||
func (statement *Statement) genColumnStr() string {
|
||||
if statement.RefTable == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var buf strings.Builder
|
||||
columns := statement.RefTable.Columns()
|
||||
|
||||
for _, col := range columns {
|
||||
if statement.OmitColumnMap.Contain(col.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(statement.ColumnMap) > 0 && !statement.ColumnMap.Contain(col.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
if col.MapType == schemas.ONLYTODB {
|
||||
continue
|
||||
}
|
||||
|
||||
if buf.Len() != 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
|
||||
if statement.JoinStr != "" {
|
||||
if statement.TableAlias != "" {
|
||||
buf.WriteString(statement.TableAlias)
|
||||
} else {
|
||||
buf.WriteString(statement.TableName())
|
||||
}
|
||||
|
||||
buf.WriteString(".")
|
||||
}
|
||||
|
||||
statement.dialect.Quoter().QuoteTo(&buf, col.Name)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (statement *Statement) colName(col *schemas.Column, tableName string) string {
|
||||
if statement.needTableName() {
|
||||
nm := tableName
|
||||
if len(statement.TableAlias) > 0 {
|
||||
nm = statement.TableAlias
|
||||
}
|
||||
return fmt.Sprintf("%s.%s", statement.quote(nm), statement.quote(col.Name))
|
||||
}
|
||||
return statement.quote(col.Name)
|
||||
}
|
||||
|
||||
// Distinct generates "DISTINCT col1, col2 " statement
|
||||
func (statement *Statement) Distinct(columns ...string) *Statement {
|
||||
statement.IsDistinct = true
|
||||
statement.Cols(columns...)
|
||||
return statement
|
||||
}
|
|
@ -43,7 +43,8 @@ type Statement struct {
|
|||
Start int
|
||||
LimitN *int
|
||||
idParam schemas.PK
|
||||
OrderStr string
|
||||
orderStr string
|
||||
orderArgs []interface{}
|
||||
JoinStr string
|
||||
joinArgs []interface{}
|
||||
GroupByStr string
|
||||
|
@ -101,15 +102,6 @@ func (statement *Statement) GenRawSQL() string {
|
|||
return statement.ReplaceQuote(statement.RawSQL)
|
||||
}
|
||||
|
||||
// GenCondSQL generates condition SQL
|
||||
func (statement *Statement) GenCondSQL(condOrBuilder interface{}) (string, []interface{}, error) {
|
||||
condSQL, condArgs, err := builder.ToSQL(condOrBuilder)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return statement.ReplaceQuote(condSQL), condArgs, nil
|
||||
}
|
||||
|
||||
// ReplaceQuote replace sql key words with quote
|
||||
func (statement *Statement) ReplaceQuote(sql string) string {
|
||||
if sql == "" || statement.dialect.URI().DBType == schemas.MYSQL ||
|
||||
|
@ -129,7 +121,7 @@ func (statement *Statement) Reset() {
|
|||
statement.RefTable = nil
|
||||
statement.Start = 0
|
||||
statement.LimitN = nil
|
||||
statement.OrderStr = ""
|
||||
statement.ResetOrderBy()
|
||||
statement.UseCascade = true
|
||||
statement.JoinStr = ""
|
||||
statement.joinArgs = make([]interface{}, 0)
|
||||
|
@ -164,21 +156,6 @@ func (statement *Statement) Reset() {
|
|||
statement.LastError = nil
|
||||
}
|
||||
|
||||
// SetNoAutoCondition if you do not want convert bean's field as query condition, then use this function
|
||||
func (statement *Statement) SetNoAutoCondition(no ...bool) *Statement {
|
||||
statement.NoAutoCondition = true
|
||||
if len(no) > 0 {
|
||||
statement.NoAutoCondition = no[0]
|
||||
}
|
||||
return statement
|
||||
}
|
||||
|
||||
// Alias set the table alias
|
||||
func (statement *Statement) Alias(alias string) *Statement {
|
||||
statement.TableAlias = alias
|
||||
return statement
|
||||
}
|
||||
|
||||
// SQL adds raw sql statement
|
||||
func (statement *Statement) SQL(query interface{}, args ...interface{}) *Statement {
|
||||
switch query.(type) {
|
||||
|
@ -198,80 +175,10 @@ func (statement *Statement) SQL(query interface{}, args ...interface{}) *Stateme
|
|||
return statement
|
||||
}
|
||||
|
||||
// Where add Where statement
|
||||
func (statement *Statement) Where(query interface{}, args ...interface{}) *Statement {
|
||||
return statement.And(query, args...)
|
||||
}
|
||||
|
||||
func (statement *Statement) quote(s string) string {
|
||||
return statement.dialect.Quoter().Quote(s)
|
||||
}
|
||||
|
||||
// And add Where & and statement
|
||||
func (statement *Statement) And(query interface{}, args ...interface{}) *Statement {
|
||||
switch qr := query.(type) {
|
||||
case string:
|
||||
cond := builder.Expr(qr, args...)
|
||||
statement.cond = statement.cond.And(cond)
|
||||
case map[string]interface{}:
|
||||
cond := make(builder.Eq)
|
||||
for k, v := range qr {
|
||||
cond[statement.quote(k)] = v
|
||||
}
|
||||
statement.cond = statement.cond.And(cond)
|
||||
case builder.Cond:
|
||||
statement.cond = statement.cond.And(qr)
|
||||
for _, v := range args {
|
||||
if vv, ok := v.(builder.Cond); ok {
|
||||
statement.cond = statement.cond.And(vv)
|
||||
}
|
||||
}
|
||||
default:
|
||||
statement.LastError = ErrConditionType
|
||||
}
|
||||
|
||||
return statement
|
||||
}
|
||||
|
||||
// Or add Where & Or statement
|
||||
func (statement *Statement) Or(query interface{}, args ...interface{}) *Statement {
|
||||
switch qr := query.(type) {
|
||||
case string:
|
||||
cond := builder.Expr(qr, args...)
|
||||
statement.cond = statement.cond.Or(cond)
|
||||
case map[string]interface{}:
|
||||
cond := make(builder.Eq)
|
||||
for k, v := range qr {
|
||||
cond[statement.quote(k)] = v
|
||||
}
|
||||
statement.cond = statement.cond.Or(cond)
|
||||
case builder.Cond:
|
||||
statement.cond = statement.cond.Or(qr)
|
||||
for _, v := range args {
|
||||
if vv, ok := v.(builder.Cond); ok {
|
||||
statement.cond = statement.cond.Or(vv)
|
||||
}
|
||||
}
|
||||
default:
|
||||
statement.LastError = ErrConditionType
|
||||
}
|
||||
return statement
|
||||
}
|
||||
|
||||
// In generate "Where column IN (?) " statement
|
||||
func (statement *Statement) In(column string, args ...interface{}) *Statement {
|
||||
in := builder.In(statement.quote(column), args...)
|
||||
statement.cond = statement.cond.And(in)
|
||||
return statement
|
||||
}
|
||||
|
||||
// NotIn generate "Where column NOT IN (?) " statement
|
||||
func (statement *Statement) NotIn(column string, args ...interface{}) *Statement {
|
||||
notIn := builder.NotIn(statement.quote(column), args...)
|
||||
statement.cond = statement.cond.And(notIn)
|
||||
return statement
|
||||
}
|
||||
|
||||
// SetRefValue set ref value
|
||||
func (statement *Statement) SetRefValue(v reflect.Value) error {
|
||||
var err error
|
||||
|
@ -302,26 +209,6 @@ func (statement *Statement) needTableName() bool {
|
|||
return len(statement.JoinStr) > 0
|
||||
}
|
||||
|
||||
func (statement *Statement) colName(col *schemas.Column, tableName string) string {
|
||||
if statement.needTableName() {
|
||||
nm := tableName
|
||||
if len(statement.TableAlias) > 0 {
|
||||
nm = statement.TableAlias
|
||||
}
|
||||
return fmt.Sprintf("%s.%s", statement.quote(nm), statement.quote(col.Name))
|
||||
}
|
||||
return statement.quote(col.Name)
|
||||
}
|
||||
|
||||
// TableName return current tableName
|
||||
func (statement *Statement) TableName() string {
|
||||
if statement.AltTableName != "" {
|
||||
return statement.AltTableName
|
||||
}
|
||||
|
||||
return statement.tableName
|
||||
}
|
||||
|
||||
// Incr Generate "Update ... Set column = column + arg" statement
|
||||
func (statement *Statement) Incr(column string, arg ...interface{}) *Statement {
|
||||
if len(arg) > 0 {
|
||||
|
@ -352,85 +239,12 @@ func (statement *Statement) SetExpr(column string, expression interface{}) *Stat
|
|||
return statement
|
||||
}
|
||||
|
||||
// Distinct generates "DISTINCT col1, col2 " statement
|
||||
func (statement *Statement) Distinct(columns ...string) *Statement {
|
||||
statement.IsDistinct = true
|
||||
statement.Cols(columns...)
|
||||
return statement
|
||||
}
|
||||
|
||||
// ForUpdate generates "SELECT ... FOR UPDATE" statement
|
||||
func (statement *Statement) ForUpdate() *Statement {
|
||||
statement.IsForUpdate = true
|
||||
return statement
|
||||
}
|
||||
|
||||
// Select replace select
|
||||
func (statement *Statement) Select(str string) *Statement {
|
||||
statement.SelectStr = statement.ReplaceQuote(str)
|
||||
return statement
|
||||
}
|
||||
|
||||
func col2NewCols(columns ...string) []string {
|
||||
newColumns := make([]string, 0, len(columns))
|
||||
for _, col := range columns {
|
||||
col = strings.Replace(col, "`", "", -1)
|
||||
col = strings.Replace(col, `"`, "", -1)
|
||||
ccols := strings.Split(col, ",")
|
||||
for _, c := range ccols {
|
||||
newColumns = append(newColumns, strings.TrimSpace(c))
|
||||
}
|
||||
}
|
||||
return newColumns
|
||||
}
|
||||
|
||||
// Cols generate "col1, col2" statement
|
||||
func (statement *Statement) Cols(columns ...string) *Statement {
|
||||
cols := col2NewCols(columns...)
|
||||
for _, nc := range cols {
|
||||
statement.ColumnMap.Add(nc)
|
||||
}
|
||||
return statement
|
||||
}
|
||||
|
||||
// ColumnStr returns column string
|
||||
func (statement *Statement) ColumnStr() string {
|
||||
return statement.dialect.Quoter().Join(statement.ColumnMap, ", ")
|
||||
}
|
||||
|
||||
// AllCols update use only: update all columns
|
||||
func (statement *Statement) AllCols() *Statement {
|
||||
statement.useAllCols = true
|
||||
return statement
|
||||
}
|
||||
|
||||
// MustCols update use only: must update columns
|
||||
func (statement *Statement) MustCols(columns ...string) *Statement {
|
||||
newColumns := col2NewCols(columns...)
|
||||
for _, nc := range newColumns {
|
||||
statement.MustColumnMap[strings.ToLower(nc)] = true
|
||||
}
|
||||
return statement
|
||||
}
|
||||
|
||||
// UseBool indicates that use bool fields as update contents and query contiditions
|
||||
func (statement *Statement) UseBool(columns ...string) *Statement {
|
||||
if len(columns) > 0 {
|
||||
statement.MustCols(columns...)
|
||||
} else {
|
||||
statement.allUseBool = true
|
||||
}
|
||||
return statement
|
||||
}
|
||||
|
||||
// Omit do not use the columns
|
||||
func (statement *Statement) Omit(columns ...string) {
|
||||
newColumns := col2NewCols(columns...)
|
||||
for _, nc := range newColumns {
|
||||
statement.OmitColumnMap = append(statement.OmitColumnMap, nc)
|
||||
}
|
||||
}
|
||||
|
||||
// Nullable Update use only: update columns to null when value is nullable and zero-value
|
||||
func (statement *Statement) Nullable(columns ...string) {
|
||||
newColumns := col2NewCols(columns...)
|
||||
|
@ -454,54 +268,6 @@ func (statement *Statement) Limit(limit int, start ...int) *Statement {
|
|||
return statement
|
||||
}
|
||||
|
||||
// OrderBy generate "Order By order" statement
|
||||
func (statement *Statement) OrderBy(order string) *Statement {
|
||||
if len(statement.OrderStr) > 0 {
|
||||
statement.OrderStr += ", "
|
||||
}
|
||||
statement.OrderStr += statement.ReplaceQuote(order)
|
||||
return statement
|
||||
}
|
||||
|
||||
// Desc generate `ORDER BY xx DESC`
|
||||
func (statement *Statement) Desc(colNames ...string) *Statement {
|
||||
var buf strings.Builder
|
||||
if len(statement.OrderStr) > 0 {
|
||||
fmt.Fprint(&buf, statement.OrderStr, ", ")
|
||||
}
|
||||
for i, col := range colNames {
|
||||
if i > 0 {
|
||||
fmt.Fprint(&buf, ", ")
|
||||
}
|
||||
_ = statement.dialect.Quoter().QuoteTo(&buf, col)
|
||||
fmt.Fprint(&buf, " DESC")
|
||||
}
|
||||
statement.OrderStr = buf.String()
|
||||
return statement
|
||||
}
|
||||
|
||||
// Asc provide asc order by query condition, the input parameters are columns.
|
||||
func (statement *Statement) Asc(colNames ...string) *Statement {
|
||||
var buf strings.Builder
|
||||
if len(statement.OrderStr) > 0 {
|
||||
fmt.Fprint(&buf, statement.OrderStr, ", ")
|
||||
}
|
||||
for i, col := range colNames {
|
||||
if i > 0 {
|
||||
fmt.Fprint(&buf, ", ")
|
||||
}
|
||||
_ = statement.dialect.Quoter().QuoteTo(&buf, col)
|
||||
fmt.Fprint(&buf, " ASC")
|
||||
}
|
||||
statement.OrderStr = buf.String()
|
||||
return statement
|
||||
}
|
||||
|
||||
// Conds returns condtions
|
||||
func (statement *Statement) Conds() builder.Cond {
|
||||
return statement.cond
|
||||
}
|
||||
|
||||
// SetTable tempororily set table name, the parameter could be a string or a pointer of struct
|
||||
func (statement *Statement) SetTable(tableNameOrBean interface{}) error {
|
||||
v := rValue(tableNameOrBean)
|
||||
|
@ -518,71 +284,34 @@ func (statement *Statement) SetTable(tableNameOrBean interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Join The joinOP should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
|
||||
func (statement *Statement) Join(joinOP string, tablename interface{}, condition string, args ...interface{}) *Statement {
|
||||
var buf strings.Builder
|
||||
if len(statement.JoinStr) > 0 {
|
||||
fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP)
|
||||
} else {
|
||||
fmt.Fprintf(&buf, "%v JOIN ", joinOP)
|
||||
}
|
||||
|
||||
switch tp := tablename.(type) {
|
||||
case builder.Builder:
|
||||
subSQL, subQueryArgs, err := tp.ToSQL()
|
||||
if err != nil {
|
||||
statement.LastError = err
|
||||
return statement
|
||||
}
|
||||
|
||||
fields := strings.Split(tp.TableName(), ".")
|
||||
aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1])
|
||||
aliasName = schemas.CommonQuoter.Trim(aliasName)
|
||||
|
||||
fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condition))
|
||||
statement.joinArgs = append(statement.joinArgs, subQueryArgs...)
|
||||
case *builder.Builder:
|
||||
subSQL, subQueryArgs, err := tp.ToSQL()
|
||||
if err != nil {
|
||||
statement.LastError = err
|
||||
return statement
|
||||
}
|
||||
|
||||
fields := strings.Split(tp.TableName(), ".")
|
||||
aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1])
|
||||
aliasName = schemas.CommonQuoter.Trim(aliasName)
|
||||
|
||||
fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condition))
|
||||
statement.joinArgs = append(statement.joinArgs, subQueryArgs...)
|
||||
default:
|
||||
tbName := dialects.FullTableName(statement.dialect, statement.tagParser.GetTableMapper(), tablename, true)
|
||||
if !utils.IsSubQuery(tbName) {
|
||||
var buf strings.Builder
|
||||
_ = statement.dialect.Quoter().QuoteTo(&buf, tbName)
|
||||
tbName = buf.String()
|
||||
} else {
|
||||
tbName = statement.ReplaceQuote(tbName)
|
||||
}
|
||||
fmt.Fprintf(&buf, "%s ON %v", tbName, statement.ReplaceQuote(condition))
|
||||
}
|
||||
|
||||
statement.JoinStr = buf.String()
|
||||
statement.joinArgs = append(statement.joinArgs, args...)
|
||||
return statement
|
||||
}
|
||||
|
||||
// GroupBy generate "Group By keys" statement
|
||||
func (statement *Statement) GroupBy(keys string) *Statement {
|
||||
statement.GroupByStr = statement.ReplaceQuote(keys)
|
||||
return statement
|
||||
}
|
||||
|
||||
func (statement *Statement) WriteGroupBy(w builder.Writer) error {
|
||||
if statement.GroupByStr == "" {
|
||||
return nil
|
||||
}
|
||||
_, err := fmt.Fprintf(w, " GROUP BY %s", statement.GroupByStr)
|
||||
return err
|
||||
}
|
||||
|
||||
// Having generate "Having conditions" statement
|
||||
func (statement *Statement) Having(conditions string) *Statement {
|
||||
statement.HavingStr = fmt.Sprintf("HAVING %v", statement.ReplaceQuote(conditions))
|
||||
return statement
|
||||
}
|
||||
|
||||
func (statement *Statement) writeHaving(w builder.Writer) error {
|
||||
if statement.HavingStr == "" {
|
||||
return nil
|
||||
}
|
||||
_, err := fmt.Fprint(w, " ", statement.HavingStr)
|
||||
return err
|
||||
}
|
||||
|
||||
// SetUnscoped always disable struct tag "deleted"
|
||||
func (statement *Statement) SetUnscoped() *Statement {
|
||||
statement.unscoped = true
|
||||
|
@ -594,47 +323,6 @@ func (statement *Statement) GetUnscoped() bool {
|
|||
return statement.unscoped
|
||||
}
|
||||
|
||||
func (statement *Statement) genColumnStr() string {
|
||||
if statement.RefTable == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var buf strings.Builder
|
||||
columns := statement.RefTable.Columns()
|
||||
|
||||
for _, col := range columns {
|
||||
if statement.OmitColumnMap.Contain(col.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(statement.ColumnMap) > 0 && !statement.ColumnMap.Contain(col.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
if col.MapType == schemas.ONLYTODB {
|
||||
continue
|
||||
}
|
||||
|
||||
if buf.Len() != 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
|
||||
if statement.JoinStr != "" {
|
||||
if statement.TableAlias != "" {
|
||||
buf.WriteString(statement.TableAlias)
|
||||
} else {
|
||||
buf.WriteString(statement.TableName())
|
||||
}
|
||||
|
||||
buf.WriteString(".")
|
||||
}
|
||||
|
||||
statement.dialect.Quoter().QuoteTo(&buf, col.Name)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// GenIndexSQL generated create index SQL
|
||||
func (statement *Statement) GenIndexSQL() []string {
|
||||
var sqls []string
|
||||
|
@ -914,7 +602,8 @@ func (statement *Statement) BuildConds(table *schemas.Table, bean interface{}, i
|
|||
statement.unscoped, statement.MustColumnMap, statement.TableName(), statement.TableAlias, addedTableName)
|
||||
}
|
||||
|
||||
func (statement *Statement) mergeConds(bean interface{}) error {
|
||||
// MergeConds merge conditions from bean and id
|
||||
func (statement *Statement) MergeConds(bean interface{}) error {
|
||||
if !statement.NoAutoCondition && statement.RefTable != nil {
|
||||
addedTableName := (len(statement.JoinStr) > 0)
|
||||
autoCond, err := statement.BuildConds(statement.RefTable, bean, true, true, false, true, addedTableName)
|
||||
|
@ -927,15 +616,6 @@ func (statement *Statement) mergeConds(bean interface{}) error {
|
|||
return statement.ProcessIDParam()
|
||||
}
|
||||
|
||||
// GenConds generates conditions
|
||||
func (statement *Statement) GenConds(bean interface{}) (string, []interface{}, error) {
|
||||
if err := statement.mergeConds(bean); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return statement.GenCondSQL(statement.cond)
|
||||
}
|
||||
|
||||
func (statement *Statement) quoteColumnStr(columnStr string) string {
|
||||
columns := strings.Split(columnStr, ",")
|
||||
return statement.dialect.Quoter().Join(columns, ",")
|
||||
|
|
56
internal/statements/table_name.go
Normal file
56
internal/statements/table_name.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2022 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 statements
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
// TableName return current tableName
|
||||
func (statement *Statement) TableName() string {
|
||||
if statement.AltTableName != "" {
|
||||
return statement.AltTableName
|
||||
}
|
||||
|
||||
return statement.tableName
|
||||
}
|
||||
|
||||
// Alias set the table alias
|
||||
func (statement *Statement) Alias(alias string) *Statement {
|
||||
statement.TableAlias = alias
|
||||
return statement
|
||||
}
|
||||
|
||||
func (statement *Statement) writeAlias(w builder.Writer) error {
|
||||
if statement.TableAlias != "" {
|
||||
if statement.dialect.URI().DBType == schemas.ORACLE {
|
||||
if _, err := fmt.Fprint(w, " ", statement.quote(statement.TableAlias)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := fmt.Fprint(w, " AS ", statement.quote(statement.TableAlias)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (statement *Statement) writeTableName(w builder.Writer) error {
|
||||
if statement.dialect.URI().DBType == schemas.MSSQL && strings.Contains(statement.TableName(), "..") {
|
||||
if _, err := fmt.Fprint(w, statement.TableName()); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := fmt.Fprint(w, statement.quote(statement.TableName())); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
27
internal/utils/builder.go
Normal file
27
internal/utils/builder.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2022 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 utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
type BuildReader interface {
|
||||
String() string
|
||||
Args() []interface{}
|
||||
}
|
||||
|
||||
// WriteBuilder writes writers to one
|
||||
func WriteBuilder(w *builder.BytesWriter, inputs ...BuildReader) error {
|
||||
for _, input := range inputs {
|
||||
if _, err := fmt.Fprint(w, input.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
w.Append(input.Args()...)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -275,8 +275,8 @@ func (session *Session) Limit(limit int, start ...int) *Session {
|
|||
|
||||
// OrderBy provide order by query condition, the input parameter is the content
|
||||
// after order by on a sql statement.
|
||||
func (session *Session) OrderBy(order string) *Session {
|
||||
session.statement.OrderBy(order)
|
||||
func (session *Session) OrderBy(order interface{}, args ...interface{}) *Session {
|
||||
session.statement.OrderBy(order, args...)
|
||||
return session
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,9 @@ import (
|
|||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm/caches"
|
||||
"xorm.io/xorm/internal/utils"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
|
@ -99,10 +101,9 @@ func (session *Session) Delete(beans ...interface{}) (int64, error) {
|
|||
}
|
||||
|
||||
var (
|
||||
condSQL string
|
||||
condArgs []interface{}
|
||||
err error
|
||||
bean interface{}
|
||||
condWriter = builder.NewWriter()
|
||||
err error
|
||||
bean interface{}
|
||||
)
|
||||
if len(beans) > 0 {
|
||||
bean = beans[0]
|
||||
|
@ -116,115 +117,97 @@ func (session *Session) Delete(beans ...interface{}) (int64, error) {
|
|||
processor.BeforeDelete()
|
||||
}
|
||||
|
||||
condSQL, condArgs, err = session.statement.GenConds(bean)
|
||||
} else {
|
||||
condSQL, condArgs, err = session.statement.GenCondSQL(session.statement.Conds())
|
||||
if err = session.statement.MergeConds(bean); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
if err = session.statement.Conds().WriteTo(session.statement.QuoteReplacer(condWriter)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
pLimitN := session.statement.LimitN
|
||||
if len(condSQL) == 0 && (pLimitN == nil || *pLimitN == 0) {
|
||||
if condWriter.Len() == 0 && (pLimitN == nil || *pLimitN == 0) {
|
||||
return 0, ErrNeedDeletedCond
|
||||
}
|
||||
|
||||
var tableNameNoQuote = session.statement.TableName()
|
||||
var tableName = session.engine.Quote(tableNameNoQuote)
|
||||
var table = session.statement.RefTable
|
||||
var deleteSQL string
|
||||
if len(condSQL) > 0 {
|
||||
deleteSQL = fmt.Sprintf("DELETE FROM %v WHERE %v", tableName, condSQL)
|
||||
} else {
|
||||
deleteSQL = fmt.Sprintf("DELETE FROM %v", tableName)
|
||||
tableNameNoQuote := session.statement.TableName()
|
||||
tableName := session.engine.Quote(tableNameNoQuote)
|
||||
table := session.statement.RefTable
|
||||
deleteSQLWriter := builder.NewWriter()
|
||||
fmt.Fprintf(deleteSQLWriter, "DELETE FROM %v", tableName)
|
||||
if condWriter.Len() > 0 {
|
||||
fmt.Fprintf(deleteSQLWriter, " WHERE %v", condWriter.String())
|
||||
deleteSQLWriter.Append(condWriter.Args()...)
|
||||
}
|
||||
|
||||
var orderSQL string
|
||||
if len(session.statement.OrderStr) > 0 {
|
||||
orderSQL += fmt.Sprintf(" ORDER BY %s", session.statement.OrderStr)
|
||||
orderSQLWriter := builder.NewWriter()
|
||||
if err := session.statement.WriteOrderBy(orderSQLWriter); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if pLimitN != nil && *pLimitN > 0 {
|
||||
limitNValue := *pLimitN
|
||||
orderSQL += fmt.Sprintf(" LIMIT %d", limitNValue)
|
||||
if _, err := fmt.Fprintf(orderSQLWriter, " LIMIT %d", limitNValue); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(orderSQL) > 0 {
|
||||
orderCondWriter := builder.NewWriter()
|
||||
if orderSQLWriter.Len() > 0 {
|
||||
switch session.engine.dialect.URI().DBType {
|
||||
case schemas.POSTGRES:
|
||||
inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL)
|
||||
if len(condSQL) > 0 {
|
||||
deleteSQL += " AND " + inSQL
|
||||
if condWriter.Len() > 0 {
|
||||
fmt.Fprintf(orderCondWriter, " AND ")
|
||||
} else {
|
||||
deleteSQL += " WHERE " + inSQL
|
||||
fmt.Fprintf(orderCondWriter, " WHERE ")
|
||||
}
|
||||
fmt.Fprintf(orderCondWriter, "ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQLWriter.String())
|
||||
orderCondWriter.Append(orderSQLWriter.Args()...)
|
||||
case schemas.SQLITE:
|
||||
inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL)
|
||||
if len(condSQL) > 0 {
|
||||
deleteSQL += " AND " + inSQL
|
||||
if condWriter.Len() > 0 {
|
||||
fmt.Fprintf(orderCondWriter, " AND ")
|
||||
} else {
|
||||
deleteSQL += " WHERE " + inSQL
|
||||
fmt.Fprintf(orderCondWriter, " WHERE ")
|
||||
}
|
||||
fmt.Fprintf(orderCondWriter, "rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQLWriter.String())
|
||||
// TODO: how to handle delete limit on mssql?
|
||||
case schemas.MSSQL:
|
||||
return 0, ErrNotImplemented
|
||||
default:
|
||||
deleteSQL += orderSQL
|
||||
fmt.Fprint(orderCondWriter, orderSQLWriter.String())
|
||||
orderCondWriter.Append(orderSQLWriter.Args()...)
|
||||
}
|
||||
}
|
||||
|
||||
var realSQL string
|
||||
argsForCache := make([]interface{}, 0, len(condArgs)*2)
|
||||
realSQLWriter := builder.NewWriter()
|
||||
argsForCache := make([]interface{}, 0, len(deleteSQLWriter.Args())*2)
|
||||
copy(argsForCache, deleteSQLWriter.Args())
|
||||
argsForCache = append(deleteSQLWriter.Args(), argsForCache...)
|
||||
if session.statement.GetUnscoped() || table == nil || table.DeletedColumn() == nil { // tag "deleted" is disabled
|
||||
realSQL = deleteSQL
|
||||
copy(argsForCache, condArgs)
|
||||
argsForCache = append(condArgs, argsForCache...)
|
||||
if err := utils.WriteBuilder(realSQLWriter, deleteSQLWriter, orderCondWriter); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
// !oinume! sqlStrForCache and argsForCache is needed to behave as executing "DELETE FROM ..." for caches.
|
||||
copy(argsForCache, condArgs)
|
||||
argsForCache = append(condArgs, argsForCache...)
|
||||
|
||||
deletedColumn := table.DeletedColumn()
|
||||
realSQL = fmt.Sprintf("UPDATE %v SET %v = ? WHERE %v",
|
||||
if _, err := fmt.Fprintf(realSQLWriter, "UPDATE %v SET %v = ? WHERE %v",
|
||||
session.engine.Quote(session.statement.TableName()),
|
||||
session.engine.Quote(deletedColumn.Name),
|
||||
condSQL)
|
||||
|
||||
if len(orderSQL) > 0 {
|
||||
switch session.engine.dialect.URI().DBType {
|
||||
case schemas.POSTGRES:
|
||||
inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL)
|
||||
if len(condSQL) > 0 {
|
||||
realSQL += " AND " + inSQL
|
||||
} else {
|
||||
realSQL += " WHERE " + inSQL
|
||||
}
|
||||
case schemas.SQLITE:
|
||||
inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL)
|
||||
if len(condSQL) > 0 {
|
||||
realSQL += " AND " + inSQL
|
||||
} else {
|
||||
realSQL += " WHERE " + inSQL
|
||||
}
|
||||
// TODO: how to handle delete limit on mssql?
|
||||
case schemas.MSSQL:
|
||||
return 0, ErrNotImplemented
|
||||
default:
|
||||
realSQL += orderSQL
|
||||
}
|
||||
condWriter.String()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// !oinume! Insert nowTime to the head of session.statement.Params
|
||||
condArgs = append(condArgs, "")
|
||||
paramsLen := len(condArgs)
|
||||
copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1])
|
||||
|
||||
val, t, err := session.engine.nowTime(deletedColumn)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
condArgs[0] = val
|
||||
realSQLWriter.Append(val)
|
||||
realSQLWriter.Append(condWriter.Args()...)
|
||||
|
||||
var colName = deletedColumn.Name
|
||||
if err := utils.WriteBuilder(realSQLWriter, orderCondWriter); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
colName := deletedColumn.Name
|
||||
session.afterClosures = append(session.afterClosures, func(bean interface{}) {
|
||||
col := table.GetColumn(colName)
|
||||
setColumnTime(bean, col, t)
|
||||
|
@ -232,11 +215,11 @@ func (session *Session) Delete(beans ...interface{}) (int64, error) {
|
|||
}
|
||||
|
||||
if cacher := session.engine.GetCacher(tableNameNoQuote); cacher != nil && session.statement.UseCache {
|
||||
_ = session.cacheDelete(table, tableNameNoQuote, deleteSQL, argsForCache...)
|
||||
_ = session.cacheDelete(table, tableNameNoQuote, deleteSQLWriter.String(), argsForCache...)
|
||||
}
|
||||
|
||||
session.statement.RefTable = table
|
||||
res, err := session.exec(realSQL, condArgs...)
|
||||
res, err := session.exec(realSQLWriter.String(), realSQLWriter.Args()...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
|
@ -60,9 +60,7 @@ func (session *Session) FindAndCount(rowsSlicePtr interface{}, condiBean ...inte
|
|||
if len(session.statement.ColumnMap) > 0 && !session.statement.IsDistinct {
|
||||
session.statement.ColumnMap = []string{}
|
||||
}
|
||||
if session.statement.OrderStr != "" {
|
||||
session.statement.OrderStr = ""
|
||||
}
|
||||
session.statement.ResetOrderBy()
|
||||
if session.statement.LimitN != nil {
|
||||
session.statement.LimitN = nil
|
||||
}
|
||||
|
@ -85,15 +83,15 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{})
|
|||
}
|
||||
|
||||
sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
|
||||
var isSlice = sliceValue.Kind() == reflect.Slice
|
||||
var isMap = sliceValue.Kind() == reflect.Map
|
||||
isSlice := sliceValue.Kind() == reflect.Slice
|
||||
isMap := sliceValue.Kind() == reflect.Map
|
||||
if !isSlice && !isMap {
|
||||
return errors.New("needs a pointer to a slice or a map")
|
||||
}
|
||||
|
||||
sliceElementType := sliceValue.Type().Elem()
|
||||
|
||||
var tp = tpStruct
|
||||
tp := tpStruct
|
||||
if session.statement.RefTable == nil {
|
||||
if sliceElementType.Kind() == reflect.Ptr {
|
||||
if sliceElementType.Elem().Kind() == reflect.Struct {
|
||||
|
@ -190,7 +188,7 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect
|
|||
return err
|
||||
}
|
||||
|
||||
var newElemFunc = func(fields []string) reflect.Value {
|
||||
newElemFunc := func(fields []string) reflect.Value {
|
||||
return utils.New(elemType, len(fields), len(fields))
|
||||
}
|
||||
|
||||
|
@ -235,7 +233,7 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect
|
|||
}
|
||||
|
||||
if elemType.Kind() == reflect.Struct {
|
||||
var newValue = newElemFunc(fields)
|
||||
newValue := newElemFunc(fields)
|
||||
tb, err := session.engine.tagParser.ParseWithCache(newValue)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -249,7 +247,7 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect
|
|||
}
|
||||
|
||||
for rows.Next() {
|
||||
var newValue = newElemFunc(fields)
|
||||
newValue := newElemFunc(fields)
|
||||
bean := newValue.Interface()
|
||||
|
||||
switch elemType.Kind() {
|
||||
|
@ -310,7 +308,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
|
|||
session.engine.logger.Debugf("[cacheFind] ids length > 500, no cache")
|
||||
return ErrCacheFailed
|
||||
}
|
||||
var res = make([]string, len(table.PrimaryKeys))
|
||||
res := make([]string, len(table.PrimaryKeys))
|
||||
err = rows.ScanSlice(&res)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -342,7 +340,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
|
|||
|
||||
ididxes := make(map[string]int)
|
||||
var ides []schemas.PK
|
||||
var temps = make([]interface{}, len(ids))
|
||||
temps := make([]interface{}, len(ids))
|
||||
|
||||
for idx, id := range ids {
|
||||
sid, err := id.ToString()
|
||||
|
@ -457,7 +455,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
|
|||
sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(bean))))
|
||||
}
|
||||
} else if sliceValue.Kind() == reflect.Map {
|
||||
var key = ids[j]
|
||||
key := ids[j]
|
||||
keyType := sliceValue.Type().Key()
|
||||
keyValue := reflect.New(keyType)
|
||||
var ikey interface{}
|
||||
|
|
|
@ -60,7 +60,7 @@ func (session *Session) cacheUpdate(table *schemas.Table, tableName, sqlStr stri
|
|||
|
||||
ids = make([]schemas.PK, 0)
|
||||
for rows.Next() {
|
||||
var res = make([]string, len(table.PrimaryKeys))
|
||||
res := make([]string, len(table.PrimaryKeys))
|
||||
err = rows.ScanSlice(&res)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -176,8 +176,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
// --
|
||||
|
||||
var err error
|
||||
var isMap = t.Kind() == reflect.Map
|
||||
var isStruct = t.Kind() == reflect.Struct
|
||||
isMap := t.Kind() == reflect.Map
|
||||
isStruct := t.Kind() == reflect.Struct
|
||||
if isStruct {
|
||||
if err := session.statement.SetRefBean(bean); err != nil {
|
||||
return 0, err
|
||||
|
@ -226,7 +226,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
args = append(args, val)
|
||||
}
|
||||
|
||||
var colName = col.Name
|
||||
colName := col.Name
|
||||
if isStruct {
|
||||
session.afterClosures = append(session.afterClosures, func(bean interface{}) {
|
||||
col := table.GetColumn(colName)
|
||||
|
@ -258,10 +258,11 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
}
|
||||
colNames = append(colNames, session.engine.Quote(expr.ColName)+"="+tp)
|
||||
case *builder.Builder:
|
||||
subQuery, subArgs, err := session.statement.GenCondSQL(tp)
|
||||
subQuery, subArgs, err := builder.ToSQL(tp)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
subQuery = session.statement.ReplaceQuote(subQuery)
|
||||
colNames = append(colNames, session.engine.Quote(expr.ColName)+"=("+subQuery+")")
|
||||
args = append(args, subArgs...)
|
||||
default:
|
||||
|
@ -279,7 +280,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
condBeanIsStruct := false
|
||||
if len(condiBean) > 0 {
|
||||
if c, ok := condiBean[0].(map[string]interface{}); ok {
|
||||
var eq = make(builder.Eq)
|
||||
eq := make(builder.Eq)
|
||||
for k, v := range c {
|
||||
eq[session.engine.Quote(k)] = v
|
||||
}
|
||||
|
@ -323,11 +324,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
st := session.statement
|
||||
|
||||
var (
|
||||
sqlStr string
|
||||
condArgs []interface{}
|
||||
condSQL string
|
||||
cond = session.statement.Conds().And(autoCond)
|
||||
|
||||
doIncVer = isStruct && (table != nil && table.Version != "" && session.statement.CheckVersion)
|
||||
verValue *reflect.Value
|
||||
)
|
||||
|
@ -347,70 +344,65 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
return 0, ErrNoColumnsTobeUpdated
|
||||
}
|
||||
|
||||
condSQL, condArgs, err = session.statement.GenCondSQL(cond)
|
||||
if err != nil {
|
||||
whereWriter := builder.NewWriter()
|
||||
if cond.IsValid() {
|
||||
fmt.Fprint(whereWriter, "WHERE ")
|
||||
}
|
||||
if err := cond.WriteTo(st.QuoteReplacer(whereWriter)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := st.WriteOrderBy(whereWriter); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if len(condSQL) > 0 {
|
||||
condSQL = "WHERE " + condSQL
|
||||
}
|
||||
|
||||
if st.OrderStr != "" {
|
||||
condSQL += fmt.Sprintf(" ORDER BY %v", st.OrderStr)
|
||||
}
|
||||
|
||||
var tableName = session.statement.TableName()
|
||||
tableName := session.statement.TableName()
|
||||
// TODO: Oracle support needed
|
||||
var top string
|
||||
if st.LimitN != nil {
|
||||
limitValue := *st.LimitN
|
||||
switch session.engine.dialect.URI().DBType {
|
||||
case schemas.MYSQL:
|
||||
condSQL += fmt.Sprintf(" LIMIT %d", limitValue)
|
||||
fmt.Fprintf(whereWriter, " LIMIT %d", limitValue)
|
||||
case schemas.SQLITE:
|
||||
tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", limitValue)
|
||||
fmt.Fprintf(whereWriter, " LIMIT %d", limitValue)
|
||||
|
||||
cond = cond.And(builder.Expr(fmt.Sprintf("rowid IN (SELECT rowid FROM %v %v)",
|
||||
session.engine.Quote(tableName), tempCondSQL), condArgs...))
|
||||
condSQL, condArgs, err = session.statement.GenCondSQL(cond)
|
||||
if err != nil {
|
||||
session.engine.Quote(tableName), whereWriter.String()), whereWriter.Args()...))
|
||||
|
||||
whereWriter = builder.NewWriter()
|
||||
fmt.Fprint(whereWriter, "WHERE ")
|
||||
if err := cond.WriteTo(st.QuoteReplacer(whereWriter)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(condSQL) > 0 {
|
||||
condSQL = "WHERE " + condSQL
|
||||
}
|
||||
case schemas.POSTGRES:
|
||||
tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", limitValue)
|
||||
fmt.Fprintf(whereWriter, " LIMIT %d", limitValue)
|
||||
|
||||
cond = cond.And(builder.Expr(fmt.Sprintf("CTID IN (SELECT CTID FROM %v %v)",
|
||||
session.engine.Quote(tableName), tempCondSQL), condArgs...))
|
||||
condSQL, condArgs, err = session.statement.GenCondSQL(cond)
|
||||
if err != nil {
|
||||
session.engine.Quote(tableName), whereWriter.String()), whereWriter.Args()...))
|
||||
|
||||
whereWriter = builder.NewWriter()
|
||||
fmt.Fprint(whereWriter, "WHERE ")
|
||||
if err := cond.WriteTo(st.QuoteReplacer(whereWriter)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if len(condSQL) > 0 {
|
||||
condSQL = "WHERE " + condSQL
|
||||
}
|
||||
case schemas.MSSQL:
|
||||
if st.OrderStr != "" && table != nil && len(table.PrimaryKeys) == 1 {
|
||||
if st.HasOrderBy() && table != nil && len(table.PrimaryKeys) == 1 {
|
||||
cond = builder.Expr(fmt.Sprintf("%s IN (SELECT TOP (%d) %s FROM %v%v)",
|
||||
table.PrimaryKeys[0], limitValue, table.PrimaryKeys[0],
|
||||
session.engine.Quote(tableName), condSQL), condArgs...)
|
||||
session.engine.Quote(tableName), whereWriter.String()), whereWriter.Args()...)
|
||||
|
||||
condSQL, condArgs, err = session.statement.GenCondSQL(cond)
|
||||
if err != nil {
|
||||
whereWriter = builder.NewWriter()
|
||||
fmt.Fprint(whereWriter, "WHERE ")
|
||||
if err := cond.WriteTo(whereWriter); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(condSQL) > 0 {
|
||||
condSQL = "WHERE " + condSQL
|
||||
}
|
||||
} else {
|
||||
top = fmt.Sprintf("TOP (%d) ", limitValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tableAlias = session.engine.Quote(tableName)
|
||||
tableAlias := session.engine.Quote(tableName)
|
||||
var fromSQL string
|
||||
if session.statement.TableAlias != "" {
|
||||
switch session.engine.dialect.URI().DBType {
|
||||
|
@ -422,14 +414,19 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
}
|
||||
}
|
||||
|
||||
sqlStr = fmt.Sprintf("UPDATE %v%v SET %v %v%v",
|
||||
updateWriter := builder.NewWriter()
|
||||
if _, err := fmt.Fprintf(updateWriter, "UPDATE %v%v SET %v %v",
|
||||
top,
|
||||
tableAlias,
|
||||
strings.Join(colNames, ", "),
|
||||
fromSQL,
|
||||
condSQL)
|
||||
fromSQL); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := utils.WriteBuilder(updateWriter, whereWriter); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
res, err := session.exec(sqlStr, append(args, condArgs...)...)
|
||||
res, err := session.exec(updateWriter.String(), append(args, updateWriter.Args()...)...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if doIncVer {
|
||||
|
@ -535,7 +532,7 @@ func (session *Session) genUpdateColumns(bean interface{}) ([]string, []interfac
|
|||
}
|
||||
args = append(args, val)
|
||||
|
||||
var colName = col.Name
|
||||
colName := col.Name
|
||||
session.afterClosures = append(session.afterClosures, func(bean interface{}) {
|
||||
col := table.GetColumn(colName)
|
||||
setColumnTime(bean, col, t)
|
||||
|
|
Loading…
Reference in New Issue
Block a user