Merge b1634f73588d413bfe3bff1df52fa6f6d81f471e into 6f64b8610da6d5214e9197ed3c1bf8ecf8983c89
This commit is contained in:
commit
8310b11db8
10
main.go
10
main.go
@ -168,6 +168,14 @@ func (s *DB) NewScope(value interface{}) *Scope {
|
|||||||
return &Scope{db: dbClone, Search: dbClone.search.clone(), Value: value}
|
return &Scope{db: dbClone, Search: dbClone.search.clone(), Value: value}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Subquery returns the query as eprx object
|
||||||
|
func (s *DB) Subquery() *expr {
|
||||||
|
scope := s.NewScope(s.Value)
|
||||||
|
scope.prepareQuerySQL()
|
||||||
|
|
||||||
|
return Expr("("+scope.SQL+")", scope.SQLVars...)
|
||||||
|
}
|
||||||
|
|
||||||
// Where return a new relation, filter records with given conditions, accepts `map`, `struct` or `string` as conditions, refer http://jinzhu.github.io/gorm/crud.html#query
|
// Where return a new relation, filter records with given conditions, accepts `map`, `struct` or `string` as conditions, refer http://jinzhu.github.io/gorm/crud.html#query
|
||||||
func (s *DB) Where(query interface{}, args ...interface{}) *DB {
|
func (s *DB) Where(query interface{}, args ...interface{}) *DB {
|
||||||
return s.clone().search.Where(query, args...).db
|
return s.clone().search.Where(query, args...).db
|
||||||
@ -218,7 +226,7 @@ func (s *DB) Group(query string) *DB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Having specify HAVING conditions for GROUP BY
|
// Having specify HAVING conditions for GROUP BY
|
||||||
func (s *DB) Having(query string, values ...interface{}) *DB {
|
func (s *DB) Having(query interface{}, values ...interface{}) *DB {
|
||||||
return s.clone().search.Having(query, values...).db
|
return s.clone().search.Having(query, values...).db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
45
main_test.go
45
main_test.go
@ -607,6 +607,51 @@ func TestHaving(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestQueryBuilderSubselectInWhere(t *testing.T) {
|
||||||
|
user := User{Name: "ruser1", Email: "root@user1.com", Age: 32}
|
||||||
|
DB.Save(&user)
|
||||||
|
user = User{Name: "ruser2", Email: "nobody@user2.com", Age: 16}
|
||||||
|
DB.Save(&user)
|
||||||
|
user = User{Name: "ruser3", Email: "root@user3.com", Age: 64}
|
||||||
|
DB.Save(&user)
|
||||||
|
user = User{Name: "ruser4", Email: "somebody@user3.com", Age: 128}
|
||||||
|
DB.Save(&user)
|
||||||
|
|
||||||
|
var users []User
|
||||||
|
DB.Select("*").Where("name IN ?", DB.
|
||||||
|
Select("name").Table("users").Where("email LIKE ?", "root@%").Subquery()).Find(&users)
|
||||||
|
|
||||||
|
if len(users) != 2 {
|
||||||
|
t.Errorf("Two users should be found, instead found %d", len(users))
|
||||||
|
}
|
||||||
|
|
||||||
|
DB.Select("*").Where("email LIKE ?", "root%").Where("age >= ?", DB.
|
||||||
|
Select("AVG(age)").Table("users").Subquery()).Find(&users)
|
||||||
|
|
||||||
|
if len(users) != 2 {
|
||||||
|
t.Errorf("Two users should be found, instead found %d", len(users))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQueryBuilderSubselectInHaving(t *testing.T) {
|
||||||
|
user := User{Name: "ruser1", Email: "root@user1.com", Age: 64}
|
||||||
|
DB.Save(&user)
|
||||||
|
user = User{Name: "ruser2", Email: "root@user2.com", Age: 128}
|
||||||
|
DB.Save(&user)
|
||||||
|
user = User{Name: "ruser3", Email: "root@user1.com", Age: 64}
|
||||||
|
DB.Save(&user)
|
||||||
|
user = User{Name: "ruser4", Email: "root@user2.com", Age: 128}
|
||||||
|
DB.Save(&user)
|
||||||
|
|
||||||
|
var users []User
|
||||||
|
DB.Select("AVG(age) as avgage").Where("email LIKE ?", "root%").Group("email").Having("avgage > ?", DB.
|
||||||
|
Select("AVG(age)").Where("email LIKE ?", "root%").Table("users").Subquery()).Find(&users)
|
||||||
|
|
||||||
|
if len(users) != 1 {
|
||||||
|
t.Errorf("One user group should be found, instead found %d", len(users))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func DialectHasTzSupport() bool {
|
func DialectHasTzSupport() bool {
|
||||||
// NB: mssql and FoundationDB do not support time zones.
|
// NB: mssql and FoundationDB do not support time zones.
|
||||||
if dialect := os.Getenv("GORM_DIALECT"); dialect == "mssql" || dialect == "foundation" {
|
if dialect := os.Getenv("GORM_DIALECT"); dialect == "mssql" || dialect == "foundation" {
|
||||||
|
40
scope.go
40
scope.go
@ -256,7 +256,7 @@ func (scope *Scope) AddToVars(value interface{}) string {
|
|||||||
if expr, ok := value.(*expr); ok {
|
if expr, ok := value.(*expr); ok {
|
||||||
exp := expr.expr
|
exp := expr.expr
|
||||||
for _, arg := range expr.args {
|
for _, arg := range expr.args {
|
||||||
exp = strings.Replace(exp, "?", scope.AddToVars(arg), 1)
|
exp = scope.replaceParameterPlaceholder(exp, arg)
|
||||||
}
|
}
|
||||||
return exp
|
return exp
|
||||||
}
|
}
|
||||||
@ -452,6 +452,7 @@ var (
|
|||||||
isNumberRegexp = regexp.MustCompile("^\\s*\\d+\\s*$") // match if string is number
|
isNumberRegexp = regexp.MustCompile("^\\s*\\d+\\s*$") // match if string is number
|
||||||
comparisonRegexp = regexp.MustCompile("(?i) (=|<>|>|<|LIKE|IS|IN) ")
|
comparisonRegexp = regexp.MustCompile("(?i) (=|<>|>|<|LIKE|IS|IN) ")
|
||||||
countingQueryRegexp = regexp.MustCompile("(?i)^count(.+)$")
|
countingQueryRegexp = regexp.MustCompile("(?i)^count(.+)$")
|
||||||
|
pgParameterRegexp = regexp.MustCompile(`\$[0-9]+`) // to exchange postgres `$1` style parameter placeholders
|
||||||
)
|
)
|
||||||
|
|
||||||
func (scope *Scope) quoteIfPossible(str string) string {
|
func (scope *Scope) quoteIfPossible(str string) string {
|
||||||
@ -461,6 +462,21 @@ func (scope *Scope) quoteIfPossible(str string) string {
|
|||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (scope *Scope) replaceParameterPlaceholderLiteral(sql string, parameter interface{}, addToVars bool) string {
|
||||||
|
if scope.Dialect().GetName() == "postgres" && pgParameterRegexp.MatchString(sql) {
|
||||||
|
sql = pgParameterRegexp.ReplaceAllLiteralString(sql, "?")
|
||||||
|
}
|
||||||
|
if val, ok := parameter.(string); ok && !addToVars {
|
||||||
|
return strings.Replace(sql, "?", val, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Replace(sql, "?", scope.AddToVars(parameter), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scope *Scope) replaceParameterPlaceholder(sql string, parameter interface{}) string {
|
||||||
|
return scope.replaceParameterPlaceholderLiteral(sql, parameter, true)
|
||||||
|
}
|
||||||
|
|
||||||
func (scope *Scope) scan(rows *sql.Rows, columns []string, fields []*Field) {
|
func (scope *Scope) scan(rows *sql.Rows, columns []string, fields []*Field) {
|
||||||
var (
|
var (
|
||||||
ignored interface{}
|
ignored interface{}
|
||||||
@ -550,22 +566,22 @@ func (scope *Scope) buildWhereCondition(clause map[string]interface{}) (str stri
|
|||||||
switch reflect.ValueOf(arg).Kind() {
|
switch reflect.ValueOf(arg).Kind() {
|
||||||
case reflect.Slice: // For where("id in (?)", []int64{1,2})
|
case reflect.Slice: // For where("id in (?)", []int64{1,2})
|
||||||
if bytes, ok := arg.([]byte); ok {
|
if bytes, ok := arg.([]byte); ok {
|
||||||
str = strings.Replace(str, "?", scope.AddToVars(bytes), 1)
|
str = scope.replaceParameterPlaceholder(str, bytes)
|
||||||
} else if values := reflect.ValueOf(arg); values.Len() > 0 {
|
} else if values := reflect.ValueOf(arg); values.Len() > 0 {
|
||||||
var tempMarks []string
|
var tempMarks []string
|
||||||
for i := 0; i < values.Len(); i++ {
|
for i := 0; i < values.Len(); i++ {
|
||||||
tempMarks = append(tempMarks, scope.AddToVars(values.Index(i).Interface()))
|
tempMarks = append(tempMarks, scope.AddToVars(values.Index(i).Interface()))
|
||||||
}
|
}
|
||||||
str = strings.Replace(str, "?", strings.Join(tempMarks, ","), 1)
|
str = scope.replaceParameterPlaceholderLiteral(str, strings.Join(tempMarks, ","), false)
|
||||||
} else {
|
} else {
|
||||||
str = strings.Replace(str, "?", scope.AddToVars(Expr("NULL")), 1)
|
str = scope.replaceParameterPlaceholder(str, Expr("NULL"))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if valuer, ok := interface{}(arg).(driver.Valuer); ok {
|
if valuer, ok := interface{}(arg).(driver.Valuer); ok {
|
||||||
arg, _ = valuer.Value()
|
arg, _ = valuer.Value()
|
||||||
}
|
}
|
||||||
|
|
||||||
str = strings.Replace(str, "?", scope.AddToVars(arg), 1)
|
str = scope.replaceParameterPlaceholder(str, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -622,21 +638,21 @@ func (scope *Scope) buildNotCondition(clause map[string]interface{}) (str string
|
|||||||
switch reflect.ValueOf(arg).Kind() {
|
switch reflect.ValueOf(arg).Kind() {
|
||||||
case reflect.Slice: // For where("id in (?)", []int64{1,2})
|
case reflect.Slice: // For where("id in (?)", []int64{1,2})
|
||||||
if bytes, ok := arg.([]byte); ok {
|
if bytes, ok := arg.([]byte); ok {
|
||||||
str = strings.Replace(str, "?", scope.AddToVars(bytes), 1)
|
str = scope.replaceParameterPlaceholder(str, bytes)
|
||||||
} else if values := reflect.ValueOf(arg); values.Len() > 0 {
|
} else if values := reflect.ValueOf(arg); values.Len() > 0 {
|
||||||
var tempMarks []string
|
var tempMarks []string
|
||||||
for i := 0; i < values.Len(); i++ {
|
for i := 0; i < values.Len(); i++ {
|
||||||
tempMarks = append(tempMarks, scope.AddToVars(values.Index(i).Interface()))
|
tempMarks = append(tempMarks, scope.AddToVars(values.Index(i).Interface()))
|
||||||
}
|
}
|
||||||
str = strings.Replace(str, "?", strings.Join(tempMarks, ","), 1)
|
str = scope.replaceParameterPlaceholder(str, strings.Join(tempMarks, ","))
|
||||||
} else {
|
} else {
|
||||||
str = strings.Replace(str, "?", scope.AddToVars(Expr("NULL")), 1)
|
str = scope.replaceParameterPlaceholder(str, Expr("NULL"))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if scanner, ok := interface{}(arg).(driver.Valuer); ok {
|
if scanner, ok := interface{}(arg).(driver.Valuer); ok {
|
||||||
arg, _ = scanner.Value()
|
arg, _ = scanner.Value()
|
||||||
}
|
}
|
||||||
str = strings.Replace(notEqualSQL, "?", scope.AddToVars(arg), 1)
|
str = scope.replaceParameterPlaceholder(notEqualSQL, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -659,12 +675,12 @@ func (scope *Scope) buildSelectQuery(clause map[string]interface{}) (str string)
|
|||||||
for i := 0; i < values.Len(); i++ {
|
for i := 0; i < values.Len(); i++ {
|
||||||
tempMarks = append(tempMarks, scope.AddToVars(values.Index(i).Interface()))
|
tempMarks = append(tempMarks, scope.AddToVars(values.Index(i).Interface()))
|
||||||
}
|
}
|
||||||
str = strings.Replace(str, "?", strings.Join(tempMarks, ","), 1)
|
str = scope.replaceParameterPlaceholder(str, strings.Join(tempMarks, ","))
|
||||||
default:
|
default:
|
||||||
if valuer, ok := interface{}(arg).(driver.Valuer); ok {
|
if valuer, ok := interface{}(arg).(driver.Valuer); ok {
|
||||||
arg, _ = valuer.Value()
|
arg, _ = valuer.Value()
|
||||||
}
|
}
|
||||||
str = strings.Replace(str, "?", scope.AddToVars(arg), 1)
|
str = scope.replaceParameterPlaceholder(str, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -750,7 +766,7 @@ func (scope *Scope) orderSQL() string {
|
|||||||
} else if expr, ok := order.(*expr); ok {
|
} else if expr, ok := order.(*expr); ok {
|
||||||
exp := expr.expr
|
exp := expr.expr
|
||||||
for _, arg := range expr.args {
|
for _, arg := range expr.args {
|
||||||
exp = strings.Replace(exp, "?", scope.AddToVars(arg), 1)
|
exp = scope.replaceParameterPlaceholder(exp, arg)
|
||||||
}
|
}
|
||||||
orders = append(orders, exp)
|
orders = append(orders, exp)
|
||||||
}
|
}
|
||||||
|
@ -104,8 +104,12 @@ func (s *search) Group(query string) *search {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *search) Having(query string, values ...interface{}) *search {
|
func (s *search) Having(query interface{}, values ...interface{}) *search {
|
||||||
s.havingConditions = append(s.havingConditions, map[string]interface{}{"query": query, "args": values})
|
if val, ok := query.(*expr); ok {
|
||||||
|
s.havingConditions = append(s.havingConditions, map[string]interface{}{"query": val.expr, "args": val.args})
|
||||||
|
} else {
|
||||||
|
s.havingConditions = append(s.havingConditions, map[string]interface{}{"query": query, "args": values})
|
||||||
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user