398 lines
11 KiB
Go
398 lines
11 KiB
Go
package squirrel
|
|
|
|
import (
|
|
"bytes"
|
|
"database/sql"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/lann/builder"
|
|
)
|
|
|
|
type selectData struct {
|
|
PlaceholderFormat PlaceholderFormat
|
|
RunWith BaseRunner
|
|
Prefixes []Sqlizer
|
|
Options []string
|
|
Columns []Sqlizer
|
|
From Sqlizer
|
|
Joins []Sqlizer
|
|
WhereParts []Sqlizer
|
|
GroupBys []string
|
|
HavingParts []Sqlizer
|
|
OrderByParts []Sqlizer
|
|
Limit string
|
|
Offset string
|
|
Suffixes []Sqlizer
|
|
}
|
|
|
|
func (d *selectData) Exec() (sql.Result, error) {
|
|
if d.RunWith == nil {
|
|
return nil, RunnerNotSet
|
|
}
|
|
return ExecWith(d.RunWith, d)
|
|
}
|
|
|
|
func (d *selectData) Query() (*sql.Rows, error) {
|
|
if d.RunWith == nil {
|
|
return nil, RunnerNotSet
|
|
}
|
|
return QueryWith(d.RunWith, d)
|
|
}
|
|
|
|
func (d *selectData) QueryRow() RowScanner {
|
|
if d.RunWith == nil {
|
|
return &Row{err: RunnerNotSet}
|
|
}
|
|
queryRower, ok := d.RunWith.(QueryRower)
|
|
if !ok {
|
|
return &Row{err: RunnerNotQueryRunner}
|
|
}
|
|
return QueryRowWith(queryRower, d)
|
|
}
|
|
|
|
func (d *selectData) ToSql() (sqlStr string, args []interface{}, err error) {
|
|
sqlStr, args, err = d.toSql()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
sqlStr, err = d.PlaceholderFormat.ReplacePlaceholders(sqlStr)
|
|
return
|
|
}
|
|
|
|
func (d *selectData) toSqlRaw() (sqlStr string, args []interface{}, err error) {
|
|
return d.toSql()
|
|
}
|
|
|
|
func (d *selectData) toSql() (sqlStr string, args []interface{}, err error) {
|
|
if len(d.Columns) == 0 {
|
|
err = fmt.Errorf("select statements must have at least one result column")
|
|
return
|
|
}
|
|
|
|
sql := &bytes.Buffer{}
|
|
|
|
if len(d.Prefixes) > 0 {
|
|
args, err = appendToSql(d.Prefixes, sql, " ", args)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
sql.WriteString(" ")
|
|
}
|
|
|
|
sql.WriteString("SELECT ")
|
|
|
|
if len(d.Options) > 0 {
|
|
sql.WriteString(strings.Join(d.Options, " "))
|
|
sql.WriteString(" ")
|
|
}
|
|
|
|
if len(d.Columns) > 0 {
|
|
args, err = appendToSql(d.Columns, sql, ", ", args)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
if d.From != nil {
|
|
sql.WriteString(" FROM ")
|
|
args, err = appendToSql([]Sqlizer{d.From}, sql, "", args)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
if len(d.Joins) > 0 {
|
|
sql.WriteString(" ")
|
|
args, err = appendToSql(d.Joins, sql, " ", args)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
if len(d.WhereParts) > 0 {
|
|
sql.WriteString(" WHERE ")
|
|
args, err = appendToSql(d.WhereParts, sql, " AND ", args)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
if len(d.GroupBys) > 0 {
|
|
sql.WriteString(" GROUP BY ")
|
|
sql.WriteString(strings.Join(d.GroupBys, ", "))
|
|
}
|
|
|
|
if len(d.HavingParts) > 0 {
|
|
sql.WriteString(" HAVING ")
|
|
args, err = appendToSql(d.HavingParts, sql, " AND ", args)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
if len(d.OrderByParts) > 0 {
|
|
sql.WriteString(" ORDER BY ")
|
|
args, err = appendToSql(d.OrderByParts, sql, ", ", args)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
if len(d.Limit) > 0 {
|
|
sql.WriteString(" LIMIT ")
|
|
sql.WriteString(d.Limit)
|
|
}
|
|
|
|
if len(d.Offset) > 0 {
|
|
sql.WriteString(" OFFSET ")
|
|
sql.WriteString(d.Offset)
|
|
}
|
|
|
|
if len(d.Suffixes) > 0 {
|
|
sql.WriteString(" ")
|
|
|
|
args, err = appendToSql(d.Suffixes, sql, " ", args)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
sqlStr = sql.String()
|
|
return
|
|
}
|
|
|
|
// Builder
|
|
|
|
// SelectBuilder builds SQL SELECT statements.
|
|
type SelectBuilder builder.Builder
|
|
|
|
func init() {
|
|
builder.Register(SelectBuilder{}, selectData{})
|
|
}
|
|
|
|
// Format methods
|
|
|
|
// PlaceholderFormat sets PlaceholderFormat (e.g. Question or Dollar) for the
|
|
// query.
|
|
func (b SelectBuilder) PlaceholderFormat(f PlaceholderFormat) SelectBuilder {
|
|
return builder.Set(b, "PlaceholderFormat", f).(SelectBuilder)
|
|
}
|
|
|
|
// Runner methods
|
|
|
|
// RunWith sets a Runner (like database/sql.DB) to be used with e.g. Exec.
|
|
// For most cases runner will be a database connection.
|
|
//
|
|
// Internally we use this to mock out the database connection for testing.
|
|
func (b SelectBuilder) RunWith(runner BaseRunner) SelectBuilder {
|
|
return setRunWith(b, runner).(SelectBuilder)
|
|
}
|
|
|
|
// Exec builds and Execs the query with the Runner set by RunWith.
|
|
func (b SelectBuilder) Exec() (sql.Result, error) {
|
|
data := builder.GetStruct(b).(selectData)
|
|
return data.Exec()
|
|
}
|
|
|
|
// Query builds and Querys the query with the Runner set by RunWith.
|
|
func (b SelectBuilder) Query() (*sql.Rows, error) {
|
|
data := builder.GetStruct(b).(selectData)
|
|
return data.Query()
|
|
}
|
|
|
|
// QueryRow builds and QueryRows the query with the Runner set by RunWith.
|
|
func (b SelectBuilder) QueryRow() RowScanner {
|
|
data := builder.GetStruct(b).(selectData)
|
|
return data.QueryRow()
|
|
}
|
|
|
|
// Scan is a shortcut for QueryRow().Scan.
|
|
func (b SelectBuilder) Scan(dest ...interface{}) error {
|
|
return b.QueryRow().Scan(dest...)
|
|
}
|
|
|
|
// SQL methods
|
|
|
|
// ToSql builds the query into a SQL string and bound args.
|
|
func (b SelectBuilder) ToSql() (string, []interface{}, error) {
|
|
data := builder.GetStruct(b).(selectData)
|
|
return data.ToSql()
|
|
}
|
|
|
|
func (b SelectBuilder) MustSql() (string, []interface{}) {
|
|
sql, args, err := b.ToSql()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return sql, args
|
|
}
|
|
|
|
func (b SelectBuilder) toSqlRaw() (string, []interface{}, error) {
|
|
data := builder.GetStruct(b).(selectData)
|
|
return data.toSqlRaw()
|
|
}
|
|
|
|
// Prefix adds an expression to the beginning of the query
|
|
func (b SelectBuilder) Prefix(sql string, args ...interface{}) SelectBuilder {
|
|
return b.PrefixExpr(Expr(sql, args...))
|
|
}
|
|
|
|
// PrefixExpr adds an expression to the very beginning of the query
|
|
func (b SelectBuilder) PrefixExpr(expr Sqlizer) SelectBuilder {
|
|
return builder.Append(b, "Prefixes", expr).(SelectBuilder)
|
|
}
|
|
|
|
// Distinct adds a DISTINCT clause to the query.
|
|
func (b SelectBuilder) Distinct() SelectBuilder {
|
|
return b.Options("DISTINCT")
|
|
}
|
|
|
|
// Options adds select option to the query
|
|
func (b SelectBuilder) Options(options ...string) SelectBuilder {
|
|
return builder.Extend(b, "Options", options).(SelectBuilder)
|
|
}
|
|
|
|
// Columns adds result columns to the query.
|
|
func (b SelectBuilder) Columns(columns ...string) SelectBuilder {
|
|
parts := make([]interface{}, 0, len(columns))
|
|
for _, str := range columns {
|
|
parts = append(parts, newPart(str))
|
|
}
|
|
return builder.Extend(b, "Columns", parts).(SelectBuilder)
|
|
}
|
|
|
|
// Column adds a result column to the query.
|
|
// Unlike Columns, Column accepts args which will be bound to placeholders in
|
|
// the columns string, for example:
|
|
// Column("IF(col IN ("+squirrel.Placeholders(3)+"), 1, 0) as col", 1, 2, 3)
|
|
func (b SelectBuilder) Column(column interface{}, args ...interface{}) SelectBuilder {
|
|
return builder.Append(b, "Columns", newPart(column, args...)).(SelectBuilder)
|
|
}
|
|
|
|
// From sets the FROM clause of the query.
|
|
func (b SelectBuilder) From(from string) SelectBuilder {
|
|
return builder.Set(b, "From", newPart(from)).(SelectBuilder)
|
|
}
|
|
|
|
// FromSelect sets a subquery into the FROM clause of the query.
|
|
func (b SelectBuilder) FromSelect(from SelectBuilder, alias string) SelectBuilder {
|
|
// Prevent misnumbered parameters in nested selects (#183).
|
|
from = from.PlaceholderFormat(Question)
|
|
return builder.Set(b, "From", Alias(from, alias)).(SelectBuilder)
|
|
}
|
|
|
|
// JoinClause adds a join clause to the query.
|
|
func (b SelectBuilder) JoinClause(pred interface{}, args ...interface{}) SelectBuilder {
|
|
return builder.Append(b, "Joins", newPart(pred, args...)).(SelectBuilder)
|
|
}
|
|
|
|
// Join adds a JOIN clause to the query.
|
|
func (b SelectBuilder) Join(join string, rest ...interface{}) SelectBuilder {
|
|
return b.JoinClause("JOIN "+join, rest...)
|
|
}
|
|
|
|
// LeftJoin adds a LEFT JOIN clause to the query.
|
|
func (b SelectBuilder) LeftJoin(join string, rest ...interface{}) SelectBuilder {
|
|
return b.JoinClause("LEFT JOIN "+join, rest...)
|
|
}
|
|
|
|
// RightJoin adds a RIGHT JOIN clause to the query.
|
|
func (b SelectBuilder) RightJoin(join string, rest ...interface{}) SelectBuilder {
|
|
return b.JoinClause("RIGHT JOIN "+join, rest...)
|
|
}
|
|
|
|
// InnerJoin adds a INNER JOIN clause to the query.
|
|
func (b SelectBuilder) InnerJoin(join string, rest ...interface{}) SelectBuilder {
|
|
return b.JoinClause("INNER JOIN "+join, rest...)
|
|
}
|
|
|
|
// CrossJoin adds a CROSS JOIN clause to the query.
|
|
func (b SelectBuilder) CrossJoin(join string, rest ...interface{}) SelectBuilder {
|
|
return b.JoinClause("CROSS JOIN "+join, rest...)
|
|
}
|
|
|
|
// Where adds an expression to the WHERE clause of the query.
|
|
//
|
|
// Expressions are ANDed together in the generated SQL.
|
|
//
|
|
// Where accepts several types for its pred argument:
|
|
//
|
|
// nil OR "" - ignored.
|
|
//
|
|
// string - SQL expression.
|
|
// If the expression has SQL placeholders then a set of arguments must be passed
|
|
// as well, one for each placeholder.
|
|
//
|
|
// map[string]interface{} OR Eq - map of SQL expressions to values. Each key is
|
|
// transformed into an expression like "<key> = ?", with the corresponding value
|
|
// bound to the placeholder. If the value is nil, the expression will be "<key>
|
|
// IS NULL". If the value is an array or slice, the expression will be "<key> IN
|
|
// (?,?,...)", with one placeholder for each item in the value. These expressions
|
|
// are ANDed together.
|
|
//
|
|
// Where will panic if pred isn't any of the above types.
|
|
func (b SelectBuilder) Where(pred interface{}, args ...interface{}) SelectBuilder {
|
|
if pred == nil || pred == "" {
|
|
return b
|
|
}
|
|
return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(SelectBuilder)
|
|
}
|
|
|
|
// GroupBy adds GROUP BY expressions to the query.
|
|
func (b SelectBuilder) GroupBy(groupBys ...string) SelectBuilder {
|
|
return builder.Extend(b, "GroupBys", groupBys).(SelectBuilder)
|
|
}
|
|
|
|
// Having adds an expression to the HAVING clause of the query.
|
|
//
|
|
// See Where.
|
|
func (b SelectBuilder) Having(pred interface{}, rest ...interface{}) SelectBuilder {
|
|
return builder.Append(b, "HavingParts", newWherePart(pred, rest...)).(SelectBuilder)
|
|
}
|
|
|
|
// OrderByClause adds ORDER BY clause to the query.
|
|
func (b SelectBuilder) OrderByClause(pred interface{}, args ...interface{}) SelectBuilder {
|
|
return builder.Append(b, "OrderByParts", newPart(pred, args...)).(SelectBuilder)
|
|
}
|
|
|
|
// OrderBy adds ORDER BY expressions to the query.
|
|
func (b SelectBuilder) OrderBy(orderBys ...string) SelectBuilder {
|
|
for _, orderBy := range orderBys {
|
|
b = b.OrderByClause(orderBy)
|
|
}
|
|
|
|
return b
|
|
}
|
|
|
|
// Limit sets a LIMIT clause on the query.
|
|
func (b SelectBuilder) Limit(limit uint64) SelectBuilder {
|
|
return builder.Set(b, "Limit", fmt.Sprintf("%d", limit)).(SelectBuilder)
|
|
}
|
|
|
|
// Limit ALL allows to access all records with limit
|
|
func (b SelectBuilder) RemoveLimit() SelectBuilder {
|
|
return builder.Delete(b, "Limit").(SelectBuilder)
|
|
}
|
|
|
|
// Offset sets a OFFSET clause on the query.
|
|
func (b SelectBuilder) Offset(offset uint64) SelectBuilder {
|
|
return builder.Set(b, "Offset", fmt.Sprintf("%d", offset)).(SelectBuilder)
|
|
}
|
|
|
|
// RemoveOffset removes OFFSET clause.
|
|
func (b SelectBuilder) RemoveOffset() SelectBuilder {
|
|
return builder.Delete(b, "Offset").(SelectBuilder)
|
|
}
|
|
|
|
// Suffix adds an expression to the end of the query
|
|
func (b SelectBuilder) Suffix(sql string, args ...interface{}) SelectBuilder {
|
|
return b.SuffixExpr(Expr(sql, args...))
|
|
}
|
|
|
|
// SuffixExpr adds an expression to the end of the query
|
|
func (b SelectBuilder) SuffixExpr(expr Sqlizer) SelectBuilder {
|
|
return builder.Append(b, "Suffixes", expr).(SelectBuilder)
|
|
}
|