forked from mirror/gorm
Add QueryExpr, thanks @ManReinsp for PR #1548
This commit is contained in:
parent
d61b7db8fa
commit
e5432b14d2
|
@ -68,11 +68,11 @@ func TestCreateWithExistingTimestamp(t *testing.T) {
|
||||||
user.UpdatedAt = timeA
|
user.UpdatedAt = timeA
|
||||||
DB.Save(&user)
|
DB.Save(&user)
|
||||||
|
|
||||||
if user.CreatedAt.Format(time.RFC3339) != timeA.Format(time.RFC3339) {
|
if user.CreatedAt.UTC().Format(time.RFC3339) != timeA.UTC().Format(time.RFC3339) {
|
||||||
t.Errorf("CreatedAt should not be changed")
|
t.Errorf("CreatedAt should not be changed")
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.UpdatedAt.Format(time.RFC3339) != timeA.Format(time.RFC3339) {
|
if user.UpdatedAt.UTC().Format(time.RFC3339) != timeA.UTC().Format(time.RFC3339) {
|
||||||
t.Errorf("UpdatedAt should not be changed")
|
t.Errorf("UpdatedAt should not be changed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
main.go
11
main.go
|
@ -168,6 +168,15 @@ 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}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryExpr returns the query as expr object
|
||||||
|
func (s *DB) QueryExpr() *expr {
|
||||||
|
scope := s.NewScope(s.Value)
|
||||||
|
scope.InstanceSet("skip_bindvar", true)
|
||||||
|
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 +227,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
47
main_test.go
47
main_test.go
|
@ -607,9 +607,54 @@ 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@%").SubqueryExpr()).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").SubqueryExpr()).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("AVG(age) > (?)", DB.
|
||||||
|
Select("AVG(age)").Where("email LIKE ?", "root%").Table("users").SubqueryExpr()).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 == "foundation" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
20
scope.go
20
scope.go
|
@ -253,15 +253,25 @@ func (scope *Scope) CallMethod(methodName string) {
|
||||||
|
|
||||||
// AddToVars add value as sql's vars, used to prevent SQL injection
|
// AddToVars add value as sql's vars, used to prevent SQL injection
|
||||||
func (scope *Scope) AddToVars(value interface{}) string {
|
func (scope *Scope) AddToVars(value interface{}) string {
|
||||||
|
_, skipBindVar := scope.InstanceGet("skip_bindvar")
|
||||||
|
|
||||||
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)
|
if skipBindVar {
|
||||||
|
scope.AddToVars(arg)
|
||||||
|
} else {
|
||||||
|
exp = strings.Replace(exp, "?", scope.AddToVars(arg), 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return exp
|
return exp
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.SQLVars = append(scope.SQLVars, value)
|
scope.SQLVars = append(scope.SQLVars, value)
|
||||||
|
|
||||||
|
if skipBindVar {
|
||||||
|
return "?"
|
||||||
|
}
|
||||||
return scope.Dialect().BindVar(len(scope.SQLVars))
|
return scope.Dialect().BindVar(len(scope.SQLVars))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,12 +339,12 @@ func (scope *Scope) QuotedTableName() (name string) {
|
||||||
|
|
||||||
// CombinedConditionSql return combined condition sql
|
// CombinedConditionSql return combined condition sql
|
||||||
func (scope *Scope) CombinedConditionSql() string {
|
func (scope *Scope) CombinedConditionSql() string {
|
||||||
joinSql := scope.joinsSQL()
|
joinSQL := scope.joinsSQL()
|
||||||
whereSql := scope.whereSQL()
|
whereSQL := scope.whereSQL()
|
||||||
if scope.Search.raw {
|
if scope.Search.raw {
|
||||||
whereSql = strings.TrimSuffix(strings.TrimPrefix(whereSql, "WHERE ("), ")")
|
whereSQL = strings.TrimSuffix(strings.TrimPrefix(whereSQL, "WHERE ("), ")")
|
||||||
}
|
}
|
||||||
return joinSql + whereSql + scope.groupSQL() +
|
return joinSQL + whereSQL + scope.groupSQL() +
|
||||||
scope.havingSQL() + scope.orderSQL() + scope.limitAndOffsetSQL()
|
scope.havingSQL() + scope.orderSQL() + scope.limitAndOffsetSQL()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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…
Reference in New Issue