2020-02-28 12:29:08 +00:00
// 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 statements
import (
"errors"
"fmt"
"reflect"
"strings"
"xorm.io/builder"
"xorm.io/xorm/schemas"
)
2021-04-12 08:00:07 +00:00
// GenQuerySQL generate query SQL
2020-02-28 12:29:08 +00:00
func ( statement * Statement ) GenQuerySQL ( sqlOrArgs ... interface { } ) ( string , [ ] interface { } , error ) {
if len ( sqlOrArgs ) > 0 {
2020-03-09 02:17:37 +00:00
return statement . ConvertSQLOrArgs ( sqlOrArgs ... )
2020-02-28 12:29:08 +00:00
}
if statement . RawSQL != "" {
2020-03-09 02:17:37 +00:00
return statement . GenRawSQL ( ) , statement . RawParams , nil
2020-02-28 12:29:08 +00:00
}
if len ( statement . TableName ( ) ) <= 0 {
return "" , nil , ErrTableNotFound
}
2022-04-24 08:28:06 +00:00
columnStr := statement . ColumnStr ( )
2020-02-28 12:29:08 +00:00
if len ( statement . SelectStr ) > 0 {
columnStr = statement . SelectStr
} else {
if statement . JoinStr == "" {
if columnStr == "" {
if statement . GroupByStr != "" {
columnStr = statement . quoteColumnStr ( statement . GroupByStr )
} else {
columnStr = statement . genColumnStr ( )
}
}
} else {
if columnStr == "" {
if statement . GroupByStr != "" {
columnStr = statement . quoteColumnStr ( statement . GroupByStr )
} else {
columnStr = "*"
}
}
}
if columnStr == "" {
columnStr = "*"
}
}
if err := statement . ProcessIDParam ( ) ; err != nil {
return "" , nil , err
}
2020-03-06 06:43:49 +00:00
sqlStr , condArgs , err := statement . genSelectSQL ( columnStr , true , true )
2020-02-28 12:29:08 +00:00
if err != nil {
return "" , nil , err
}
args := append ( statement . joinArgs , condArgs ... )
2020-03-06 06:43:49 +00:00
2020-02-28 12:29:08 +00:00
// for mssql and use limit
qs := strings . Count ( sqlStr , "?" )
if len ( args ) * 2 == qs {
args = append ( args , args ... )
}
return sqlStr , args , nil
}
2021-04-12 08:00:07 +00:00
// GenSumSQL generates sum SQL
2020-02-28 12:29:08 +00:00
func ( statement * Statement ) GenSumSQL ( bean interface { } , columns ... string ) ( string , [ ] interface { } , error ) {
if statement . RawSQL != "" {
2020-03-09 02:17:37 +00:00
return statement . GenRawSQL ( ) , statement . RawParams , nil
2020-02-28 12:29:08 +00:00
}
2021-12-14 01:00:35 +00:00
if err := statement . SetRefBean ( bean ) ; err != nil {
return "" , nil , err
}
2020-02-28 12:29:08 +00:00
2022-04-24 08:28:06 +00:00
sumStrs := make ( [ ] string , 0 , len ( columns ) )
2020-02-28 12:29:08 +00:00
for _ , colName := range columns {
if ! strings . Contains ( colName , " " ) && ! strings . Contains ( colName , "(" ) {
colName = statement . quote ( colName )
2020-03-09 02:17:37 +00:00
} else {
colName = statement . ReplaceQuote ( colName )
2020-02-28 12:29:08 +00:00
}
sumStrs = append ( sumStrs , fmt . Sprintf ( "COALESCE(sum(%s),0)" , colName ) )
}
sumSelect := strings . Join ( sumStrs , ", " )
2020-03-06 06:43:49 +00:00
if err := statement . mergeConds ( bean ) ; err != nil {
2020-02-28 12:29:08 +00:00
return "" , nil , err
}
2020-03-06 06:43:49 +00:00
sqlStr , condArgs , err := statement . genSelectSQL ( sumSelect , true , true )
2020-02-28 12:29:08 +00:00
if err != nil {
return "" , nil , err
}
return sqlStr , append ( statement . joinArgs , condArgs ... ) , nil
}
2021-04-12 08:00:07 +00:00
// GenGetSQL generates Get SQL
2020-02-28 12:29:08 +00:00
func ( statement * Statement ) GenGetSQL ( bean interface { } ) ( string , [ ] interface { } , error ) {
2021-06-07 05:45:29 +00:00
var isStruct bool
if bean != nil {
v := rValue ( bean )
isStruct = v . Kind ( ) == reflect . Struct
if isStruct {
2021-12-14 01:00:35 +00:00
if err := statement . SetRefBean ( bean ) ; err != nil {
return "" , nil , err
}
2021-06-07 05:45:29 +00:00
}
2020-02-28 12:29:08 +00:00
}
2022-04-24 08:28:06 +00:00
columnStr := statement . ColumnStr ( )
2020-02-28 12:29:08 +00:00
if len ( statement . SelectStr ) > 0 {
columnStr = statement . SelectStr
} else {
// TODO: always generate column names, not use * even if join
if len ( statement . JoinStr ) == 0 {
if len ( columnStr ) == 0 {
if len ( statement . GroupByStr ) > 0 {
columnStr = statement . quoteColumnStr ( statement . GroupByStr )
} else {
columnStr = statement . genColumnStr ( )
}
}
} else {
if len ( columnStr ) == 0 {
if len ( statement . GroupByStr ) > 0 {
columnStr = statement . quoteColumnStr ( statement . GroupByStr )
}
}
}
}
if len ( columnStr ) == 0 {
columnStr = "*"
}
if isStruct {
if err := statement . mergeConds ( bean ) ; err != nil {
return "" , nil , err
}
} else {
if err := statement . ProcessIDParam ( ) ; err != nil {
return "" , nil , err
}
}
2020-03-06 06:43:49 +00:00
sqlStr , condArgs , err := statement . genSelectSQL ( columnStr , true , true )
2020-02-28 12:29:08 +00:00
if err != nil {
return "" , nil , err
}
return sqlStr , append ( statement . joinArgs , condArgs ... ) , nil
}
2020-03-11 03:29:43 +00:00
// GenCountSQL generates the SQL for counting
2020-02-28 12:29:08 +00:00
func ( statement * Statement ) GenCountSQL ( beans ... interface { } ) ( string , [ ] interface { } , error ) {
if statement . RawSQL != "" {
2020-03-09 02:17:37 +00:00
return statement . GenRawSQL ( ) , statement . RawParams , nil
2020-02-28 12:29:08 +00:00
}
var condArgs [ ] interface { }
var err error
if len ( beans ) > 0 {
2021-12-14 01:00:35 +00:00
if err := statement . SetRefBean ( beans [ 0 ] ) ; err != nil {
return "" , nil , err
}
2020-03-06 06:43:49 +00:00
if err := statement . mergeConds ( beans [ 0 ] ) ; err != nil {
return "" , nil , err
}
2020-02-28 12:29:08 +00:00
}
2022-04-24 08:28:06 +00:00
selectSQL := statement . SelectStr
2020-02-28 12:29:08 +00:00
if len ( selectSQL ) <= 0 {
if statement . IsDistinct {
selectSQL = fmt . Sprintf ( "count(DISTINCT %s)" , statement . ColumnStr ( ) )
2020-03-11 03:29:43 +00:00
} else if statement . ColumnStr ( ) != "" {
selectSQL = fmt . Sprintf ( "count(%s)" , statement . ColumnStr ( ) )
2020-02-28 12:29:08 +00:00
} else {
selectSQL = "count(*)"
}
}
2021-06-06 12:21:39 +00:00
var subQuerySelect string
if statement . GroupByStr != "" {
subQuerySelect = statement . GroupByStr
} else {
subQuerySelect = selectSQL
}
sqlStr , condArgs , err := statement . genSelectSQL ( subQuerySelect , false , false )
2020-02-28 12:29:08 +00:00
if err != nil {
return "" , nil , err
}
2021-06-06 12:21:39 +00:00
if statement . GroupByStr != "" {
sqlStr = fmt . Sprintf ( "SELECT %s FROM (%s) sub" , selectSQL , sqlStr )
}
2020-02-28 12:29:08 +00:00
return sqlStr , append ( statement . joinArgs , condArgs ... ) , nil
}
2021-09-07 08:03:08 +00:00
func ( statement * Statement ) fromBuilder ( ) * strings . Builder {
var builder strings . Builder
2022-04-24 08:28:06 +00:00
quote := statement . quote
dialect := statement . dialect
2021-09-07 08:03:08 +00:00
builder . WriteString ( " FROM " )
if dialect . URI ( ) . DBType == schemas . MSSQL && strings . Contains ( statement . TableName ( ) , ".." ) {
builder . WriteString ( statement . TableName ( ) )
} else {
builder . WriteString ( quote ( statement . TableName ( ) ) )
}
if statement . TableAlias != "" {
if dialect . URI ( ) . DBType == schemas . ORACLE {
builder . WriteString ( " " )
} else {
builder . WriteString ( " AS " )
}
builder . WriteString ( quote ( statement . TableAlias ) )
}
if statement . JoinStr != "" {
builder . WriteString ( " " )
builder . WriteString ( statement . JoinStr )
}
return & builder
}
2020-03-06 06:43:49 +00:00
func ( statement * Statement ) genSelectSQL ( columnStr string , needLimit , needOrderBy bool ) ( string , [ ] interface { } , error ) {
2020-02-28 12:29:08 +00:00
var (
distinct string
dialect = statement . dialect
2021-09-07 08:03:08 +00:00
fromStr = statement . fromBuilder ( ) . String ( )
2020-02-28 12:29:08 +00:00
top , mssqlCondi , whereStr string
)
2021-09-07 08:03:08 +00:00
2020-02-28 12:29:08 +00:00
if statement . IsDistinct && ! strings . HasPrefix ( columnStr , "count" ) {
distinct = "DISTINCT "
}
2020-03-06 06:43:49 +00:00
2020-03-09 02:17:37 +00:00
condSQL , condArgs , err := statement . GenCondSQL ( statement . cond )
2020-03-06 06:43:49 +00:00
if err != nil {
return "" , nil , err
}
2020-02-28 12:29:08 +00:00
if len ( condSQL ) > 0 {
2021-09-07 08:03:08 +00:00
whereStr = fmt . Sprintf ( " WHERE %s" , condSQL )
2020-02-28 12:29:08 +00:00
}
pLimitN := statement . LimitN
2020-03-07 08:51:30 +00:00
if dialect . URI ( ) . DBType == schemas . MSSQL {
2020-02-28 12:29:08 +00:00
if pLimitN != nil {
LimitNValue := * pLimitN
top = fmt . Sprintf ( "TOP %d " , LimitNValue )
}
if statement . Start > 0 {
2021-07-21 13:13:15 +00:00
if statement . RefTable == nil {
return "" , nil , errors . New ( "Unsupported query limit without reference table" )
}
2020-02-28 12:29:08 +00:00
var column string
if len ( statement . RefTable . PKColumns ( ) ) == 0 {
for _ , index := range statement . RefTable . Indexes {
if len ( index . Cols ) == 1 {
column = index . Cols [ 0 ]
break
}
}
if len ( column ) == 0 {
column = statement . RefTable . ColumnsSeq ( ) [ 0 ]
}
} else {
column = statement . RefTable . PKColumns ( ) [ 0 ] . Name
}
if statement . needTableName ( ) {
if len ( statement . TableAlias ) > 0 {
2021-09-07 08:03:08 +00:00
column = fmt . Sprintf ( "%s.%s" , statement . TableAlias , column )
2020-02-28 12:29:08 +00:00
} else {
2021-09-07 08:03:08 +00:00
column = fmt . Sprintf ( "%s.%s" , statement . TableName ( ) , column )
2020-02-28 12:29:08 +00:00
}
}
var orderStr string
if needOrderBy && len ( statement . OrderStr ) > 0 {
2021-09-07 08:03:08 +00:00
orderStr = fmt . Sprintf ( " ORDER BY %s" , statement . OrderStr )
2020-02-28 12:29:08 +00:00
}
var groupStr string
if len ( statement . GroupByStr ) > 0 {
2021-09-07 08:03:08 +00:00
groupStr = fmt . Sprintf ( " GROUP BY %s" , statement . GroupByStr )
2020-02-28 12:29:08 +00:00
}
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
2022-04-24 08:28:06 +00:00
fmt . Fprint ( & buf , "SELECT " , distinct , top , columnStr , fromStr , whereStr )
2020-02-28 12:29:08 +00:00
if len ( mssqlCondi ) > 0 {
if len ( whereStr ) > 0 {
fmt . Fprint ( & buf , " AND " , mssqlCondi )
} else {
fmt . Fprint ( & buf , " WHERE " , mssqlCondi )
}
}
if statement . GroupByStr != "" {
fmt . Fprint ( & buf , " GROUP BY " , statement . GroupByStr )
}
if statement . HavingStr != "" {
fmt . Fprint ( & buf , " " , statement . HavingStr )
}
if needOrderBy && statement . OrderStr != "" {
fmt . Fprint ( & buf , " ORDER BY " , statement . OrderStr )
}
if needLimit {
2020-03-07 08:51:30 +00:00
if dialect . URI ( ) . DBType != schemas . MSSQL && dialect . URI ( ) . DBType != schemas . ORACLE {
2020-02-28 12:29:08 +00:00
if statement . Start > 0 {
if pLimitN != nil {
2022-04-24 08:28:06 +00:00
fmt . Fprint ( & buf , " LIMIT " , * pLimitN , " OFFSET " , statement . Start )
2020-02-28 12:29:08 +00:00
} else {
2022-04-24 08:28:06 +00:00
fmt . Fprint ( & buf , " LIMIT 0 OFFSET " , statement . Start )
2020-02-28 12:29:08 +00:00
}
} else if pLimitN != nil {
fmt . Fprint ( & buf , " LIMIT " , * pLimitN )
}
2020-03-07 08:51:30 +00:00
} else if dialect . URI ( ) . DBType == schemas . ORACLE {
2022-01-16 10:04:24 +00:00
if pLimitN != nil {
2020-02-28 12:29:08 +00:00
oldString := buf . String ( )
buf . Reset ( )
rawColStr := columnStr
if rawColStr == "*" {
rawColStr = "at.*"
}
2022-04-24 08:28:06 +00:00
fmt . Fprint ( & buf , "SELECT " , columnStr , " FROM (SELECT " , rawColStr , ",ROWNUM RN FROM (" , oldString , ") at WHERE ROWNUM <= " , statement . Start + * pLimitN , ") aat WHERE RN > " ,
statement . Start )
2020-02-28 12:29:08 +00:00
}
}
}
if statement . IsForUpdate {
2020-03-06 06:43:49 +00:00
return dialect . ForUpdateSQL ( buf . String ( ) ) , condArgs , nil
2020-02-28 12:29:08 +00:00
}
2020-03-06 06:43:49 +00:00
return buf . String ( ) , condArgs , nil
2020-02-28 12:29:08 +00:00
}
2021-04-12 08:00:07 +00:00
// GenExistSQL generates Exist SQL
2020-02-28 12:29:08 +00:00
func ( statement * Statement ) GenExistSQL ( bean ... interface { } ) ( string , [ ] interface { } , error ) {
if statement . RawSQL != "" {
2020-03-09 02:17:37 +00:00
return statement . GenRawSQL ( ) , statement . RawParams , nil
2020-02-28 12:29:08 +00:00
}
var sqlStr string
var args [ ] interface { }
var joinStr string
var err error
2021-06-15 12:28:49 +00:00
var b interface { }
2021-06-07 05:45:29 +00:00
if len ( bean ) > 0 {
b = bean [ 0 ]
beanValue := reflect . ValueOf ( bean [ 0 ] )
if beanValue . Kind ( ) != reflect . Ptr {
return "" , nil , errors . New ( "needs a pointer" )
2020-02-28 12:29:08 +00:00
}
2021-06-07 05:45:29 +00:00
if beanValue . Elem ( ) . Kind ( ) == reflect . Struct {
if err := statement . SetRefBean ( bean [ 0 ] ) ; err != nil {
return "" , nil , err
}
}
}
tableName := statement . TableName ( )
if len ( tableName ) <= 0 {
return "" , nil , ErrTableNotFound
}
if statement . RefTable == nil {
2020-02-28 12:29:08 +00:00
tableName = statement . quote ( tableName )
if len ( statement . JoinStr ) > 0 {
joinStr = statement . JoinStr
}
if statement . Conds ( ) . IsValid ( ) {
2020-03-09 02:17:37 +00:00
condSQL , condArgs , err := statement . GenCondSQL ( statement . Conds ( ) )
2020-02-28 12:29:08 +00:00
if err != nil {
return "" , nil , err
}
2020-03-07 08:51:30 +00:00
if statement . dialect . URI ( ) . DBType == schemas . MSSQL {
2020-02-28 12:29:08 +00:00
sqlStr = fmt . Sprintf ( "SELECT TOP 1 * FROM %s %s WHERE %s" , tableName , joinStr , condSQL )
2020-03-07 08:51:30 +00:00
} else if statement . dialect . URI ( ) . DBType == schemas . ORACLE {
2020-02-28 12:29:08 +00:00
sqlStr = fmt . Sprintf ( "SELECT * FROM %s WHERE (%s) %s AND ROWNUM=1" , tableName , joinStr , condSQL )
} else {
2021-12-01 05:54:47 +00:00
sqlStr = fmt . Sprintf ( "SELECT 1 FROM %s %s WHERE %s LIMIT 1" , tableName , joinStr , condSQL )
2020-02-28 12:29:08 +00:00
}
args = condArgs
} else {
2020-03-07 08:51:30 +00:00
if statement . dialect . URI ( ) . DBType == schemas . MSSQL {
2020-02-28 12:29:08 +00:00
sqlStr = fmt . Sprintf ( "SELECT TOP 1 * FROM %s %s" , tableName , joinStr )
2020-03-07 08:51:30 +00:00
} else if statement . dialect . URI ( ) . DBType == schemas . ORACLE {
2020-02-28 12:29:08 +00:00
sqlStr = fmt . Sprintf ( "SELECT * FROM %s %s WHERE ROWNUM=1" , tableName , joinStr )
} else {
2021-12-01 05:54:47 +00:00
sqlStr = fmt . Sprintf ( "SELECT 1 FROM %s %s LIMIT 1" , tableName , joinStr )
2020-02-28 12:29:08 +00:00
}
args = [ ] interface { } { }
}
} else {
statement . Limit ( 1 )
2021-06-07 05:45:29 +00:00
sqlStr , args , err = statement . GenGetSQL ( b )
2020-02-28 12:29:08 +00:00
if err != nil {
return "" , nil , err
}
}
return sqlStr , args , nil
}
2021-04-12 08:00:07 +00:00
// GenFindSQL generates Find SQL
2020-02-28 12:29:08 +00:00
func ( statement * Statement ) GenFindSQL ( autoCond builder . Cond ) ( string , [ ] interface { } , error ) {
if statement . RawSQL != "" {
2020-03-09 02:17:37 +00:00
return statement . GenRawSQL ( ) , statement . RawParams , nil
2020-02-28 12:29:08 +00:00
}
var sqlStr string
var args [ ] interface { }
var err error
if len ( statement . TableName ( ) ) <= 0 {
return "" , nil , ErrTableNotFound
}
2022-04-24 08:28:06 +00:00
columnStr := statement . ColumnStr ( )
2020-02-28 12:29:08 +00:00
if len ( statement . SelectStr ) > 0 {
columnStr = statement . SelectStr
} else {
if statement . JoinStr == "" {
if columnStr == "" {
if statement . GroupByStr != "" {
columnStr = statement . quoteColumnStr ( statement . GroupByStr )
} else {
columnStr = statement . genColumnStr ( )
}
}
} else {
if columnStr == "" {
if statement . GroupByStr != "" {
columnStr = statement . quoteColumnStr ( statement . GroupByStr )
} else {
columnStr = "*"
}
}
}
if columnStr == "" {
columnStr = "*"
}
}
statement . cond = statement . cond . And ( autoCond )
2020-03-06 06:43:49 +00:00
sqlStr , condArgs , err := statement . genSelectSQL ( columnStr , true , true )
2020-02-28 12:29:08 +00:00
if err != nil {
return "" , nil , err
}
2020-03-06 06:43:49 +00:00
args = append ( statement . joinArgs , condArgs ... )
2020-02-28 12:29:08 +00:00
// for mssql and use limit
qs := strings . Count ( sqlStr , "?" )
if len ( args ) * 2 == qs {
args = append ( args , args ... )
}
return sqlStr , args , nil
}