gorm/expression_ext.go
Gerhard Gruber c807fe3202 Started with expression extension
Improved questionmark parameter placeholder replacementw

Added support for subqueries in Where and Having clauses

Queries can be transformed into subqueries by calling .Subquery() on a db object

See main_test.go:TestQueryBuilderSubselectInWhere

Fixed comment spacing

Refactoring, adding Having Subquery support, allowing db.T for tablenames

Removed quoting from tablename in db.T, use db.QT for that

Refactoring, adding Having Subquery support, allowing db.T for tablenames

Added changes

Started with expression extension

Refactoring, adding Having Subquery support, allowing db.T for tablenames

Added method to easily update fields of the Model struct

Added column comparison and Join support

Added subquery support for InnerJoin querybuilder

Fixed column comparison

Added support for column prefixes

Models can set their column prefix by implementing the method ColumnPrefix() string

Fixed multi-parameter subselects and introduced aliasing

Improved Related method

Improved Related method to search for foreign key struct fields with the suffix "ID" (additional to "Id")

Got QueryExpr support from upstream

Added support for subqueries in Where and Having clauses

Queries can be transformed into subqueries by calling .Subquery() on a db object

See main_test.go:TestQueryBuilderSubselectInWhere

Improved questionmark parameter placeholder replacementw

Refactoring, adding Having Subquery support, allowing db.T for tablenames

Removed quoting from tablename in db.T, use db.QT for that

Removed quoting from tablename in db.T, use db.QT for that

Added changes

Added method to easily update fields of the Model struct

Fixed column comparison

Added support for column prefixes

Models can set their column prefix by implementing the method ColumnPrefix() string

Fixed multi-parameter subselects and introduced aliasing

Improved Related method

Improved Related method to search for foreign key struct fields with the suffix "ID" (additional to "Id")

Added select extension for multiple columns

Added support for LEFT RIGHT OUTER joins

Fixed slice support for lexpr.In()

Publizised LExpr

Added DateFormatting for all dialects

Added SUM function for columns

Fixed FormatDate

Added count for column

Removed literal expressions LExpr

Rewrote LExpr methods to work with expr structs.

Added methods BAnd and BOr (bitwise & and | )

Added SetLogWriter method

Added NotIn query expression

Added Distinct query expression

Added DistinctColumn query expression

Same as Distinct but returns a string

Added method OnExp to jexpr

Improved query expression .Eq() method for nil pointers

Fixed rebase errors
2018-02-26 16:00:19 +01:00

278 lines
6.1 KiB
Go

package gorm
import (
"reflect"
"strings"
)
type jexpr struct {
expr string
args []interface{}
}
func join(joinType string, db *DB, model interface{}, alias ...string) *jexpr {
var al string
if len(alias) > 0 {
al = alias[0]
}
if val, ok := model.(*expr); ok {
return &jexpr{expr: " " + joinType + " JOIN (" + val.expr + ") " + al, args: val.args}
}
return &jexpr{expr: " " + joinType + " JOIN " + db.T(model) + " " + al}
}
func (db *DB) InnerJoin(model interface{}, alias ...string) *jexpr {
return join("INNER", db, model, alias...)
}
func (db *DB) LeftJoin(model interface{}, alias ...string) *jexpr {
return join("LEFT", db, model, alias...)
}
func (db *DB) RightJoin(model interface{}, alias ...string) *jexpr {
return join("RIGHT", db, model, alias...)
}
func (db *DB) OuterJoin(model interface{}, alias ...string) *jexpr {
return join("OUTER", db, model, alias...)
}
func (je *jexpr) On(col1 *expr, col2 *expr) *expr {
return &expr{expr: je.expr + " ON " + col1.expr + " = " + col2.expr, args: je.args}
}
func (je *jexpr) OnExp(e2 *expr) *expr {
e := &expr{expr: je.expr + " ON " + e2.expr, args: je.args}
e.args = append(e.args, e2.args...)
return e
}
func (db *DB) L(model interface{}, name string) *expr {
scope := db.NewScope(model)
field, _ := scope.FieldByName(name)
return &expr{expr: scope.Quote(scope.TableName()) + "." + scope.Quote(field.DBName)}
}
func (db *DB) LA(model interface{}, alias string, name string) *expr {
scope := db.NewScope(model)
field, _ := scope.FieldByName(name)
return &expr{expr: scope.Quote(alias) + "." + scope.Quote(field.DBName)}
}
func (db *DB) C(model interface{}, names ...string) string {
columns := make([]string, 0)
scope := db.NewScope(model)
for _, name := range names {
field, _ := scope.FieldByName(name)
columns = append(columns, field.DBName)
}
return strings.Join(columns, ", ")
}
func (db *DB) CA(model interface{}, alias string, names ...string) string {
columns := make([]string, 0)
for _, name := range names {
columns = append(columns, db.LA(model, alias, name).expr)
}
return strings.Join(columns, ", ")
}
func (db *DB) CQ(model interface{}, names ...string) string {
columns := make([]string, 0)
for _, name := range names {
columns = append(columns, db.L(model, name).expr)
}
return strings.Join(columns, ", ")
}
func (db *DB) T(model interface{}) string {
scope := db.NewScope(model)
return scope.TableName()
}
func (db *DB) QT(model interface{}) string {
scope := db.NewScope(model)
return scope.QuotedTableName()
}
func (e *expr) operator(operator string, value interface{}) *expr {
if value == nil {
e.expr = "(" + e.expr + " " + operator + " )"
return e
}
if _, ok := value.(*expr); ok {
e.expr = "(" + e.expr + " " + operator + " (?))"
} else {
e.expr = "(" + e.expr + " " + operator + " ?)"
}
e.args = append(e.args, value)
return e
}
func (e *expr) Gt(value interface{}) *expr {
return e.operator(">", value)
}
func (e *expr) Ge(value interface{}) *expr {
return e.operator(">=", value)
}
func (e *expr) Lt(value interface{}) *expr {
return e.operator("<", value)
}
func (e *expr) Le(value interface{}) *expr {
return e.operator("<=", value)
}
func (e *expr) BAnd(value interface{}) *expr {
return e.operator("&", value)
}
func (e *expr) BOr(value interface{}) *expr {
return e.operator("|", value)
}
func (e *expr) Like(value interface{}) *expr {
return e.operator("LIKE", value)
}
func (e *expr) Eq(value interface{}) *expr {
if value == nil {
return e.operator("IS NULL", value)
} else if val := reflect.ValueOf(value); val.Kind() == reflect.Ptr && val.IsNil() {
return e.operator("IS NULL", nil)
}
return e.operator("=", value)
}
func (e *expr) Neq(value interface{}) *expr {
if value == nil {
return e.operator("IS NOT NULL", value)
}
return e.operator("!=", value)
}
func (e *expr) Sum() string {
return "SUM(" + e.expr + ")"
}
func (e *expr) Count() string {
return "COUNT(" + e.expr + ")"
}
func (e *expr) Distinct() *expr {
e.expr = "DISTINCT " + e.expr
return e
}
func (e *expr) DistinctColumn() string {
return "DISTINCT " + e.expr
}
func (e *expr) in(operator string, values ...interface{}) *expr {
// NOTE: Maybe there is a better way to do this? :)
if len(values) == 1 {
if s := reflect.ValueOf(values[0]); s.Kind() == reflect.Slice {
vals := make([]interface{}, s.Len())
qm := make([]string, s.Len())
for i := 0; i < s.Len(); i++ {
vals[i] = s.Index(i).Interface()
qm[i] = "?"
}
e.expr = "(" + e.expr + operator + " IN (" + strings.Join(qm, ",") + "))"
e.args = append(e.args, vals...)
return e
}
}
qm := make([]string, len(values))
for i := 0; i < len(values); i++ {
qm[i] = "?"
}
e.expr = "(" + e.expr + operator + " IN (" + strings.Join(qm, ",") + "))"
e.args = append(e.args, values...)
return e
}
func (e *expr) In(values ...interface{}) *expr {
return e.in("", values...)
}
func (e *expr) NotIn(values ...interface{}) *expr {
return e.in(" NOT", values...)
}
func (e *expr) OrderAsc() string {
return e.expr + " ASC "
}
func (e *expr) OrderDesc() string {
return e.expr + " DESC "
}
func (e *expr) Or(e2 *expr) *expr {
e.expr = "(" + e.expr + " OR " + e2.expr + ")"
e.args = append(e.args, e2.args...)
return e
}
func (e *expr) And(e2 *expr) *expr {
e.expr = "(" + e.expr + " AND " + e2.expr + ")"
e.args = append(e.args, e2.args...)
return e
}
func (db *DB) UpdateFields(fields ...string) *DB {
sets := make(map[string]interface{})
m := reflect.ValueOf(db.Value).Elem()
for _, field := range fields {
sets[db.C(db.Value, field)] = m.FieldByName(field).Interface()
}
return db.Update(sets)
}
func (db *DB) SelectFields(fields ...string) *DB {
selects := strings.Join(fields, ", ")
return db.Select(selects)
}
func (e *expr) Intersect(e2 *expr) *expr {
e.expr = "((" + e.expr + ") INTERSECT (" + e2.expr + "))"
e.args = append(e.args, e2.args...)
return e
}
func (e *expr) Alias(alias string) *expr {
e.expr = e.expr + " " + alias + " "
return e
}
func (db *DB) FormatDate(e *expr, format string) *expr {
return db.Dialect().FormatDate(e, format)
}
func (db *DB) FormatDateColumn(e *expr, format string) string {
return db.FormatDate(e, format).expr
}