211 lines
5.0 KiB
Go
211 lines
5.0 KiB
Go
package dbr
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
)
|
|
|
|
// SelectBuilder contains the clauses for a SELECT statement
|
|
type SelectBuilder struct {
|
|
*Session
|
|
Runner
|
|
|
|
RawFullSql string
|
|
RawArguments []interface{}
|
|
|
|
IsDistinct bool
|
|
Columns []string
|
|
FromTable string
|
|
WhereFragments []*whereFragment
|
|
GroupBys []string
|
|
HavingFragments []*whereFragment
|
|
OrderBys []string
|
|
LimitCount uint64
|
|
LimitValid bool
|
|
OffsetCount uint64
|
|
OffsetValid bool
|
|
}
|
|
|
|
// Select creates a new SelectBuilder that select that given columns
|
|
func (sess *Session) Select(cols ...string) *SelectBuilder {
|
|
return &SelectBuilder{
|
|
Session: sess,
|
|
Runner: sess.cxn.Db,
|
|
Columns: cols,
|
|
}
|
|
}
|
|
|
|
// SelectBySql creates a new SelectBuilder for the given SQL string and arguments
|
|
func (sess *Session) SelectBySql(sql string, args ...interface{}) *SelectBuilder {
|
|
return &SelectBuilder{
|
|
Session: sess,
|
|
Runner: sess.cxn.Db,
|
|
RawFullSql: sql,
|
|
RawArguments: args,
|
|
}
|
|
}
|
|
|
|
// Select creates a new SelectBuilder that select that given columns bound to the transaction
|
|
func (tx *Tx) Select(cols ...string) *SelectBuilder {
|
|
return &SelectBuilder{
|
|
Session: tx.Session,
|
|
Runner: tx.Tx,
|
|
Columns: cols,
|
|
}
|
|
}
|
|
|
|
// SelectBySql creates a new SelectBuilder for the given SQL string and arguments bound to the transaction
|
|
func (tx *Tx) SelectBySql(sql string, args ...interface{}) *SelectBuilder {
|
|
return &SelectBuilder{
|
|
Session: tx.Session,
|
|
Runner: tx.Tx,
|
|
RawFullSql: sql,
|
|
RawArguments: args,
|
|
}
|
|
}
|
|
|
|
// Distinct marks the statement as a DISTINCT SELECT
|
|
func (b *SelectBuilder) Distinct() *SelectBuilder {
|
|
b.IsDistinct = true
|
|
return b
|
|
}
|
|
|
|
// From sets the table to SELECT FROM
|
|
func (b *SelectBuilder) From(from string) *SelectBuilder {
|
|
b.FromTable = from
|
|
return b
|
|
}
|
|
|
|
// Where appends a WHERE clause to the statement for the given string and args
|
|
// or map of column/value pairs
|
|
func (b *SelectBuilder) Where(whereSqlOrMap interface{}, args ...interface{}) *SelectBuilder {
|
|
b.WhereFragments = append(b.WhereFragments, newWhereFragment(whereSqlOrMap, args))
|
|
return b
|
|
}
|
|
|
|
// GroupBy appends a column to group the statement
|
|
func (b *SelectBuilder) GroupBy(group string) *SelectBuilder {
|
|
b.GroupBys = append(b.GroupBys, group)
|
|
return b
|
|
}
|
|
|
|
// Having appends a HAVING clause to the statement
|
|
func (b *SelectBuilder) Having(whereSqlOrMap interface{}, args ...interface{}) *SelectBuilder {
|
|
b.HavingFragments = append(b.HavingFragments, newWhereFragment(whereSqlOrMap, args))
|
|
return b
|
|
}
|
|
|
|
// OrderBy appends a column to ORDER the statement by
|
|
func (b *SelectBuilder) OrderBy(ord string) *SelectBuilder {
|
|
b.OrderBys = append(b.OrderBys, ord)
|
|
return b
|
|
}
|
|
|
|
// OrderDir appends a column to ORDER the statement by with a given direction
|
|
func (b *SelectBuilder) OrderDir(ord string, isAsc bool) *SelectBuilder {
|
|
if isAsc {
|
|
b.OrderBys = append(b.OrderBys, ord+" ASC")
|
|
} else {
|
|
b.OrderBys = append(b.OrderBys, ord+" DESC")
|
|
}
|
|
return b
|
|
}
|
|
|
|
// Limit sets a limit for the statement; overrides any existing LIMIT
|
|
func (b *SelectBuilder) Limit(limit uint64) *SelectBuilder {
|
|
b.LimitCount = limit
|
|
b.LimitValid = true
|
|
return b
|
|
}
|
|
|
|
// Offset sets an offset for the statement; overrides any existing OFFSET
|
|
func (b *SelectBuilder) Offset(offset uint64) *SelectBuilder {
|
|
b.OffsetCount = offset
|
|
b.OffsetValid = true
|
|
return b
|
|
}
|
|
|
|
// Paginate sets LIMIT/OFFSET for the statement based on the given page/perPage
|
|
// Assumes page/perPage are valid. Page and perPage must be >= 1
|
|
func (b *SelectBuilder) Paginate(page, perPage uint64) *SelectBuilder {
|
|
b.Limit(perPage)
|
|
b.Offset((page - 1) * perPage)
|
|
return b
|
|
}
|
|
|
|
// ToSql serialized the SelectBuilder to a SQL string
|
|
// It returns the string with placeholders and a slice of query arguments
|
|
func (b *SelectBuilder) ToSql() (string, []interface{}) {
|
|
if b.RawFullSql != "" {
|
|
return b.RawFullSql, b.RawArguments
|
|
}
|
|
|
|
if len(b.Columns) == 0 {
|
|
panic("no columns specified")
|
|
}
|
|
if len(b.FromTable) == 0 {
|
|
panic("no table specified")
|
|
}
|
|
|
|
var sql bytes.Buffer
|
|
var args []interface{}
|
|
|
|
sql.WriteString("SELECT ")
|
|
|
|
if b.IsDistinct {
|
|
sql.WriteString("DISTINCT ")
|
|
}
|
|
|
|
for i, s := range b.Columns {
|
|
if i > 0 {
|
|
sql.WriteString(", ")
|
|
}
|
|
sql.WriteString(s)
|
|
}
|
|
|
|
sql.WriteString(" FROM ")
|
|
sql.WriteString(b.FromTable)
|
|
|
|
if len(b.WhereFragments) > 0 {
|
|
sql.WriteString(" WHERE ")
|
|
writeWhereFragmentsToSql(b.WhereFragments, &sql, &args)
|
|
}
|
|
|
|
if len(b.GroupBys) > 0 {
|
|
sql.WriteString(" GROUP BY ")
|
|
for i, s := range b.GroupBys {
|
|
if i > 0 {
|
|
sql.WriteString(", ")
|
|
}
|
|
sql.WriteString(s)
|
|
}
|
|
}
|
|
|
|
if len(b.HavingFragments) > 0 {
|
|
sql.WriteString(" HAVING ")
|
|
writeWhereFragmentsToSql(b.HavingFragments, &sql, &args)
|
|
}
|
|
|
|
if len(b.OrderBys) > 0 {
|
|
sql.WriteString(" ORDER BY ")
|
|
for i, s := range b.OrderBys {
|
|
if i > 0 {
|
|
sql.WriteString(", ")
|
|
}
|
|
sql.WriteString(s)
|
|
}
|
|
}
|
|
|
|
if b.LimitValid {
|
|
sql.WriteString(" LIMIT ")
|
|
fmt.Fprint(&sql, b.LimitCount)
|
|
}
|
|
|
|
if b.OffsetValid {
|
|
sql.WriteString(" OFFSET ")
|
|
fmt.Fprint(&sql, b.OffsetCount)
|
|
}
|
|
|
|
return sql.String(), args
|
|
}
|