db/builder.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
}