2506 lines
64 KiB
Go
2506 lines
64 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"math"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var Operators = []string{
|
|
"=", "<", ">", "<=", ">=", "<>", "!=", "<=>",
|
|
"like", "like binary", "not like", "ilike",
|
|
"&", "|", "^", "<<", ">>",
|
|
"rlike", "regexp", "not regexp",
|
|
"~", "~*", "!~", "!~*", "similar to",
|
|
"not similar to", "not ilike", "~~*", "!~~*"}
|
|
|
|
var SelectComponents = []string{
|
|
TYPE_AGGREGRATE,
|
|
TYPE_COLUMN,
|
|
TYPE_FROM,
|
|
TYPE_JOIN,
|
|
TYPE_WHERE,
|
|
TYPE_GROUP_BY,
|
|
TYPE_HAVING,
|
|
TYPE_ORDER,
|
|
TYPE_LIMIT,
|
|
TYPE_OFFSET,
|
|
TYPE_UNION,
|
|
TYPE_LOCK,
|
|
}
|
|
|
|
var Bindings = map[string]struct{}{
|
|
TYPE_SELECT: {},
|
|
TYPE_FROM: {},
|
|
TYPE_JOIN: {},
|
|
TYPE_UPDATE: {},
|
|
TYPE_WHERE: {},
|
|
TYPE_GROUP_BY: {},
|
|
TYPE_HAVING: {},
|
|
TYPE_ORDER: {},
|
|
TYPE_UNION: {},
|
|
TYPE_UNION_ORDER: {},
|
|
TYPE_INSERT: {},
|
|
}
|
|
var BindingKeysInOrder = []string{TYPE_SELECT, TYPE_FROM, TYPE_JOIN, TYPE_UPDATE, TYPE_WHERE, TYPE_GROUP_BY, TYPE_HAVING, TYPE_ORDER, TYPE_UNION, TYPE_UNION_ORDER, TYPE_INSERT}
|
|
|
|
type Builder struct {
|
|
Connection *Connection
|
|
Tx *Transaction
|
|
Grammar IGrammar
|
|
//Processor processors.IProcessor
|
|
PreSql strings.Builder
|
|
Bindings map[string][]interface{} //available options (select,from,join,where,groupBy,having,order,union,unionOrder)
|
|
FromTable interface{}
|
|
//TablePrefix string
|
|
TableAlias string
|
|
Wheres []Where
|
|
Aggregates []Aggregate
|
|
Columns []interface{} // The columns that should be returned.
|
|
IsDistinct bool // Indicates if the query returns distinct results.
|
|
DistinctColumns []string // distinct columns.
|
|
Joins []*Builder
|
|
Groups []interface{}
|
|
Havings []Having
|
|
Orders []Order
|
|
LimitNum int
|
|
OffsetNum int
|
|
//Unions []Where
|
|
//UnionLimit int
|
|
//UnionOffset int
|
|
//UnionOrders int
|
|
Components map[string]struct{} //SelectComponents
|
|
LockMode interface{}
|
|
LoggingQueries bool
|
|
Pretending bool
|
|
PreparedSql string
|
|
Dest interface{}
|
|
Pivots []string
|
|
PivotWheres []Where
|
|
OnlyColumns map[string]interface{}
|
|
ExceptColumns map[string]interface{}
|
|
JoinBuilder bool
|
|
JoinType string
|
|
JoinTable interface{}
|
|
UseWrite bool //TODO:
|
|
BeforeQueryCallBacks []func(builder *Builder, t string, data ...map[string]any)
|
|
AfterQueryCallBacks []func(builder *Builder, t string, res sql.Result, err error, data ...map[string]any)
|
|
RemovedScopes map[string]struct{}
|
|
}
|
|
|
|
type Log struct {
|
|
SQL string
|
|
Bindings []interface{}
|
|
Result sql.Result
|
|
Time time.Duration
|
|
}
|
|
|
|
type ScopeFunc func(builder *Builder) *Builder
|
|
|
|
const (
|
|
CONDITION_TYPE_BASIC = "basic"
|
|
CONDITION_TYPE_COLUMN = "column"
|
|
CONDITION_TYPE_RAW = "raw"
|
|
CONDITION_TYPE_IN = "in"
|
|
CONDITION_TYPE_NOT_IN = "not in"
|
|
CONDITION_TYPE_NULL = "null"
|
|
CONDITION_TYPE_BETWEEN = "between"
|
|
CONDITION_TYPE_BETWEEN_COLUMN = "between column"
|
|
CONDITION_TYPE_NOT_BETWEEN = "not between"
|
|
CONDITION_TYPE_DATE = "date"
|
|
CONDITION_TYPE_TIME = "time"
|
|
CONDITION_TYPE_DATETIME = "datetime"
|
|
CONDITION_TYPE_DAY = "day"
|
|
CONDITION_TYPE_MONTH = "month"
|
|
CONDITION_TYPE_YEAR = "year"
|
|
CONDITION_TYPE_CLOSURE = "closure" //todo
|
|
CONDITION_TYPE_NESTED = "nested"
|
|
CONDITION_TYPE_SUB = "subquery"
|
|
CONDITION_TYPE_EXIST = "exist"
|
|
CONDITION_TYPE_NOT_EXIST = "not exist"
|
|
CONDITION_TYPE_ROW_VALUES = "rowValues"
|
|
BOOLEAN_AND = "and"
|
|
BOOLEAN_OR = "or"
|
|
CONDITION_JOIN_NOT = "not" //todo
|
|
JOIN_TYPE_LEFT = "left"
|
|
JOIN_TYPE_RIGHT = "right"
|
|
JOIN_TYPE_INNER = "inner"
|
|
JOIN_TYPE_CROSS = "cross"
|
|
ORDER_ASC = "asc"
|
|
ORDER_DESC = "desc"
|
|
TYPE_SELECT = "select"
|
|
TYPE_FROM = "from"
|
|
TYPE_JOIN = "join"
|
|
TYPE_WHERE = "where"
|
|
TYPE_GROUP_BY = "groupBy"
|
|
TYPE_HAVING = "having"
|
|
TYPE_ORDER = "order"
|
|
TYPE_UNION = "union"
|
|
TYPE_UNION_ORDER = "unionOrder"
|
|
TYPE_COLUMN = "column"
|
|
TYPE_AGGREGRATE = "aggregrate"
|
|
TYPE_OFFSET = "offset"
|
|
TYPE_LIMIT = "limit"
|
|
TYPE_LOCK = "lock"
|
|
TYPE_INSERT = "insert"
|
|
TYPE_UPDATE = "update"
|
|
TYPE_DELETE = "delete"
|
|
)
|
|
|
|
type Aggregate struct {
|
|
AggregateName string
|
|
AggregateColumn string
|
|
}
|
|
|
|
type Order struct {
|
|
OrderType string
|
|
Direction string
|
|
Column string
|
|
RawSql interface{}
|
|
}
|
|
type Having struct {
|
|
HavingType string
|
|
HavingColumn string
|
|
HavingOperator string
|
|
HavingValue interface{}
|
|
HavingBoolean string
|
|
RawSql interface{}
|
|
Not bool
|
|
}
|
|
type Where struct {
|
|
Type string
|
|
Column string
|
|
Columns []string
|
|
Operator string
|
|
FirstColumn string
|
|
SecondColumn string
|
|
RawSql interface{}
|
|
Value interface{}
|
|
Values []interface{}
|
|
Boolean string
|
|
Not bool //not in,not between,not null
|
|
Query *Builder
|
|
}
|
|
|
|
func NewBuilder(c *Connection) *Builder {
|
|
b := Builder{
|
|
Connection: c,
|
|
Components: make(map[string]struct{}),
|
|
//Processor: processors.MysqlProcessor{},
|
|
Bindings: make(map[string][]interface{}),
|
|
RemovedScopes: make(map[string]struct{}),
|
|
LoggingQueries: c.Config.EnableLog,
|
|
}
|
|
return &b
|
|
}
|
|
func NewTxBuilder(tx *Transaction) *Builder {
|
|
b := Builder{
|
|
Components: make(map[string]struct{}),
|
|
Tx: tx,
|
|
Bindings: make(map[string][]interface{}),
|
|
LoggingQueries: tx.Config.EnableLog,
|
|
RemovedScopes: make(map[string]struct{}),
|
|
}
|
|
return &b
|
|
}
|
|
|
|
/*
|
|
CloneBuilderWithTable clone builder use same connection and table
|
|
*/
|
|
func CloneBuilderWithTable(b *Builder) *Builder {
|
|
cb := Builder{
|
|
Connection: b.Connection,
|
|
Components: make(map[string]struct{}),
|
|
Tx: b.Tx,
|
|
Bindings: make(map[string][]interface{}),
|
|
LoggingQueries: b.LoggingQueries,
|
|
}
|
|
if b.Connection.Config.Driver == DriverMysql {
|
|
cb.Grammar = &MysqlGrammar{}
|
|
} else if b.Connection.Config.Driver == DriverSqlite3 {
|
|
cb.Grammar = &Sqlite3Grammar{}
|
|
} else {
|
|
panic("不支持的数据库类型")
|
|
}
|
|
cb.Grammar.SetTablePrefix(b.Grammar.GetTablePrefix())
|
|
cb.Grammar.SetBuilder(&cb)
|
|
return &cb
|
|
}
|
|
func MergeBuilder(b *Builder, builder *Builder) *Builder {
|
|
cb := Builder{
|
|
Connection: b.Connection,
|
|
Components: make(map[string]struct{}),
|
|
Tx: b.Tx,
|
|
Bindings: make(map[string][]interface{}),
|
|
LoggingQueries: b.LoggingQueries,
|
|
}
|
|
if b.Connection.Config.Driver == DriverMysql {
|
|
cb.Grammar = &MysqlGrammar{}
|
|
} else if b.Connection.Config.Driver == DriverSqlite3 {
|
|
cb.Grammar = &Sqlite3Grammar{}
|
|
} else {
|
|
panic("不支持的数据库类型")
|
|
}
|
|
cb.Grammar.SetTablePrefix(b.Grammar.GetTablePrefix())
|
|
cb.Grammar.SetBuilder(&cb)
|
|
return &cb
|
|
}
|
|
|
|
/*
|
|
Clone Clone the query.
|
|
*/
|
|
func Clone(original *Builder) *Builder {
|
|
newBuilder := Builder{
|
|
Connection: original.Connection,
|
|
Tx: original.Tx,
|
|
PreSql: strings.Builder{},
|
|
Bindings: make(map[string][]interface{}, len(original.Bindings)),
|
|
FromTable: original.FromTable,
|
|
TableAlias: original.TableAlias,
|
|
Wheres: make([]Where, len(original.Wheres)),
|
|
Aggregates: make([]Aggregate, len(original.Aggregates)),
|
|
Columns: make([]interface{}, len(original.Columns)),
|
|
IsDistinct: original.IsDistinct,
|
|
DistinctColumns: make([]string, len(original.DistinctColumns)),
|
|
Joins: []*Builder{},
|
|
Groups: make([]interface{}, len(original.Groups)),
|
|
Havings: make([]Having, len(original.Havings)),
|
|
Orders: make([]Order, len(original.Orders)),
|
|
LimitNum: original.LimitNum,
|
|
OffsetNum: original.OffsetNum,
|
|
Components: make(map[string]struct{}, len(original.Components)),
|
|
LockMode: original.LockMode,
|
|
LoggingQueries: original.LoggingQueries,
|
|
Pretending: original.Pretending,
|
|
PreparedSql: "",
|
|
Dest: nil,
|
|
Pivots: make([]string, len(original.Pivots)),
|
|
PivotWheres: make([]Where, len(original.PivotWheres)),
|
|
OnlyColumns: make(map[string]interface{}, len(original.OnlyColumns)),
|
|
ExceptColumns: make(map[string]interface{}, len(original.ExceptColumns)),
|
|
BeforeQueryCallBacks: make([]func(builder *Builder, t string, data ...map[string]any), len(original.BeforeQueryCallBacks)),
|
|
AfterQueryCallBacks: make([]func(builder *Builder, t string, res sql.Result, err error, data ...map[string]any), len(original.AfterQueryCallBacks)),
|
|
JoinBuilder: original.JoinBuilder,
|
|
JoinType: original.JoinType,
|
|
JoinTable: original.JoinTable,
|
|
}
|
|
for key, _ := range original.Bindings {
|
|
newBuilder.Bindings[key] = make([]interface{}, len(original.Bindings[key]))
|
|
copy(newBuilder.Bindings[key], original.Bindings[key])
|
|
}
|
|
copy(newBuilder.Wheres, original.Wheres)
|
|
copy(newBuilder.Aggregates, original.Aggregates)
|
|
copy(newBuilder.Columns, original.Columns)
|
|
copy(newBuilder.DistinctColumns, original.DistinctColumns)
|
|
copy(newBuilder.Groups, original.Groups)
|
|
copy(newBuilder.Havings, original.Havings)
|
|
copy(newBuilder.Orders, original.Orders)
|
|
copy(newBuilder.Pivots, original.Pivots)
|
|
copy(newBuilder.PivotWheres, original.PivotWheres)
|
|
copy(newBuilder.BeforeQueryCallBacks, original.BeforeQueryCallBacks)
|
|
copy(newBuilder.AfterQueryCallBacks, original.AfterQueryCallBacks)
|
|
for key, _ := range original.Components {
|
|
newBuilder.Components[key] = original.Components[key]
|
|
}
|
|
for key, _ := range original.OnlyColumns {
|
|
newBuilder.OnlyColumns[key] = original.OnlyColumns[key]
|
|
}
|
|
for key, _ := range original.ExceptColumns {
|
|
newBuilder.ExceptColumns[key] = original.ExceptColumns[key]
|
|
}
|
|
for _, join := range original.Joins {
|
|
newBuilder.Joins = append(newBuilder.Joins, join.Clone()) //TODO: add tests,
|
|
}
|
|
if original.Connection.Config.Driver == DriverMysql {
|
|
newBuilder.Grammar = &MysqlGrammar{
|
|
Prefix: original.Grammar.GetTablePrefix(),
|
|
Builder: &newBuilder,
|
|
}
|
|
} else if original.Connection.Config.Driver == DriverSqlite3 {
|
|
newBuilder.Grammar = &Sqlite3Grammar{
|
|
Prefix: original.Grammar.GetTablePrefix(),
|
|
Builder: &newBuilder,
|
|
}
|
|
} else {
|
|
panic("不支持的数据库类型")
|
|
}
|
|
|
|
return &newBuilder
|
|
}
|
|
func (b *Builder) Clone() *Builder {
|
|
return Clone(b)
|
|
}
|
|
|
|
/*
|
|
CloneWithout
|
|
CloneWithoutClone the query without the given properties.
|
|
*/
|
|
func CloneWithout(original *Builder, without ...string) *Builder {
|
|
b := Clone(original)
|
|
b.Reset(without...)
|
|
return b
|
|
}
|
|
func (b *Builder) CloneWithout(without ...string) *Builder {
|
|
return CloneWithout(b, without...)
|
|
}
|
|
|
|
/*
|
|
CloneWithoutBindings Clone the query without the given bindings.
|
|
*/
|
|
func CloneWithoutBindings(original *Builder, bindings ...string) *Builder {
|
|
b := Clone(original)
|
|
for _, binding := range bindings {
|
|
b.Bindings[binding] = nil
|
|
}
|
|
return b
|
|
}
|
|
func (b *Builder) CloneWithoutBindings(bindings ...string) *Builder {
|
|
return CloneWithoutBindings(b, bindings...)
|
|
}
|
|
|
|
// Select set the columns to be selected
|
|
func (b *Builder) Select(columns ...interface{}) *Builder {
|
|
b.Components[TYPE_COLUMN] = struct{}{}
|
|
|
|
for i := 0; i < len(columns); i++ {
|
|
switch columnType := columns[i].(type) {
|
|
case string:
|
|
b.Columns = append(b.Columns, columnType)
|
|
case map[string]interface{}:
|
|
for as, q := range columnType {
|
|
switch q.(type) {
|
|
case func(builder *Builder):
|
|
b.SelectSub(q, as)
|
|
case *Builder:
|
|
b.SelectSub(q, as)
|
|
case Expression:
|
|
b.AddSelect(q)
|
|
case string:
|
|
b.Columns = append(b.Columns, q)
|
|
default:
|
|
panic(errors.New("unsupported type for select"))
|
|
}
|
|
}
|
|
case Expression:
|
|
b.AddSelect(columnType)
|
|
}
|
|
}
|
|
return b
|
|
}
|
|
|
|
// SelectSub Add a subselect expression to the query.
|
|
func (b *Builder) SelectSub(query interface{}, as string) *Builder {
|
|
qStr, bindings := b.CreateSub(query)
|
|
queryStr := fmt.Sprintf("( %s ) as %s", qStr, b.Grammar.Wrap(as))
|
|
|
|
return b.SelectRaw(queryStr, bindings)
|
|
|
|
}
|
|
|
|
// AddSelect Add a new select column to the query
|
|
// 1. slice of string
|
|
// 2. map[string]{"alias"}
|
|
func (b *Builder) AddSelect(columns ...interface{}) *Builder {
|
|
b.Components[TYPE_COLUMN] = struct{}{}
|
|
|
|
for i := 0; i < len(columns); i++ {
|
|
switch columnType := columns[i].(type) {
|
|
case string:
|
|
b.Columns = append(b.Columns, columnType)
|
|
case map[string]interface{}:
|
|
for as, q := range columnType {
|
|
b.SelectSub(q, as)
|
|
}
|
|
case Expression:
|
|
b.Columns = append(b.Columns, columnType)
|
|
}
|
|
}
|
|
return b
|
|
}
|
|
|
|
// SelectRaw Add a new "raw" select expression to the query.
|
|
func (b *Builder) SelectRaw(expression string, bindings ...[]interface{}) *Builder {
|
|
b.AddSelect(Expression(expression))
|
|
if len(bindings) > 0 {
|
|
b.AddBinding(bindings[0], TYPE_SELECT)
|
|
}
|
|
return b
|
|
}
|
|
|
|
// CreateSub Creates a subquery and parse it.
|
|
func (b *Builder) CreateSub(query interface{}) (string, []interface{}) {
|
|
var builder *Builder
|
|
if bT, ok := query.(*Builder); ok {
|
|
builder = bT
|
|
} else if function, ok := query.(func(builder *Builder)); ok {
|
|
builder = CloneBuilderWithTable(b)
|
|
function(builder)
|
|
} else if str, ok := query.(string); ok {
|
|
return b.ParseSub(str)
|
|
} else {
|
|
panic("can not create sub")
|
|
}
|
|
return b.ParseSub(builder)
|
|
}
|
|
|
|
/*
|
|
ParseSub Parse the subquery into SQL and bindings.
|
|
*/
|
|
func (b *Builder) ParseSub(query interface{}) (string, []interface{}) {
|
|
if s, ok := query.(string); ok {
|
|
return s, []interface{}{}
|
|
} else if builder, ok := query.(*Builder); ok {
|
|
return builder.ToSql(), builder.GetBindings()
|
|
}
|
|
panic("A subquery must be a query builder instance, a Closure, or a string.")
|
|
}
|
|
|
|
/*
|
|
ToSql Get the SQL representation of the query.
|
|
*/
|
|
func (b *Builder) ToSql() string {
|
|
b.ApplyBeforeQueryCallBacks(TYPE_SELECT)
|
|
if len(b.PreparedSql) > 0 {
|
|
b.PreparedSql = ""
|
|
}
|
|
return b.Grammar.CompileSelect()
|
|
}
|
|
|
|
/*
|
|
Distinct Force the query to only return distinct results.
|
|
*/
|
|
func (b *Builder) Distinct(distinct ...string) *Builder {
|
|
b.IsDistinct = true
|
|
if len(distinct) > 0 {
|
|
b.DistinctColumns = append(b.DistinctColumns, distinct...)
|
|
}
|
|
return b
|
|
}
|
|
|
|
/*
|
|
IsQueryable Determine if the value is a query builder instance or a Closure.
|
|
*/
|
|
func IsQueryable(value interface{}) bool {
|
|
switch value.(type) {
|
|
case Builder, *Builder:
|
|
return true
|
|
case func(builder *Builder):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/*
|
|
Table Begin a fluent query against a database table.
|
|
*/
|
|
func (b *Builder) Table(params ...string) *Builder {
|
|
if len(params) > 1 && params[1] != "" {
|
|
return b.From(params[0], params[1])
|
|
}
|
|
|
|
return b.From(params[0])
|
|
}
|
|
|
|
/*
|
|
From Set the table which the query is targeting.
|
|
*/
|
|
func (b *Builder) From(table interface{}, params ...string) *Builder {
|
|
if IsQueryable(table) {
|
|
return b.FromSub(table, params[0])
|
|
}
|
|
b.Components[TYPE_FROM] = struct{}{}
|
|
if len(params) == 1 {
|
|
b.TableAlias = params[0]
|
|
b.FromTable = fmt.Sprintf("%s as %s", table, params[0])
|
|
} else {
|
|
b.FromTable = table.(string)
|
|
}
|
|
return b
|
|
}
|
|
|
|
/*
|
|
FromSub Makes "from" fetch from a subquery.
|
|
*/
|
|
func (b *Builder) FromSub(table interface{}, as string) *Builder {
|
|
qStr, bindings := b.CreateSub(table)
|
|
queryStr := fmt.Sprintf("(%s) as %s", qStr, b.Grammar.WrapTable(as))
|
|
|
|
return b.FromRaw(queryStr, bindings)
|
|
}
|
|
|
|
/*
|
|
FromRaw Add a raw from clause to the query.
|
|
*/
|
|
func (b *Builder) FromRaw(raw interface{}, bindings ...[]interface{}) *Builder {
|
|
var expression Expression
|
|
if str, ok := raw.(string); ok {
|
|
expression = Expression(str)
|
|
} else {
|
|
expression = raw.(Expression)
|
|
}
|
|
b.FromTable = expression
|
|
b.Components[TYPE_FROM] = struct{}{}
|
|
if len(bindings) > 0 {
|
|
b.AddBinding(bindings[0], TYPE_FROM)
|
|
}
|
|
return b
|
|
}
|
|
|
|
/*
|
|
Join Add a join clause to the query.
|
|
*/
|
|
func (b *Builder) Join(table string, first interface{}, params ...interface{}) *Builder {
|
|
//$table, $first, $operator = null, $second = null, $type = 'inner', $where = false
|
|
|
|
var second string
|
|
var isWhere = false
|
|
var operator = "="
|
|
var joinType = JOIN_TYPE_INNER
|
|
|
|
length := len(params)
|
|
|
|
switch length {
|
|
case 0:
|
|
if _, ok := first.(func(builder *Builder)); !ok {
|
|
panic(errors.New("arguements num mismatch"))
|
|
}
|
|
case 1:
|
|
if _, ok := first.(func(builder *Builder)); ok {
|
|
joinType = params[0].(string)
|
|
} else {
|
|
second = params[0].(string)
|
|
}
|
|
case 2:
|
|
operator = params[0].(string)
|
|
second = params[1].(string)
|
|
case 3:
|
|
operator = params[0].(string)
|
|
second = params[1].(string)
|
|
joinType = params[2].(string)
|
|
case 4:
|
|
operator = params[0].(string)
|
|
second = params[1].(string)
|
|
joinType = params[2].(string)
|
|
isWhere = params[3].(bool)
|
|
}
|
|
return b.join(table, first, operator, second, joinType, isWhere)
|
|
}
|
|
|
|
/*
|
|
RightJoin Add a right join to the query.
|
|
*/
|
|
func (b *Builder) RightJoin(table string, firstColumn interface{}, params ...interface{}) *Builder {
|
|
|
|
var operator, second string
|
|
joinType := JOIN_TYPE_RIGHT
|
|
length := len(params)
|
|
switch length {
|
|
case 0:
|
|
if function, ok := firstColumn.(func(builder *Builder)); ok {
|
|
clause := NewJoin(b, joinType, table)
|
|
function(clause)
|
|
b.Components[TYPE_JOIN] = struct{}{}
|
|
b.Joins = append(b.Joins, clause)
|
|
b.AddBinding(clause.GetBindings(), TYPE_JOIN)
|
|
return b
|
|
} else {
|
|
panic(errors.New("arguements num mismatch"))
|
|
}
|
|
case 1:
|
|
operator = "="
|
|
second = params[0].(string)
|
|
case 2:
|
|
operator = params[0].(string)
|
|
second = params[1].(string)
|
|
|
|
}
|
|
return b.join(table, firstColumn, operator, second, joinType, false)
|
|
}
|
|
|
|
/*
|
|
LeftJoin Add a left join to the query.
|
|
*/
|
|
func (b *Builder) LeftJoin(table string, firstColumn interface{}, params ...interface{}) *Builder {
|
|
var operator, second string
|
|
joinType := JOIN_TYPE_LEFT
|
|
length := len(params)
|
|
switch length {
|
|
case 0:
|
|
if function, ok := firstColumn.(func(builder *Builder)); ok {
|
|
clause := NewJoin(b, joinType, table)
|
|
function(clause)
|
|
b.Components[TYPE_JOIN] = struct{}{}
|
|
b.Joins = append(b.Joins, clause)
|
|
b.AddBinding(clause.GetBindings(), TYPE_JOIN)
|
|
return b
|
|
} else {
|
|
panic(errors.New("arguements num mismatch"))
|
|
}
|
|
case 1:
|
|
operator = "="
|
|
second = params[0].(string)
|
|
case 2:
|
|
operator = params[0].(string)
|
|
second = params[1].(string)
|
|
}
|
|
return b.join(table, firstColumn, operator, second, joinType, false)
|
|
}
|
|
|
|
/*
|
|
LeftJoinWhere Add a "join where" clause to the query.
|
|
*/
|
|
func (b *Builder) LeftJoinWhere(table, firstColumn, joinOperator, secondColumn string) *Builder {
|
|
return b.joinWhere(table, firstColumn, joinOperator, secondColumn, JOIN_TYPE_LEFT)
|
|
}
|
|
|
|
/*
|
|
RightJoinWhere Add a "right join where" clause to the query.
|
|
*/
|
|
func (b *Builder) RightJoinWhere(table, firstColumn, joinOperator, secondColumn string) *Builder {
|
|
return b.joinWhere(table, firstColumn, joinOperator, secondColumn, JOIN_TYPE_RIGHT)
|
|
}
|
|
func NewJoin(builder *Builder, joinType string, table interface{}) *Builder {
|
|
cb := CloneBuilderWithTable(builder)
|
|
cb.JoinBuilder = true
|
|
cb.JoinType = joinType
|
|
cb.JoinTable = table
|
|
|
|
return cb
|
|
}
|
|
func (b *Builder) On(first interface{}, params ...interface{}) *Builder {
|
|
var second string
|
|
boolean := BOOLEAN_AND
|
|
operator := "="
|
|
switch len(params) {
|
|
case 0:
|
|
if function, ok := first.(func(builder *Builder)); ok {
|
|
b.WhereNested(function, boolean)
|
|
return b
|
|
}
|
|
panic(errors.New("arguements mismatch"))
|
|
case 1:
|
|
second = params[0].(string)
|
|
case 2:
|
|
operator = params[0].(string)
|
|
second = params[1].(string)
|
|
case 3:
|
|
operator = params[0].(string)
|
|
second = params[1].(string)
|
|
boolean = params[2].(string)
|
|
}
|
|
|
|
b.WhereColumn(first.(string), operator, second, boolean)
|
|
return b
|
|
|
|
}
|
|
func (b *Builder) OrOn(first interface{}, params ...interface{}) *Builder {
|
|
var second string
|
|
boolean := BOOLEAN_OR
|
|
operator := "="
|
|
switch len(params) {
|
|
case 0:
|
|
if function, ok := first.(func(builder *Builder)); ok {
|
|
b.WhereNested(function, boolean)
|
|
return b
|
|
}
|
|
panic(errors.New("arguements mismatch"))
|
|
case 1:
|
|
second = params[0].(string)
|
|
return b.On(first, operator, second, boolean)
|
|
case 2:
|
|
operator = params[0].(string)
|
|
second = params[1].(string)
|
|
return b.On(first, operator, second, boolean)
|
|
}
|
|
panic(errors.New("arguements mismatch"))
|
|
}
|
|
|
|
/*
|
|
CrossJoin Add a "cross join" clause to the query.
|
|
*/
|
|
func (b *Builder) CrossJoin(table string, params ...interface{}) *Builder {
|
|
var operator, first, second string
|
|
joinType := JOIN_TYPE_CROSS
|
|
length := len(params)
|
|
switch length {
|
|
case 0:
|
|
clause := NewJoin(b, joinType, table)
|
|
b.Joins = append(b.Joins, clause)
|
|
b.Components[TYPE_JOIN] = struct{}{}
|
|
|
|
return b
|
|
case 1:
|
|
if function, ok := params[0].(func(builder *Builder)); ok {
|
|
clause := NewJoin(b, joinType, table)
|
|
function(clause)
|
|
b.Joins = append(b.Joins, clause)
|
|
b.AddBinding(clause.GetBindings(), TYPE_JOIN)
|
|
b.Components[TYPE_JOIN] = struct{}{}
|
|
|
|
return b
|
|
} else {
|
|
panic(errors.New("cross join arguements mismatch"))
|
|
}
|
|
case 2:
|
|
first = params[0].(string)
|
|
operator = "="
|
|
second = params[1].(string)
|
|
case 3:
|
|
first = params[0].(string)
|
|
operator = params[1].(string)
|
|
second = params[2].(string)
|
|
|
|
}
|
|
return b.join(table, first, operator, second, joinType, false)
|
|
}
|
|
|
|
/*
|
|
CrossJoinSub Add a subquery cross join to the query.
|
|
*/
|
|
func (b *Builder) CrossJoinSub(query interface{}, as string) *Builder {
|
|
queryStr, bindings := b.CreateSub(query)
|
|
expr := fmt.Sprintf("(%s) as %s", queryStr, b.Grammar.WrapTable(as))
|
|
b.AddBinding(bindings, TYPE_JOIN)
|
|
clause := NewJoin(b, JOIN_TYPE_CROSS, Raw(expr))
|
|
b.Joins = append(b.Joins, clause)
|
|
b.Components[TYPE_JOIN] = struct{}{}
|
|
|
|
return b
|
|
}
|
|
|
|
/*
|
|
join Add a join clause to the query.
|
|
*/
|
|
func (b *Builder) join(table, first, operator, second, joinType, isWhere interface{}) *Builder {
|
|
//$table, $first, $operator = null, $second = null, $type = 'inner', $where = false
|
|
b.Components[TYPE_JOIN] = struct{}{}
|
|
|
|
if function, ok := first.(func(builder *Builder)); ok {
|
|
clause := NewJoin(b, joinType.(string), table)
|
|
clause.Grammar.SetTablePrefix(b.Grammar.GetTablePrefix())
|
|
function(clause)
|
|
b.Joins = append(b.Joins, clause)
|
|
b.AddBinding(clause.GetBindings(), TYPE_JOIN)
|
|
return b
|
|
}
|
|
|
|
clause := NewJoin(b, joinType.(string), table)
|
|
|
|
if isWhere.(bool) {
|
|
clause.Where(first, operator, second)
|
|
} else {
|
|
clause.On(first, operator, second, BOOLEAN_AND)
|
|
}
|
|
b.AddBinding(clause.GetBindings(), TYPE_JOIN)
|
|
|
|
clause.Grammar.SetTablePrefix(b.Grammar.GetTablePrefix())
|
|
|
|
b.Joins = append(b.Joins, clause)
|
|
|
|
return b
|
|
}
|
|
|
|
/*
|
|
joinWhere Add a "join where" clause to the query.
|
|
*/
|
|
func (b *Builder) joinWhere(table, firstColumn, joinOperator, secondColumn, joinType string) *Builder {
|
|
return b.join(table, firstColumn, joinOperator, secondColumn, joinType, true)
|
|
}
|
|
|
|
/*
|
|
JoinWhere Add a "join where" clause to the query.
|
|
*/
|
|
func (b *Builder) JoinWhere(table, firstColumn, joinOperator, secondColumn string) *Builder {
|
|
return b.joinWhere(table, firstColumn, joinOperator, secondColumn, JOIN_TYPE_INNER)
|
|
}
|
|
|
|
/*
|
|
JoinSub Add a subquery join clause to the query.
|
|
*/
|
|
func (b *Builder) JoinSub(query interface{}, as string, first interface{}, params ...interface{}) *Builder {
|
|
queryStr, bindings := b.CreateSub(query)
|
|
expr := fmt.Sprintf("(%s) as %s", queryStr, b.Grammar.WrapTable(as))
|
|
var operator string
|
|
joinType := JOIN_TYPE_INNER
|
|
var isWhere = false
|
|
var second interface{}
|
|
switch len(params) {
|
|
case 1:
|
|
operator = "="
|
|
second = params[0]
|
|
case 2:
|
|
operator = params[0].(string)
|
|
second = params[1]
|
|
case 3:
|
|
operator = params[0].(string)
|
|
second = params[1]
|
|
joinType = params[2].(string)
|
|
case 4:
|
|
operator = params[0].(string)
|
|
second = params[1]
|
|
joinType = params[2].(string)
|
|
isWhere = params[3].(bool)
|
|
}
|
|
b.AddBinding(bindings, TYPE_JOIN)
|
|
|
|
return b.join(Raw(expr), first, operator, second, joinType, isWhere)
|
|
}
|
|
|
|
/*
|
|
LeftJoinSub Add a subquery left join to the query.
|
|
*/
|
|
func (b *Builder) LeftJoinSub(query interface{}, as string, first interface{}, params ...interface{}) *Builder {
|
|
queryStr, bindings := b.CreateSub(query)
|
|
expr := fmt.Sprintf("(%s) as %s", queryStr, b.Grammar.WrapTable(as))
|
|
var operator string
|
|
joinType := JOIN_TYPE_LEFT
|
|
var second interface{}
|
|
switch len(params) {
|
|
case 1:
|
|
operator = "="
|
|
second = params[0]
|
|
case 2:
|
|
operator = params[0].(string)
|
|
second = params[1]
|
|
|
|
}
|
|
b.AddBinding(bindings, TYPE_JOIN)
|
|
|
|
return b.join(Raw(expr), first, operator, second, joinType, false)
|
|
}
|
|
|
|
/*
|
|
RightJoinSub Add a subquery right join to the query.
|
|
*/
|
|
func (b *Builder) RightJoinSub(query interface{}, as string, first interface{}, params ...interface{}) *Builder {
|
|
queryStr, bindings := b.CreateSub(query)
|
|
expr := fmt.Sprintf("(%s) as %s", queryStr, b.Grammar.WrapTable(as))
|
|
var operator string
|
|
joinType := JOIN_TYPE_RIGHT
|
|
var second interface{}
|
|
switch len(params) {
|
|
case 1:
|
|
operator = "="
|
|
second = params[0]
|
|
case 2:
|
|
operator = params[0].(string)
|
|
second = params[1]
|
|
|
|
}
|
|
b.AddBinding(bindings, TYPE_JOIN)
|
|
|
|
return b.join(Raw(expr), first, operator, second, joinType, false)
|
|
}
|
|
|
|
// AddBinding Add a binding to the query.
|
|
func (b *Builder) AddBinding(value []interface{}, bindingType string) *Builder {
|
|
if _, ok := Bindings[bindingType]; !ok {
|
|
log.Panicf("invalid binding type:%s\n", bindingType)
|
|
}
|
|
var tv []interface{}
|
|
for _, v := range value {
|
|
if _, ok := v.(Expression); !ok {
|
|
tv = append(tv, v)
|
|
}
|
|
}
|
|
b.Bindings[bindingType] = append(b.Bindings[bindingType], tv...)
|
|
return b
|
|
}
|
|
|
|
// GetBindings Get the current query value bindings in a flattened slice.
|
|
func (b *Builder) GetBindings() (res []interface{}) {
|
|
for _, key := range BindingKeysInOrder {
|
|
if bindings, ok := b.Bindings[key]; ok {
|
|
res = append(res, bindings...)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// GetRawBindings Get the raw map of array of bindings.
|
|
func (b *Builder) GetRawBindings() map[string][]interface{} {
|
|
return b.Bindings
|
|
}
|
|
|
|
/*
|
|
MergeBindings Merge an array of bindings into our bindings.
|
|
*/
|
|
//func (b *Builder) MergeBindings(builder *Builder) *Builder {
|
|
// res := make(map[string][]interface{})
|
|
//
|
|
// for i, i2 := range collection {
|
|
//
|
|
// }
|
|
// return b
|
|
//}
|
|
|
|
/*
|
|
Where Add a basic where clause to the query.
|
|
column,operator,value,
|
|
*/
|
|
func (b *Builder) Where(params ...interface{}) *Builder {
|
|
|
|
//map of where conditions
|
|
if maps, ok := params[0].([][]interface{}); ok {
|
|
for _, conditions := range maps {
|
|
b.Where(conditions...)
|
|
}
|
|
return b
|
|
}
|
|
|
|
paramsLength := len(params)
|
|
var operator string
|
|
var value interface{}
|
|
var boolean = BOOLEAN_AND
|
|
switch condition := params[0].(type) {
|
|
case func(builder *Builder):
|
|
var boolean string
|
|
if paramsLength > 1 {
|
|
boolean = params[1].(string)
|
|
} else {
|
|
boolean = BOOLEAN_AND
|
|
}
|
|
//clousure
|
|
cb := CloneBuilderWithTable(b)
|
|
condition(cb)
|
|
return b.AddNestedWhereQuery(cb, boolean)
|
|
case Where:
|
|
b.Wheres = append(b.Wheres, condition)
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
return b
|
|
|
|
case []Where:
|
|
b.Wheres = append(b.Wheres, condition...)
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
return b
|
|
case Expression:
|
|
if paramsLength > 1 {
|
|
boolean = params[1].(string)
|
|
} else {
|
|
boolean = BOOLEAN_AND
|
|
}
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: CONDITION_TYPE_RAW,
|
|
RawSql: condition,
|
|
Boolean: boolean,
|
|
})
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
return b
|
|
case map[string]interface{}:
|
|
boolean = BOOLEAN_AND
|
|
if paramsLength > 1 {
|
|
boolean = params[1].(string)
|
|
}
|
|
cb := CloneBuilderWithTable(b)
|
|
for k, v := range condition {
|
|
cb.Where(k, v)
|
|
}
|
|
return b.AddNestedWhereQuery(cb, boolean)
|
|
}
|
|
switch paramsLength {
|
|
case 2:
|
|
//assume operator is = and omitted
|
|
operator = "="
|
|
value = params[1]
|
|
case 3:
|
|
//correspond to column,operator,value
|
|
operator = params[1].(string)
|
|
value = params[2]
|
|
case 4:
|
|
//correspond to column,operator,value,boolean jointer
|
|
operator = params[1].(string)
|
|
value = params[2]
|
|
boolean = params[3].(string)
|
|
}
|
|
column := params[0].(string)
|
|
//operator might be in/not in/between/not between,in there cases we need take value as slice
|
|
if strings.Contains("in,not in,between,not between", operator) {
|
|
switch operator {
|
|
case CONDITION_TYPE_IN:
|
|
b.WhereIn(column, value, boolean)
|
|
return b
|
|
case CONDITION_TYPE_NOT_IN:
|
|
b.WhereNotIn(column, value, boolean)
|
|
return b
|
|
case CONDITION_TYPE_BETWEEN:
|
|
b.WhereBetween(column, value, boolean)
|
|
return b
|
|
case CONDITION_TYPE_NOT_BETWEEN:
|
|
b.WhereNotBetween(column, value, boolean)
|
|
return b
|
|
}
|
|
}
|
|
if f, ok := value.(func(builder *Builder)); ok {
|
|
return b.WhereSub(column, operator, f, boolean)
|
|
}
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: CONDITION_TYPE_BASIC,
|
|
Column: column,
|
|
Operator: operator,
|
|
Value: value,
|
|
Boolean: boolean,
|
|
})
|
|
b.AddBinding([]interface{}{value}, TYPE_WHERE)
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
return b
|
|
}
|
|
func (b *Builder) WherePivot(params ...interface{}) *Builder {
|
|
|
|
column := params[0].(string)
|
|
paramsLength := len(params)
|
|
var operator string
|
|
var value interface{}
|
|
var boolean = BOOLEAN_AND
|
|
switch paramsLength {
|
|
case 2:
|
|
operator = "="
|
|
value = params[1]
|
|
case 3:
|
|
operator = params[1].(string)
|
|
value = params[2]
|
|
case 4:
|
|
operator = params[1].(string)
|
|
value = params[2]
|
|
boolean = params[3].(string)
|
|
}
|
|
|
|
b.PivotWheres = append(b.PivotWheres, Where{
|
|
Type: CONDITION_TYPE_BASIC,
|
|
Column: column,
|
|
Operator: operator,
|
|
Value: value,
|
|
Boolean: boolean,
|
|
})
|
|
return b
|
|
}
|
|
|
|
/*
|
|
OrWhere Add an "or where" clause to the query.
|
|
*/
|
|
func (b *Builder) OrWhere(params ...interface{}) *Builder {
|
|
paramsLength := len(params)
|
|
if clousure, ok := params[0].(func(builder *Builder)); ok {
|
|
return b.Where(clousure, BOOLEAN_OR)
|
|
}
|
|
if paramsLength == 2 {
|
|
params = []interface{}{params[0], "=", params[1], BOOLEAN_OR}
|
|
} else {
|
|
params = append(params, BOOLEAN_OR)
|
|
}
|
|
return b.Where(params...)
|
|
}
|
|
|
|
/*
|
|
WhereColumn Add a "where" clause comparing two columns to the query.
|
|
*/
|
|
func (b *Builder) WhereColumn(first interface{}, second ...string) *Builder {
|
|
length := len(second)
|
|
var firstColumn = first
|
|
var secondColumn, operator, boolean string
|
|
if arr, ok := first.([][]interface{}); ok {
|
|
return b.WhereNested(func(builder *Builder) {
|
|
for _, term := range arr {
|
|
var strs []string
|
|
for i := 1; i < len(term); i++ {
|
|
strs = append(strs, term[i].(string))
|
|
}
|
|
builder.WhereColumn(term[0], strs...)
|
|
}
|
|
})
|
|
|
|
}
|
|
switch length {
|
|
case 1:
|
|
secondColumn = second[0]
|
|
operator = "="
|
|
boolean = BOOLEAN_AND
|
|
case 2:
|
|
operator = second[0]
|
|
secondColumn = second[1]
|
|
boolean = BOOLEAN_AND
|
|
case 3:
|
|
operator = second[0]
|
|
secondColumn = second[1]
|
|
boolean = second[2]
|
|
default:
|
|
panic("wrong arguements in where column")
|
|
}
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: CONDITION_TYPE_COLUMN,
|
|
FirstColumn: firstColumn.(string),
|
|
Operator: operator,
|
|
SecondColumn: secondColumn,
|
|
Boolean: boolean,
|
|
})
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
|
|
return b
|
|
}
|
|
|
|
/*
|
|
OrWhereColumn Add an "or where" clause comparing two columns to the query.
|
|
*/
|
|
func (b *Builder) OrWhereColumn(first string, second ...string) *Builder {
|
|
var ts = make([]string, 3, 3)
|
|
switch len(second) {
|
|
case 1:
|
|
ts = []string{"=", second[0], BOOLEAN_OR}
|
|
case 2:
|
|
ts = []string{second[0], second[1], BOOLEAN_OR}
|
|
}
|
|
return b.WhereColumn(first, ts...)
|
|
}
|
|
|
|
/*
|
|
WhereRaw Add a raw where clause to the query.
|
|
*/
|
|
func (b *Builder) WhereRaw(rawSql string, params ...interface{}) *Builder {
|
|
paramsLength := len(params)
|
|
var boolean string = BOOLEAN_AND
|
|
var bindings []interface{}
|
|
switch paramsLength {
|
|
case 1:
|
|
bindings = params[0].([]interface{})
|
|
b.AddBinding(bindings, TYPE_WHERE)
|
|
case 2:
|
|
bindings = params[0].([]interface{})
|
|
b.AddBinding(bindings, TYPE_WHERE)
|
|
boolean = params[1].(string)
|
|
}
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: CONDITION_TYPE_RAW,
|
|
RawSql: Raw(rawSql),
|
|
Boolean: boolean,
|
|
})
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
/*
|
|
OrWhereRaw Add a raw or where clause to the query.
|
|
*/
|
|
func (b *Builder) OrWhereRaw(rawSql string, bindings ...[]interface{}) *Builder {
|
|
switch len(bindings) {
|
|
case 0:
|
|
return b.WhereRaw(rawSql, []interface{}{}, BOOLEAN_OR)
|
|
case 1:
|
|
return b.WhereRaw(rawSql, bindings[0], BOOLEAN_OR)
|
|
}
|
|
panic(errors.New("arguements mismatch"))
|
|
}
|
|
|
|
/*
|
|
WhereIn Add a "where in" clause to the query.
|
|
column values boolean not
|
|
*/
|
|
func (b *Builder) WhereIn(params ...interface{}) *Builder {
|
|
paramsLength := len(params)
|
|
var boolean string
|
|
not := false
|
|
if paramsLength > 2 {
|
|
boolean = params[2].(string)
|
|
} else {
|
|
boolean = BOOLEAN_AND
|
|
}
|
|
if paramsLength > 3 {
|
|
not = params[3].(bool)
|
|
}
|
|
|
|
var values []interface{}
|
|
if IsQueryable(params[1]) {
|
|
queryStr, bindings := b.CreateSub(params[1])
|
|
values = append(values, Raw(queryStr))
|
|
b.AddBinding(bindings, TYPE_WHERE)
|
|
} else {
|
|
values = InterfaceToSlice(params[1])
|
|
}
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: CONDITION_TYPE_IN,
|
|
Column: params[0].(string),
|
|
Values: values,
|
|
Boolean: boolean,
|
|
Not: not,
|
|
})
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
b.AddBinding(values, TYPE_WHERE)
|
|
return b
|
|
}
|
|
|
|
/*
|
|
OrWhereIn Add an "or where in" clause to the query.
|
|
column values
|
|
*/
|
|
func (b *Builder) OrWhereIn(params ...interface{}) *Builder {
|
|
params = append(params, BOOLEAN_OR, false)
|
|
return b.WhereIn(params...)
|
|
}
|
|
|
|
// column values [ boolean ]
|
|
func (b *Builder) WhereNotIn(params ...interface{}) *Builder {
|
|
params = append(params, BOOLEAN_AND, true)
|
|
return b.WhereIn(params...)
|
|
}
|
|
|
|
/*
|
|
OrWhereNotIn Add an "or where not in" clause to the query.
|
|
column values
|
|
*/
|
|
func (b *Builder) OrWhereNotIn(params ...interface{}) *Builder {
|
|
params = append(params, BOOLEAN_OR, true)
|
|
return b.WhereIn(params...)
|
|
}
|
|
|
|
/*
|
|
WhereNull Add a "where null" clause to the query.
|
|
|
|
params takes in below order:
|
|
1. column string
|
|
2. boolean string in [2]string{"and","or"}
|
|
3. type string "not"
|
|
*/
|
|
func (b *Builder) WhereNull(column interface{}, params ...interface{}) *Builder {
|
|
paramsLength := len(params)
|
|
var boolean = BOOLEAN_AND
|
|
var not = false
|
|
switch paramsLength {
|
|
case 1:
|
|
boolean = params[0].(string)
|
|
case 2:
|
|
boolean = params[0].(string)
|
|
not = params[1].(bool)
|
|
}
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
switch columnTemp := column.(type) {
|
|
case string:
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: CONDITION_TYPE_NULL,
|
|
Column: columnTemp,
|
|
Boolean: boolean,
|
|
Not: not,
|
|
})
|
|
case []interface{}:
|
|
for _, i := range columnTemp {
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: CONDITION_TYPE_NULL,
|
|
Column: i.(string),
|
|
Boolean: boolean,
|
|
Not: not,
|
|
})
|
|
}
|
|
case []string:
|
|
for _, i := range columnTemp {
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: CONDITION_TYPE_NULL,
|
|
Column: i,
|
|
Boolean: boolean,
|
|
Not: not,
|
|
})
|
|
}
|
|
}
|
|
|
|
return b
|
|
}
|
|
|
|
/*
|
|
WhereNotNull Add a "where not null" clause to the query.
|
|
*/
|
|
func (b *Builder) WhereNotNull(column interface{}, params ...interface{}) *Builder {
|
|
paramsLength := len(params)
|
|
if paramsLength == 0 {
|
|
params = append(params, BOOLEAN_AND, true)
|
|
} else if paramsLength == 1 {
|
|
params = append(params, true)
|
|
}
|
|
return b.WhereNull(column, params...)
|
|
}
|
|
|
|
/*
|
|
OrWhereNull Add an "or where null" clause to the query.
|
|
column not
|
|
*/
|
|
func (b *Builder) OrWhereNull(column interface{}, params ...interface{}) *Builder {
|
|
paramsLength := len(params)
|
|
if paramsLength == 0 {
|
|
params = append(params, BOOLEAN_OR, false)
|
|
} else if paramsLength == 1 {
|
|
params = []interface{}{BOOLEAN_OR, params[0]}
|
|
}
|
|
return b.WhereNull(column, params...)
|
|
}
|
|
|
|
/*
|
|
OrWhereNotNull Add an "or where not null" clause to the query.
|
|
*/
|
|
func (b *Builder) OrWhereNotNull(column interface{}) *Builder {
|
|
params := []interface{}{BOOLEAN_OR, true}
|
|
return b.WhereNull(column, params...)
|
|
}
|
|
|
|
/*
|
|
WhereBetween Add a where between statement to the query.
|
|
|
|
params takes in below order:
|
|
1. WhereBetween(column string,values []interface{"min","max"})
|
|
2. WhereBetween(column string,values []interface{"min","max"},"and/or")
|
|
3. WhereBetween(column string,values []interface{"min","max","and/or",true/false})
|
|
*/
|
|
func (b *Builder) WhereBetween(params ...interface{}) *Builder {
|
|
paramsLength := len(params)
|
|
var boolean = BOOLEAN_AND
|
|
not := false
|
|
if paramsLength > 2 {
|
|
boolean = params[2].(string)
|
|
}
|
|
var betweenType = CONDITION_TYPE_BETWEEN
|
|
if paramsLength > 3 {
|
|
not = params[3].(bool)
|
|
}
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
tvalues := params[1].([]interface{})[0:2]
|
|
for _, tvalue := range tvalues {
|
|
if _, ok := tvalue.(Expression); !ok {
|
|
b.AddBinding([]interface{}{tvalue}, TYPE_WHERE)
|
|
}
|
|
}
|
|
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: betweenType,
|
|
Column: params[0].(string),
|
|
Boolean: boolean,
|
|
Values: params[1].([]interface{})[0:2],
|
|
Not: not,
|
|
})
|
|
return b
|
|
}
|
|
func (b *Builder) WhereNotBetween(params ...interface{}) *Builder {
|
|
if len(params) == 2 {
|
|
params = append(params, BOOLEAN_AND, true)
|
|
}
|
|
return b.WhereBetween(params...)
|
|
}
|
|
|
|
/*
|
|
OrWhereBetween Add an or where between statement to the query.
|
|
*/
|
|
func (b *Builder) OrWhereBetween(params ...interface{}) *Builder {
|
|
params = append(params, BOOLEAN_OR)
|
|
return b.WhereBetween(params...)
|
|
}
|
|
|
|
/*
|
|
OrWhereNotBetween Add an or where not between statement to the query.
|
|
*/
|
|
func (b *Builder) OrWhereNotBetween(params ...interface{}) *Builder {
|
|
params = append(params, BOOLEAN_OR, true)
|
|
|
|
return b.WhereBetween(params...)
|
|
}
|
|
|
|
/*
|
|
WhereBetweenColumns Add a where between statement using columns to the query.
|
|
*/
|
|
func (b *Builder) WhereBetweenColumns(column string, values []interface{}, params ...interface{}) *Builder {
|
|
paramsLength := len(params)
|
|
var boolean = BOOLEAN_AND
|
|
var betweenType = CONDITION_TYPE_BETWEEN_COLUMN
|
|
not := false
|
|
if paramsLength > 0 {
|
|
boolean = params[0].(string)
|
|
}
|
|
if paramsLength > 1 {
|
|
not = params[1].(bool)
|
|
}
|
|
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: betweenType,
|
|
Column: column,
|
|
Boolean: boolean,
|
|
Values: values,
|
|
Not: not,
|
|
})
|
|
return b
|
|
}
|
|
|
|
// AddTimeBasedWhere Add a time based (year, month, day, time) statement to the query.
|
|
// params order : timefuncionname column operator value boolean
|
|
// minimum : timefuncionname column value
|
|
func (b *Builder) AddTimeBasedWhere(params ...interface{}) *Builder {
|
|
paramsLength := len(params)
|
|
var timeType = params[0]
|
|
var boolean = BOOLEAN_AND
|
|
var operator string
|
|
var value interface{}
|
|
var tvalue interface{}
|
|
//timefunction column value
|
|
if paramsLength == 3 {
|
|
operator = "="
|
|
tvalue = params[2]
|
|
} else if paramsLength > 3 {
|
|
//timefunction column operator value
|
|
operator = params[2].(string)
|
|
tvalue = params[3]
|
|
//timefunction column operator value boolean
|
|
if paramsLength > 4 && params[4].(string) != boolean {
|
|
boolean = BOOLEAN_OR
|
|
}
|
|
} else {
|
|
tvalue = params[3]
|
|
}
|
|
switch tvalue.(type) {
|
|
case string:
|
|
value = tvalue.(string)
|
|
case int:
|
|
value = tvalue.(int)
|
|
case time.Time:
|
|
switch timeType.(string) {
|
|
case CONDITION_TYPE_DATE:
|
|
value = tvalue.(time.Time).Format("2006-01-02")
|
|
case CONDITION_TYPE_MONTH:
|
|
value = tvalue.(time.Time).Format("01")
|
|
case CONDITION_TYPE_YEAR:
|
|
value = tvalue.(time.Time).Format("2006")
|
|
case CONDITION_TYPE_TIME:
|
|
value = tvalue.(time.Time).Format("15:04:05")
|
|
case CONDITION_TYPE_DAY:
|
|
value = tvalue.(time.Time).Format("02")
|
|
}
|
|
case Expression:
|
|
value = tvalue.(Expression)
|
|
}
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: timeType.(string),
|
|
Column: params[1].(string),
|
|
Boolean: boolean,
|
|
Value: value,
|
|
Operator: operator,
|
|
})
|
|
b.AddBinding([]interface{}{value}, TYPE_WHERE)
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
return b
|
|
}
|
|
|
|
// column operator value boolean
|
|
func (b *Builder) WhereDate(params ...interface{}) *Builder {
|
|
p := append([]interface{}{CONDITION_TYPE_DATE}, params...)
|
|
return b.AddTimeBasedWhere(p...)
|
|
}
|
|
func (b *Builder) WhereTime(params ...interface{}) *Builder {
|
|
p := append([]interface{}{CONDITION_TYPE_TIME}, params...)
|
|
return b.AddTimeBasedWhere(p...)
|
|
}
|
|
func (b *Builder) WhereDay(params ...interface{}) *Builder {
|
|
p := append([]interface{}{CONDITION_TYPE_DAY}, params...)
|
|
return b.AddTimeBasedWhere(p...)
|
|
}
|
|
func (b *Builder) WhereMonth(params ...interface{}) *Builder {
|
|
p := append([]interface{}{CONDITION_TYPE_MONTH}, params...)
|
|
return b.AddTimeBasedWhere(p...)
|
|
}
|
|
func (b *Builder) WhereYear(params ...interface{}) *Builder {
|
|
p := append([]interface{}{CONDITION_TYPE_YEAR}, params...)
|
|
return b.AddTimeBasedWhere(p...)
|
|
}
|
|
|
|
/*
|
|
WhereNested Add a nested where statement to the query.
|
|
*/
|
|
func (b *Builder) WhereNested(params ...interface{}) *Builder {
|
|
paramsLength := len(params)
|
|
if paramsLength == 1 {
|
|
params = append(params, BOOLEAN_AND)
|
|
}
|
|
cb := CloneBuilderWithTable(b)
|
|
switch params[0].(type) {
|
|
case Where:
|
|
cb.Wheres = append(cb.Wheres, params[0].(Where))
|
|
case []Where:
|
|
cb.Wheres = append(cb.Wheres, params[0].([]Where)...)
|
|
case [][]interface{}:
|
|
tp := params[0].([][]interface{})
|
|
for i := 0; i < len(tp); i++ {
|
|
cb.Where(tp[i]...)
|
|
}
|
|
case []interface{}:
|
|
cb.Where(params[0].([]interface{}))
|
|
case func(builder *Builder):
|
|
var boolean string
|
|
if paramsLength > 1 {
|
|
boolean = params[1].(string)
|
|
} else {
|
|
boolean = BOOLEAN_AND
|
|
}
|
|
closure := params[0].(func(builder *Builder))
|
|
closure(cb)
|
|
return b.AddNestedWhereQuery(cb, boolean)
|
|
}
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: CONDITION_TYPE_NESTED,
|
|
Boolean: params[1].(string),
|
|
Value: cb,
|
|
})
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
return b
|
|
}
|
|
func (b *Builder) WhereSub(column string, operator string, value func(builder *Builder), boolean string) *Builder {
|
|
cb := CloneBuilderWithTable(b)
|
|
value(cb)
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: CONDITION_TYPE_SUB,
|
|
Operator: operator,
|
|
Value: cb,
|
|
Column: column,
|
|
Boolean: boolean,
|
|
})
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
b.AddBinding(cb.GetBindings(), TYPE_WHERE)
|
|
return b
|
|
}
|
|
|
|
// WhereExists Add an exists clause to the query.
|
|
// 1. WhereExists(cb,"and",false)
|
|
// 2. WhereExists(cb,"and")
|
|
// 3. WhereExists(cb)
|
|
func (b *Builder) WhereExists(cb func(builder *Builder), params ...interface{}) *Builder {
|
|
newBuilder := CloneBuilderWithTable(b)
|
|
cb(newBuilder)
|
|
boolean := BOOLEAN_AND
|
|
not := false
|
|
switch len(params) {
|
|
case 1:
|
|
boolean = params[0].(string)
|
|
case 2:
|
|
boolean = params[0].(string)
|
|
not = params[1].(bool)
|
|
}
|
|
|
|
return b.AddWhereExistsQuery(newBuilder, boolean, not)
|
|
}
|
|
|
|
/*
|
|
OrWhereExists Add an exists clause to the query.
|
|
*/
|
|
func (b *Builder) OrWhereExists(cb func(builder *Builder), params ...interface{}) *Builder {
|
|
not := false
|
|
if len(params) > 0 {
|
|
not = params[0].(bool)
|
|
}
|
|
return b.WhereExists(cb, BOOLEAN_OR, not)
|
|
}
|
|
|
|
/*
|
|
WhereNotExists Add a where not exists clause to the query.
|
|
*/
|
|
func (b *Builder) WhereNotExists(cb func(builder *Builder), params ...interface{}) *Builder {
|
|
boolean := BOOLEAN_AND
|
|
if len(params) > 0 {
|
|
boolean = params[0].(string)
|
|
}
|
|
return b.WhereExists(cb, boolean, true)
|
|
}
|
|
|
|
/*
|
|
OrWhereNotExists Add a where not exists clause to the query.
|
|
*/
|
|
func (b *Builder) OrWhereNotExists(cb func(builder *Builder), params ...interface{}) *Builder {
|
|
return b.OrWhereExists(cb, true)
|
|
}
|
|
|
|
// AddWhereExistsQuery Add an exists clause to the query.
|
|
func (b *Builder) AddWhereExistsQuery(builder *Builder, boolean string, not bool) *Builder {
|
|
var n bool
|
|
if not {
|
|
n = true
|
|
} else {
|
|
n = false
|
|
}
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: CONDITION_TYPE_EXIST,
|
|
Query: builder,
|
|
Boolean: boolean,
|
|
Not: n,
|
|
})
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
b.AddBinding(builder.GetBindings(), TYPE_WHERE)
|
|
return b
|
|
}
|
|
|
|
/*
|
|
GroupBy Add a "group by" clause to the query.
|
|
column operator value boolean
|
|
*/
|
|
func (b *Builder) GroupBy(columns ...interface{}) *Builder {
|
|
for _, column := range columns {
|
|
b.Groups = append(b.Groups, column)
|
|
}
|
|
b.Components[TYPE_GROUP_BY] = struct{}{}
|
|
return b
|
|
}
|
|
|
|
/*
|
|
GroupByRaw Add a raw groupBy clause to the query.
|
|
*/
|
|
func (b *Builder) GroupByRaw(sql string, bindings ...[]interface{}) *Builder {
|
|
b.Groups = append(b.Groups, Expression(sql))
|
|
if len(bindings) > 0 {
|
|
b.AddBinding(bindings[0], TYPE_GROUP_BY)
|
|
}
|
|
b.Components[TYPE_GROUP_BY] = struct{}{}
|
|
|
|
return b
|
|
}
|
|
|
|
/*
|
|
Having Add a "having" clause to the query.
|
|
column operator value boolean
|
|
*/
|
|
func (b *Builder) Having(params ...interface{}) *Builder {
|
|
havingBoolean := BOOLEAN_AND
|
|
havingOperator := "="
|
|
var havingValue interface{}
|
|
var havingColumn string
|
|
length := len(params)
|
|
switch length {
|
|
case 2:
|
|
havingColumn = params[0].(string)
|
|
havingValue = params[1]
|
|
case 3:
|
|
havingColumn = params[0].(string)
|
|
havingOperator = params[1].(string)
|
|
havingValue = params[2]
|
|
case 4:
|
|
havingColumn = params[0].(string)
|
|
havingOperator = params[1].(string)
|
|
havingValue = params[2]
|
|
havingBoolean = params[3].(string)
|
|
}
|
|
|
|
having := Having{
|
|
HavingType: CONDITION_TYPE_BASIC,
|
|
HavingColumn: havingColumn,
|
|
HavingOperator: havingOperator,
|
|
HavingValue: havingValue,
|
|
HavingBoolean: havingBoolean,
|
|
}
|
|
b.AddBinding([]interface{}{havingValue}, TYPE_HAVING)
|
|
b.Components[TYPE_HAVING] = struct{}{}
|
|
b.Havings = append(b.Havings, having)
|
|
return b
|
|
}
|
|
|
|
/*
|
|
HavingRaw Add a raw having clause to the query.
|
|
*/
|
|
func (b *Builder) HavingRaw(params ...interface{}) *Builder {
|
|
length := len(params)
|
|
havingBoolean := BOOLEAN_AND
|
|
var expression Expression
|
|
switch length {
|
|
case 1:
|
|
if expr, ok := params[0].(Expression); ok {
|
|
expression = expr
|
|
} else {
|
|
expression = Expression(params[0].(string))
|
|
}
|
|
case 2:
|
|
if expr, ok := params[0].(Expression); ok {
|
|
expression = expr
|
|
} else {
|
|
expression = Expression(params[0].(string))
|
|
}
|
|
b.AddBinding(params[1].([]interface{}), TYPE_HAVING)
|
|
case 3:
|
|
if expr, ok := params[0].(Expression); ok {
|
|
expression = expr
|
|
} else {
|
|
expression = Expression(params[0].(string))
|
|
}
|
|
b.AddBinding(params[1].([]interface{}), TYPE_HAVING)
|
|
havingBoolean = params[2].(string)
|
|
}
|
|
having := Having{
|
|
HavingType: CONDITION_TYPE_RAW,
|
|
HavingValue: expression,
|
|
HavingBoolean: havingBoolean,
|
|
RawSql: expression,
|
|
}
|
|
b.Components[TYPE_HAVING] = struct{}{}
|
|
b.Havings = append(b.Havings, having)
|
|
return b
|
|
}
|
|
|
|
/*
|
|
OrHavingRaw Add a raw having clause to the query.
|
|
*/
|
|
func (b *Builder) OrHavingRaw(params ...interface{}) *Builder {
|
|
bindings := []interface{}{}
|
|
if len(params) == 2 {
|
|
bindings = params[1].([]interface{})
|
|
}
|
|
return b.HavingRaw(params[0], bindings, BOOLEAN_OR)
|
|
}
|
|
|
|
/*
|
|
OrHaving Add an "or having" clause to the query.
|
|
*/
|
|
func (b *Builder) OrHaving(params ...interface{}) *Builder {
|
|
return b.Having(params[0], "=", params[1], BOOLEAN_OR)
|
|
}
|
|
|
|
/*
|
|
HavingBetween Add a "having between " clause to the query.
|
|
*/
|
|
func (b *Builder) HavingBetween(column string, params ...interface{}) *Builder {
|
|
var values []interface{}
|
|
boolean := BOOLEAN_AND
|
|
not := false
|
|
length := len(params)
|
|
switch length {
|
|
case 1:
|
|
values = params[0].([]interface{})[0:2]
|
|
case 2:
|
|
values = params[0].([]interface{})[0:2]
|
|
boolean = params[1].(string)
|
|
case 3:
|
|
values = params[0].([]interface{})[0:2]
|
|
boolean = params[1].(string)
|
|
not = params[2].(bool)
|
|
}
|
|
having := Having{
|
|
HavingType: CONDITION_TYPE_BETWEEN,
|
|
HavingColumn: column,
|
|
HavingValue: values,
|
|
HavingBoolean: boolean,
|
|
Not: not,
|
|
}
|
|
b.Components[TYPE_HAVING] = struct{}{}
|
|
b.AddBinding(values, TYPE_HAVING)
|
|
|
|
b.Havings = append(b.Havings, having)
|
|
return b
|
|
}
|
|
|
|
/*
|
|
OrderBy Add an "order by" clause to the query.
|
|
*/
|
|
func (b *Builder) OrderBy(params ...interface{}) *Builder {
|
|
var order = ORDER_ASC
|
|
if r, ok := params[0].(Expression); ok {
|
|
b.Orders = append(b.Orders, Order{
|
|
RawSql: r,
|
|
OrderType: CONDITION_TYPE_RAW,
|
|
})
|
|
b.Components[TYPE_ORDER] = struct{}{}
|
|
|
|
return b
|
|
}
|
|
if len(params) > 1 {
|
|
order = params[1].(string)
|
|
}
|
|
if order != ORDER_ASC && order != ORDER_DESC {
|
|
panic(errors.New("wrong order direction: " + order))
|
|
}
|
|
b.Orders = append(b.Orders, Order{
|
|
Direction: order,
|
|
Column: params[0].(string),
|
|
})
|
|
b.Components[TYPE_ORDER] = struct{}{}
|
|
|
|
return b
|
|
}
|
|
|
|
/*
|
|
OrderByRaw Add a raw "order by" clause to the query.
|
|
*/
|
|
func (b *Builder) OrderByRaw(sql string, bindings []interface{}) *Builder {
|
|
b.Orders = append(b.Orders, Order{
|
|
OrderType: CONDITION_TYPE_RAW,
|
|
RawSql: Raw(sql),
|
|
})
|
|
b.Components[TYPE_ORDER] = struct{}{}
|
|
b.AddBinding(bindings, TYPE_ORDER)
|
|
return b
|
|
}
|
|
func (b *Builder) OrderByDesc(column string) *Builder {
|
|
return b.OrderBy(column, ORDER_DESC)
|
|
}
|
|
|
|
/*
|
|
ReOrder Remove all existing orders and optionally add a new order.
|
|
*/
|
|
func (b *Builder) ReOrder(params ...string) *Builder {
|
|
b.Orders = nil
|
|
b.Bindings["order"] = nil
|
|
delete(b.Components, TYPE_ORDER)
|
|
length := len(params)
|
|
if length == 1 {
|
|
b.OrderBy(InterfaceToSlice(params[0]))
|
|
} else if length == 2 {
|
|
b.OrderBy(InterfaceToSlice(params[0:2])...)
|
|
}
|
|
return b
|
|
}
|
|
|
|
/*
|
|
Limit Set the "limit" value of the query.
|
|
*/
|
|
func (b *Builder) Limit(n int) *Builder {
|
|
b.Components[TYPE_LIMIT] = struct{}{}
|
|
b.LimitNum = int(math.Max(0, float64(n)))
|
|
return b
|
|
}
|
|
|
|
/*
|
|
Offset Set the "offset" value of the query.
|
|
*/
|
|
func (b *Builder) Offset(n int) *Builder {
|
|
b.OffsetNum = int(math.Max(0, float64(n)))
|
|
b.Components[TYPE_OFFSET] = struct{}{}
|
|
return b
|
|
}
|
|
|
|
/*
|
|
Union Add a union statement to the query.
|
|
*/
|
|
//func (b *Builder) Union(n int) *Builder {
|
|
// b.OffsetNum = n
|
|
// return b
|
|
//}
|
|
|
|
/*
|
|
Lock Lock the selected rows in the table for updating.
|
|
*/
|
|
func (b *Builder) Lock(lock ...interface{}) *Builder {
|
|
if len(lock) == 0 {
|
|
b.LockMode = true
|
|
} else {
|
|
b.LockMode = lock[0]
|
|
}
|
|
b.Components[TYPE_LOCK] = struct{}{}
|
|
return b
|
|
}
|
|
|
|
func (b *Builder) WhereMap(params map[string]interface{}) *Builder {
|
|
for key, param := range params {
|
|
b.Where(key, "=", param)
|
|
}
|
|
return b
|
|
}
|
|
|
|
// AddNestedWhereQuery Add another query builder as a nested where to the query builder.
|
|
func (b *Builder) AddNestedWhereQuery(builder *Builder, boolean string) *Builder {
|
|
|
|
if len(builder.Wheres) > 0 {
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: CONDITION_TYPE_NESTED,
|
|
Value: builder,
|
|
Boolean: boolean,
|
|
})
|
|
b.AddBinding(builder.GetRawBindings()[TYPE_WHERE], TYPE_WHERE)
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
}
|
|
return b
|
|
}
|
|
|
|
/*
|
|
First Execute the query and get the first result.
|
|
*/
|
|
func (b *Builder) First(dest interface{}, columns ...interface{}) (result sql.Result, err error) {
|
|
b.Limit(1)
|
|
return b.Get(dest, columns...)
|
|
}
|
|
|
|
/*
|
|
RunSelect run the query as a "select" statement against the connection.
|
|
*/
|
|
func (b *Builder) RunSelect() (result sql.Result, err error) {
|
|
result, err = b.Run(b.ToSql(), b.GetBindings(), func() (result sql.Result, err error) {
|
|
if b.Pretending {
|
|
return
|
|
}
|
|
if b.Tx != nil {
|
|
result, err = b.Tx.Select(b.Grammar.CompileSelect(), b.GetBindings(), b.Dest)
|
|
} else {
|
|
result, err = b.Connection.Select(b.Grammar.CompileSelect(), b.GetBindings(), b.Dest)
|
|
}
|
|
return
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
func (b *Builder) GetConnection() IConnection {
|
|
if b.Tx != nil {
|
|
return b.Tx
|
|
}
|
|
|
|
return b.Connection
|
|
}
|
|
|
|
func (b *Builder) Run(query string, bindings []interface{}, callback func() (result sql.Result, err error)) (result sql.Result, err error) {
|
|
defer func() {
|
|
catchedErr := recover()
|
|
if catchedErr != nil {
|
|
switch catchedErr.(type) {
|
|
case string:
|
|
err = errors.New(catchedErr.(string))
|
|
case error:
|
|
err = catchedErr.(error)
|
|
default:
|
|
err = errors.New("unknown panic")
|
|
}
|
|
}
|
|
}()
|
|
result, err = callback()
|
|
|
|
return
|
|
}
|
|
func (b *Builder) WithPivot(columns ...string) *Builder {
|
|
b.Pivots = append(b.Pivots, columns...)
|
|
return b
|
|
}
|
|
|
|
/*
|
|
Exists Determine if any rows exist for the current query.
|
|
*/
|
|
func (b *Builder) Exists() (exists bool, err error) {
|
|
_, err = b.Connection.Select(b.Grammar.CompileExists(), b.GetBindings(), &exists)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return exists, nil
|
|
}
|
|
|
|
/*
|
|
DoesntExist Determine if no rows exist for the current query.
|
|
*/
|
|
func (b *Builder) DoesntExist() (notExists bool, err error) {
|
|
e, err := b.Exists()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return !e, nil
|
|
}
|
|
|
|
/*
|
|
Aggregate Execute an aggregate function on the database.
|
|
*/
|
|
func (b *Builder) Aggregate(dest interface{}, fn string, column ...string) (result sql.Result, err error) {
|
|
b.Dest = dest
|
|
if column == nil {
|
|
column = append(column, "*")
|
|
}
|
|
b.Aggregates = append(b.Aggregates, Aggregate{
|
|
AggregateName: fn,
|
|
AggregateColumn: column[0],
|
|
})
|
|
b.Components[TYPE_AGGREGRATE] = struct{}{}
|
|
result, err = b.RunSelect()
|
|
return
|
|
}
|
|
|
|
/*
|
|
Count Retrieve the "count" result of the query.
|
|
*/
|
|
func (b *Builder) Count(dest interface{}, column ...string) (result sql.Result, err error) {
|
|
return b.Aggregate(dest, "count", column...)
|
|
}
|
|
|
|
/*
|
|
Min Retrieve the minimum value of a given column.
|
|
*/
|
|
func (b *Builder) Min(dest interface{}, column ...string) (result sql.Result, err error) {
|
|
|
|
return b.Aggregate(dest, "min", column...)
|
|
|
|
}
|
|
|
|
/*
|
|
Max Retrieve the maximum value of a given column.
|
|
*/
|
|
func (b *Builder) Max(dest interface{}, column ...string) (result sql.Result, err error) {
|
|
|
|
return b.Aggregate(dest, "max", column...)
|
|
|
|
}
|
|
|
|
/*
|
|
Avg Alias for the "avg" method.
|
|
*/
|
|
func (b *Builder) Avg(dest interface{}, column ...string) (result sql.Result, err error) {
|
|
|
|
return b.Aggregate(dest, "avg", column...)
|
|
|
|
}
|
|
func (b *Builder) Sum(dest interface{}, column ...string) (result sql.Result, err error) {
|
|
|
|
return b.Aggregate(dest, "sum", column...)
|
|
|
|
}
|
|
func (b *Builder) ForPage(page, perPage int64) *Builder {
|
|
|
|
b.Offset(int((page - 1) * perPage)).Limit(int(perPage))
|
|
return b
|
|
}
|
|
func (b *Builder) Only(columns ...string) *Builder {
|
|
b.OnlyColumns = make(map[string]interface{}, len(columns))
|
|
for i := 0; i < len(columns); i++ {
|
|
b.OnlyColumns[columns[i]] = nil
|
|
}
|
|
return b
|
|
}
|
|
func (b *Builder) Except(columns ...string) *Builder {
|
|
b.ExceptColumns = make(map[string]interface{}, len(columns))
|
|
for i := 0; i < len(columns); i++ {
|
|
b.ExceptColumns[columns[i]] = nil
|
|
}
|
|
return b
|
|
}
|
|
func (b *Builder) FileterColumn(column string) bool {
|
|
if b.OnlyColumns != nil {
|
|
if _, ok := b.OnlyColumns[column]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
if b.ExceptColumns != nil {
|
|
if _, ok := b.ExceptColumns[column]; ok {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func PrepareInsertValues(values interface{}) []map[string]interface{} {
|
|
rv := reflect.ValueOf(values)
|
|
var items []map[string]interface{}
|
|
if rv.Kind() == reflect.Ptr {
|
|
rv = reflect.Indirect(rv)
|
|
}
|
|
if rv.Kind() == reflect.Map {
|
|
items = append(items, rv.Interface().(map[string]interface{}))
|
|
} else if rv.Kind() == reflect.Slice {
|
|
eleType := rv.Type().Elem()
|
|
if eleType.Kind() == reflect.Ptr {
|
|
switch eleType.Elem().Kind() {
|
|
case reflect.Struct:
|
|
for i := 0; i < rv.Len(); i++ {
|
|
items = append(items, ExtractStruct(rv.Index(i).Elem().Interface()))
|
|
}
|
|
case reflect.Map:
|
|
for i := 0; i < rv.Len(); i++ {
|
|
items = append(items, rv.Index(i).Elem().Interface().(map[string]interface{}))
|
|
}
|
|
}
|
|
} else if eleType.Kind() == reflect.Map {
|
|
if tv, ok := values.(*[]map[string]interface{}); ok {
|
|
items = *tv
|
|
} else {
|
|
items = values.([]map[string]interface{})
|
|
}
|
|
} else if eleType.Kind() == reflect.Struct {
|
|
for i := 0; i < rv.Len(); i++ {
|
|
items = append(items, ExtractStruct(rv.Index(i).Interface()))
|
|
}
|
|
}
|
|
} else if rv.Kind() == reflect.Struct {
|
|
items = append(items, ExtractStruct(rv.Interface()))
|
|
}
|
|
return items
|
|
}
|
|
|
|
/*
|
|
Insert new records into the database.
|
|
*/
|
|
func (b *Builder) Insert(values interface{}) (result sql.Result, err error) {
|
|
items := PrepareInsertValues(values)
|
|
b.ApplyBeforeQueryCallBacks(TYPE_INSERT, items...)
|
|
return b.Run(b.Grammar.CompileInsert(items), b.GetBindings(), func() (result sql.Result, err error) {
|
|
if b.Pretending {
|
|
return
|
|
}
|
|
if b.Tx != nil {
|
|
result, err = b.Tx.Insert(b.PreparedSql, b.GetBindings())
|
|
} else {
|
|
result, err = b.Connection.Insert(b.PreparedSql, b.GetBindings())
|
|
}
|
|
defer b.ApplyAfterQueryCallBacks(TYPE_INSERT, result, err, items...)
|
|
return
|
|
})
|
|
}
|
|
|
|
/*
|
|
InsertGetId Insert a new record and get the value of the primary key.
|
|
*/
|
|
func (b *Builder) InsertGetId(values interface{}) (int64, error) {
|
|
insert, err := b.Insert(values)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
id, _ := insert.LastInsertId()
|
|
return id, nil
|
|
}
|
|
|
|
/*
|
|
InsertOrIgnore Insert a new record and get the value of the primary key.
|
|
*/
|
|
func (b *Builder) InsertOrIgnore(values interface{}) (result sql.Result, err error) {
|
|
items := PrepareInsertValues(values)
|
|
|
|
b.ApplyBeforeQueryCallBacks(TYPE_INSERT, items...)
|
|
return b.Run(b.Grammar.CompileInsertOrIgnore(items), b.GetBindings(), func() (result sql.Result, err error) {
|
|
if b.Pretending {
|
|
return
|
|
}
|
|
if b.Tx != nil {
|
|
result, err = b.Tx.Insert(b.PreparedSql, b.GetBindings())
|
|
} else {
|
|
result, err = b.Connection.Insert(b.PreparedSql, b.GetBindings())
|
|
}
|
|
defer b.ApplyAfterQueryCallBacks(TYPE_INSERT, result, err, items...)
|
|
return
|
|
})
|
|
}
|
|
func (b *Builder) Update(v map[string]interface{}) (result sql.Result, err error) {
|
|
b.ApplyBeforeQueryCallBacks(TYPE_UPDATE, v)
|
|
return b.Run(b.Grammar.CompileUpdate(v), b.GetBindings(), func() (result sql.Result, err error) {
|
|
if b.Pretending {
|
|
return
|
|
}
|
|
if b.Tx != nil {
|
|
result, err = b.Tx.Update(b.PreparedSql, b.GetBindings())
|
|
} else {
|
|
result, err = b.Connection.Update(b.PreparedSql, b.GetBindings())
|
|
}
|
|
defer b.ApplyAfterQueryCallBacks(TYPE_UPDATE, result, err, v)
|
|
return
|
|
})
|
|
}
|
|
|
|
/*
|
|
UpdateOrInsert insert or update a record matching the attributes, and fill it with values.
|
|
*/
|
|
func (b *Builder) UpdateOrInsert(conditions map[string]interface{}, values map[string]interface{}) (updated bool, err error) {
|
|
exist, err := b.Where(conditions).Exists()
|
|
if err != nil {
|
|
return
|
|
}
|
|
if !exist {
|
|
for k, v := range values {
|
|
conditions[k] = v
|
|
}
|
|
b.Reset(TYPE_WHERE)
|
|
_, err = b.Insert(conditions)
|
|
if err != nil {
|
|
return
|
|
}
|
|
return false, nil
|
|
} else {
|
|
_, err = b.Limit(1).Update(values)
|
|
if err != nil {
|
|
return
|
|
}
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
/*
|
|
Decrement Decrement a column's value by a given amount.
|
|
*/
|
|
func (b *Builder) Decrement(column string, amount int, extra ...map[string]interface{}) (result sql.Result, err error) {
|
|
|
|
var update map[string]interface{}
|
|
wrapped := b.Grammar.Wrap(column)
|
|
|
|
if len(extra) == 0 {
|
|
update = make(map[string]interface{})
|
|
} else {
|
|
update = extra[0]
|
|
}
|
|
update[column] = Expression(fmt.Sprintf("%s - %d", wrapped, amount))
|
|
|
|
return b.Update(update)
|
|
}
|
|
|
|
/*
|
|
Increment Increment a column's value by a given amount.
|
|
*/
|
|
func (b *Builder) Increment(column string, amount int, extra ...map[string]interface{}) (result sql.Result, err error) {
|
|
var update map[string]interface{}
|
|
wrapped := b.Grammar.Wrap(column)
|
|
|
|
if len(extra) == 0 {
|
|
update = make(map[string]interface{})
|
|
} else {
|
|
update = extra[0]
|
|
}
|
|
update[column] = Expression(fmt.Sprintf("%s + %d", wrapped, amount))
|
|
return b.Update(update)
|
|
}
|
|
|
|
/*
|
|
InRandomOrder Put the query's results in random order.
|
|
*/
|
|
func (b *Builder) InRandomOrder(seed string) *Builder {
|
|
return b.OrderByRaw(b.Grammar.CompileRandom(seed), []interface{}{})
|
|
}
|
|
|
|
/*
|
|
Delete Delete records from the database.
|
|
//TODO: Delete(1) Delete(1,2,3) Delete([]interface{}{1,2,3})
|
|
*/
|
|
func (b *Builder) Delete(id ...interface{}) (result sql.Result, err error) {
|
|
if len(id) > 0 {
|
|
b.Where("id", id[0])
|
|
}
|
|
b.ApplyBeforeQueryCallBacks(TYPE_DELETE)
|
|
return b.Run(b.Grammar.CompileDelete(), b.GetBindings(), func() (result sql.Result, err error) {
|
|
if b.Pretending {
|
|
return
|
|
}
|
|
if b.Tx != nil {
|
|
result, err = b.Tx.Delete(b.PreparedSql, b.GetBindings())
|
|
} else {
|
|
result, err = b.Connection.Delete(b.PreparedSql, b.GetBindings())
|
|
}
|
|
defer b.ApplyAfterQueryCallBacks(TYPE_DELETE, result, err)
|
|
return
|
|
})
|
|
|
|
}
|
|
func (b *Builder) Raw() *sql.DB {
|
|
return b.Connection.GetDB()
|
|
}
|
|
|
|
/*
|
|
Get Execute the query as a "select" statement.
|
|
*/
|
|
func (b *Builder) Get(dest interface{}, columns ...interface{}) (result sql.Result, err error) {
|
|
if len(columns) > 0 {
|
|
b.Select(columns...)
|
|
}
|
|
b.Dest = dest
|
|
|
|
result, err = b.RunSelect()
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
Pluck Get a collection instance containing the values of a given column.
|
|
*/
|
|
func (b *Builder) Pluck(dest interface{}, params interface{}) (sql.Result, error) {
|
|
return b.Get(dest, params)
|
|
}
|
|
|
|
/*
|
|
When Apply the callback if the given "value" is truthy.
|
|
|
|
1. When(true,func(builder *Builder))
|
|
2. When(true,func(builder *Builder),func(builder *Builder)) //with default callback
|
|
*/
|
|
func (b *Builder) When(boolean bool, cb ...func(builder *Builder)) *Builder {
|
|
if boolean {
|
|
cb[0](b)
|
|
} else if len(cb) == 2 {
|
|
//if false and we have default callback
|
|
cb[1](b)
|
|
}
|
|
return b
|
|
}
|
|
func (b *Builder) Value(dest interface{}, column string) (sql.Result, error) {
|
|
return b.First(dest, column)
|
|
}
|
|
|
|
/*
|
|
Reset
|
|
reset bindings and components
|
|
*/
|
|
func (b *Builder) Reset(targets ...string) *Builder {
|
|
for _, componentName := range targets {
|
|
switch componentName {
|
|
case TYPE_ORDER:
|
|
delete(b.Components, TYPE_ORDER)
|
|
delete(b.Bindings, TYPE_ORDER)
|
|
b.Orders = nil
|
|
case TYPE_LIMIT:
|
|
delete(b.Bindings, TYPE_LIMIT)
|
|
delete(b.Components, TYPE_LIMIT)
|
|
b.LimitNum = 0
|
|
case TYPE_OFFSET:
|
|
delete(b.Bindings, TYPE_OFFSET)
|
|
delete(b.Components, TYPE_OFFSET)
|
|
b.OffsetNum = 0
|
|
case TYPE_WHERE:
|
|
delete(b.Bindings, TYPE_WHERE)
|
|
delete(b.Components, TYPE_WHERE)
|
|
b.Wheres = nil
|
|
case TYPE_GROUP_BY:
|
|
delete(b.Bindings, TYPE_GROUP_BY)
|
|
delete(b.Components, TYPE_GROUP_BY)
|
|
b.Groups = nil
|
|
case TYPE_COLUMN:
|
|
delete(b.Bindings, TYPE_COLUMN)
|
|
delete(b.Components, TYPE_COLUMN)
|
|
b.Columns = nil
|
|
}
|
|
}
|
|
return b
|
|
}
|
|
func (b *Builder) EnableLogQuery() *Builder {
|
|
b.LoggingQueries = true
|
|
return b
|
|
}
|
|
func (b *Builder) DisableLogQuery() *Builder {
|
|
b.LoggingQueries = false
|
|
return b
|
|
}
|
|
|
|
/*
|
|
Register a closure to be invoked before the query is executed.
|
|
*/
|
|
func (b *Builder) Before(callback func(builder *Builder, t string, data ...map[string]interface{})) *Builder {
|
|
b.BeforeQueryCallBacks = append(b.BeforeQueryCallBacks, callback)
|
|
return b
|
|
}
|
|
|
|
/*
|
|
Register a closure to be invoked after the query is executed.
|
|
*/
|
|
func (b *Builder) After(callback func(builder *Builder, t string, res sql.Result, err error, data ...map[string]interface{})) *Builder {
|
|
b.AfterQueryCallBacks = append(b.AfterQueryCallBacks, callback)
|
|
return b
|
|
}
|
|
|
|
/*
|
|
ApplyBeforeQueryCallBacks
|
|
Invoke the "before query" modification callbacks.
|
|
*/
|
|
func (b *Builder) ApplyBeforeQueryCallBacks(t string, items ...map[string]any) {
|
|
for _, cb := range b.BeforeQueryCallBacks {
|
|
cb(b, t, items...)
|
|
}
|
|
|
|
b.BeforeQueryCallBacks = nil
|
|
}
|
|
|
|
func (b *Builder) ApplyAfterQueryCallBacks(t string, res sql.Result, err error, items ...map[string]any) {
|
|
for _, cb := range b.AfterQueryCallBacks {
|
|
cb(b, t, res, err, items...)
|
|
}
|
|
|
|
b.AfterQueryCallBacks = nil
|
|
}
|
|
|
|
/*
|
|
SetAggregate Set the aggregate property without running the query.
|
|
*/
|
|
func (b *Builder) SetAggregate(function string, column ...string) *Builder {
|
|
if len(column) == 0 {
|
|
column = append(column, "*")
|
|
}
|
|
b.Aggregates = append(b.Aggregates, Aggregate{
|
|
AggregateName: function,
|
|
AggregateColumn: column[0],
|
|
})
|
|
b.Components[TYPE_AGGREGRATE] = struct{}{}
|
|
if len(b.Groups) == 0 {
|
|
b.Reset(TYPE_ORDER)
|
|
}
|
|
return b
|
|
}
|
|
|
|
/*
|
|
GetCountForPagination Get the count of the total records for the paginator.
|
|
*/
|
|
func (b *Builder) GetCountForPagination() (total int) {
|
|
|
|
return
|
|
}
|
|
|
|
func (b *Builder) Chunk(dest interface{}, chunkSize int64, callback func(dest interface{}) error) (err error) {
|
|
if len(b.Orders) == 0 {
|
|
panic(errors.New("must specify an orderby clause when using this method"))
|
|
}
|
|
var page int64 = 1
|
|
var count int64 = 0
|
|
tempDest := reflect.New(reflect.Indirect(reflect.ValueOf(dest)).Type()).Interface()
|
|
get, err := b.ForPage(1, chunkSize).Get(tempDest)
|
|
if err != nil {
|
|
return
|
|
}
|
|
count, _ = get.RowsAffected()
|
|
for count > 0 {
|
|
err = callback(tempDest)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if count != chunkSize {
|
|
break
|
|
} else {
|
|
page++
|
|
tempDest = reflect.New(reflect.Indirect(reflect.ValueOf(dest)).Type()).Interface()
|
|
get, err = b.ForPage(page, chunkSize).Get(tempDest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
count, _ = get.RowsAffected()
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *Builder) Pretend() *Builder {
|
|
b.Pretending = true
|
|
return b
|
|
}
|
|
|
|
/*
|
|
Implode concatenate values of a given column as a string.
|
|
*/
|
|
func (b *Builder) Implode(column string, glue ...string) (string, error) {
|
|
var dest []string
|
|
_, err := b.Pluck(&dest, column)
|
|
if err != nil {
|
|
return "", nil
|
|
}
|
|
var sep string
|
|
if len(glue) == 0 {
|
|
sep = ""
|
|
} else {
|
|
sep = glue[0]
|
|
}
|
|
|
|
return strings.Join(dest, sep), nil
|
|
}
|
|
|
|
/*
|
|
WhereRowValues Adds a where condition using row values.
|
|
*/
|
|
func (b *Builder) WhereRowValues(columns []string, operator string, values []interface{}, params ...string) *Builder {
|
|
if len(columns) != len(values) {
|
|
panic(errors.New("argements number mismatch"))
|
|
}
|
|
var boolean = BOOLEAN_AND
|
|
if len(params) == 1 {
|
|
boolean = params[0]
|
|
}
|
|
b.Components[TYPE_WHERE] = struct{}{}
|
|
b.Wheres = append(b.Wheres, Where{
|
|
Type: CONDITION_TYPE_ROW_VALUES,
|
|
Operator: operator,
|
|
Columns: columns,
|
|
Values: values,
|
|
Boolean: boolean,
|
|
})
|
|
return b
|
|
}
|